Skip to content

Commit

Permalink
[PATCH] ppc64: SMU partition recovery
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Benjamin Herrenschmidt authored and Paul Mackerras committed Nov 8, 2005
1 parent 4350147 commit 183d020
Show file tree
Hide file tree
Showing 10 changed files with 381 additions and 57 deletions.
21 changes: 18 additions & 3 deletions arch/powerpc/kernel/prom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1974,14 +1974,29 @@ EXPORT_SYMBOL(get_property);
/*
* Add a property to a node
*/
void prom_add_property(struct device_node* np, struct property* prop)
int prom_add_property(struct device_node* np, struct property* prop)
{
struct property **next = &np->properties;
struct property **next;

prop->next = NULL;
while (*next)
write_lock(&devtree_lock);
next = &np->properties;
while (*next) {
if (strcmp(prop->name, (*next)->name) == 0) {
/* duplicate ! don't insert it */
write_unlock(&devtree_lock);
return -1;
}
next = &(*next)->next;
}
*next = prop;
write_unlock(&devtree_lock);

/* try to add to proc as well if it was initialized */
if (np->pde)
proc_device_tree_add_prop(np->pde, prop);

return 0;
}

/* I quickly hacked that one, check against spec ! */
Expand Down
4 changes: 3 additions & 1 deletion arch/ppc/syslib/prom.c
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,7 @@ get_property(struct device_node *np, const char *name, int *lenp)
/*
* Add a property to a node
*/
void
int
prom_add_property(struct device_node* np, struct property* prop)
{
struct property **next = &np->properties;
Expand All @@ -1174,6 +1174,8 @@ prom_add_property(struct device_node* np, struct property* prop)
while (*next)
next = &(*next)->next;
*next = prop;

return 0;
}

/* I quickly hacked that one, check against spec ! */
Expand Down
24 changes: 20 additions & 4 deletions arch/ppc64/kernel/prom.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <linux/initrd.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/module.h>

#include <asm/prom.h>
#include <asm/rtas.h>
Expand Down Expand Up @@ -1865,17 +1866,32 @@ get_property(struct device_node *np, const char *name, int *lenp)
EXPORT_SYMBOL(get_property);

/*
* Add a property to a node
* Add a property to a node.
*/
void
int
prom_add_property(struct device_node* np, struct property* prop)
{
struct property **next = &np->properties;
struct property **next;

prop->next = NULL;
while (*next)
write_lock(&devtree_lock);
next = &np->properties;
while (*next) {
if (strcmp(prop->name, (*next)->name) == 0) {
/* duplicate ! don't insert it */
write_unlock(&devtree_lock);
return -1;
}
next = &(*next)->next;
}
*next = prop;
write_unlock(&devtree_lock);

/* try to add to proc as well if it was initialized */
if (np->pde)
proc_device_tree_add_prop(np->pde, prop);

return 0;
}

#if 0
Expand Down
164 changes: 156 additions & 8 deletions drivers/macintosh/smu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 = &comp;
params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC;
params[1] = 0x4;
*((u32 *)&params[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);

Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 183d020

Please sign in to comment.