[PATCH] ppc64: SMU partition recovery
This patch adds the ability to the SMU driver to recover missing calibration partitions from the SMU chip itself. It also adds some dynamic mecanism to /proc/device-tree so that new properties are visible to userland. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:

committed by
Paul Mackerras

parent
4350147a81
commit
183d020258
@@ -47,13 +47,13 @@
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/of_device.h>
|
||||
|
||||
#define VERSION "0.6"
|
||||
#define VERSION "0.7"
|
||||
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
|
||||
|
||||
#undef DEBUG_SMU
|
||||
|
||||
#ifdef DEBUG_SMU
|
||||
#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
|
||||
#define DPRINTK(fmt, args...) do { udbg_printf(KERN_DEBUG fmt , ##args); } while (0)
|
||||
#else
|
||||
#define DPRINTK(fmt, args...) do { } while (0)
|
||||
#endif
|
||||
@@ -92,7 +92,7 @@ struct smu_device {
|
||||
* for now, just hard code that
|
||||
*/
|
||||
static struct smu_device *smu;
|
||||
|
||||
static DECLARE_MUTEX(smu_part_access);
|
||||
|
||||
/*
|
||||
* SMU driver low level stuff
|
||||
@@ -113,9 +113,11 @@ static void smu_start_cmd(void)
|
||||
|
||||
DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
|
||||
cmd->data_len);
|
||||
DPRINTK("SMU: data buffer: %02x %02x %02x %02x ...\n",
|
||||
DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1],
|
||||
((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3]);
|
||||
((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3],
|
||||
((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5],
|
||||
((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]);
|
||||
|
||||
/* Fill the SMU command buffer */
|
||||
smu->cmd_buf->cmd = cmd->cmd;
|
||||
@@ -440,7 +442,7 @@ int smu_present(void)
|
||||
EXPORT_SYMBOL(smu_present);
|
||||
|
||||
|
||||
int smu_init (void)
|
||||
int __init smu_init (void)
|
||||
{
|
||||
struct device_node *np;
|
||||
u32 *data;
|
||||
@@ -845,16 +847,154 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size)
|
||||
/*
|
||||
* Handling of "partitions"
|
||||
*/
|
||||
|
||||
static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
|
||||
{
|
||||
DECLARE_COMPLETION(comp);
|
||||
unsigned int chunk;
|
||||
struct smu_cmd cmd;
|
||||
int rc;
|
||||
u8 params[8];
|
||||
|
||||
/* We currently use a chunk size of 0xe. We could check the
|
||||
* SMU firmware version and use bigger sizes though
|
||||
*/
|
||||
chunk = 0xe;
|
||||
|
||||
while (len) {
|
||||
unsigned int clen = min(len, chunk);
|
||||
|
||||
cmd.cmd = SMU_CMD_MISC_ee_COMMAND;
|
||||
cmd.data_len = 7;
|
||||
cmd.data_buf = params;
|
||||
cmd.reply_len = chunk;
|
||||
cmd.reply_buf = dest;
|
||||
cmd.done = smu_done_complete;
|
||||
cmd.misc = ∁
|
||||
params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC;
|
||||
params[1] = 0x4;
|
||||
*((u32 *)¶ms[2]) = addr;
|
||||
params[6] = clen;
|
||||
|
||||
rc = smu_queue_cmd(&cmd);
|
||||
if (rc)
|
||||
return rc;
|
||||
wait_for_completion(&comp);
|
||||
if (cmd.status != 0)
|
||||
return rc;
|
||||
if (cmd.reply_len != clen) {
|
||||
printk(KERN_DEBUG "SMU: short read in "
|
||||
"smu_read_datablock, got: %d, want: %d\n",
|
||||
cmd.reply_len, clen);
|
||||
return -EIO;
|
||||
}
|
||||
len -= clen;
|
||||
addr += clen;
|
||||
dest += clen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct smu_sdbp_header *smu_create_sdb_partition(int id)
|
||||
{
|
||||
DECLARE_COMPLETION(comp);
|
||||
struct smu_simple_cmd cmd;
|
||||
unsigned int addr, len, tlen;
|
||||
struct smu_sdbp_header *hdr;
|
||||
struct property *prop;
|
||||
|
||||
/* First query the partition info */
|
||||
smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2,
|
||||
smu_done_complete, &comp,
|
||||
SMU_CMD_PARTITION_LATEST, id);
|
||||
wait_for_completion(&comp);
|
||||
|
||||
/* Partition doesn't exist (or other error) */
|
||||
if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6)
|
||||
return NULL;
|
||||
|
||||
/* Fetch address and length from reply */
|
||||
addr = *((u16 *)cmd.buffer);
|
||||
len = cmd.buffer[3] << 2;
|
||||
/* Calucluate total length to allocate, including the 17 bytes
|
||||
* for "sdb-partition-XX" that we append at the end of the buffer
|
||||
*/
|
||||
tlen = sizeof(struct property) + len + 18;
|
||||
|
||||
prop = kcalloc(tlen, 1, GFP_KERNEL);
|
||||
if (prop == NULL)
|
||||
return NULL;
|
||||
hdr = (struct smu_sdbp_header *)(prop + 1);
|
||||
prop->name = ((char *)prop) + tlen - 18;
|
||||
sprintf(prop->name, "sdb-partition-%02x", id);
|
||||
prop->length = len;
|
||||
prop->value = (unsigned char *)hdr;
|
||||
prop->next = NULL;
|
||||
|
||||
/* Read the datablock */
|
||||
if (smu_read_datablock((u8 *)hdr, addr, len)) {
|
||||
printk(KERN_DEBUG "SMU: datablock read failed while reading "
|
||||
"partition %02x !\n", id);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Got it, check a few things and create the property */
|
||||
if (hdr->id != id) {
|
||||
printk(KERN_DEBUG "SMU: Reading partition %02x and got "
|
||||
"%02x !\n", id, hdr->id);
|
||||
goto failure;
|
||||
}
|
||||
if (prom_add_property(smu->of_node, prop)) {
|
||||
printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x "
|
||||
"property !\n", id);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
return hdr;
|
||||
failure:
|
||||
kfree(prop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Note: Only allowed to return error code in pointers (using ERR_PTR)
|
||||
* when interruptible is 1
|
||||
*/
|
||||
struct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size,
|
||||
int interruptible)
|
||||
{
|
||||
char pname[32];
|
||||
struct smu_sdbp_header *part;
|
||||
|
||||
if (!smu)
|
||||
return NULL;
|
||||
|
||||
sprintf(pname, "sdb-partition-%02x", id);
|
||||
return (struct smu_sdbp_header *)get_property(smu->of_node,
|
||||
|
||||
if (interruptible) {
|
||||
int rc;
|
||||
rc = down_interruptible(&smu_part_access);
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
} else
|
||||
down(&smu_part_access);
|
||||
|
||||
part = (struct smu_sdbp_header *)get_property(smu->of_node,
|
||||
pname, size);
|
||||
if (part == NULL) {
|
||||
part = smu_create_sdb_partition(id);
|
||||
if (part != NULL && size)
|
||||
*size = part->len << 2;
|
||||
}
|
||||
up(&smu_part_access);
|
||||
return part;
|
||||
}
|
||||
|
||||
struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size)
|
||||
{
|
||||
return __smu_get_sdb_partition(id, size, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(smu_get_sdb_partition);
|
||||
|
||||
@@ -930,6 +1070,14 @@ static ssize_t smu_write(struct file *file, const char __user *buf,
|
||||
else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
|
||||
pp->mode = smu_file_events;
|
||||
return 0;
|
||||
} else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) {
|
||||
struct smu_sdbp_header *part;
|
||||
part = __smu_get_sdb_partition(hdr.cmd, NULL, 1);
|
||||
if (part == NULL)
|
||||
return -EINVAL;
|
||||
else if (IS_ERR(part))
|
||||
return PTR_ERR(part);
|
||||
return 0;
|
||||
} else if (hdr.cmdtype != SMU_CMDTYPE_SMU)
|
||||
return -EINVAL;
|
||||
else if (pp->mode != smu_file_commands)
|
||||
|
Reference in New Issue
Block a user