Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 183d0202 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Paul Mackerras
Browse files

[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: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 4350147a
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -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 ! */
+3 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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 ! */
+20 −4
Original line number Diff line number Diff line
@@ -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>
@@ -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
+156 −8
Original line number Diff line number Diff line
@@ -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 = &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);

@@ -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)
+35 −22
Original line number Diff line number Diff line
@@ -48,6 +48,39 @@ static int property_read_proc(char *page, char **start, off_t off,
 * and "@10" to it.
 */

/*
 * Add a property to a node
 */
static struct proc_dir_entry *
__proc_device_tree_add_prop(struct proc_dir_entry *de, struct property *pp)
{
	struct proc_dir_entry *ent;

	/*
	 * Unfortunately proc_register puts each new entry
	 * at the beginning of the list.  So we rearrange them.
	 */
	ent = create_proc_read_entry(pp->name,
				     strncmp(pp->name, "security-", 9)
				     ? S_IRUGO : S_IRUSR, de,
				     property_read_proc, pp);
	if (ent == NULL)
		return NULL;

	if (!strncmp(pp->name, "security-", 9))
		ent->size = 0; /* don't leak number of password chars */
	else
		ent->size = pp->length;

	return ent;
}


void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop)
{
	__proc_device_tree_add_prop(pde, prop);
}

/*
 * Process a node, adding entries for its children and its properties.
 */
@@ -57,11 +90,9 @@ void proc_device_tree_add_node(struct device_node *np,
	struct property *pp;
	struct proc_dir_entry *ent;
	struct device_node *child;
	struct proc_dir_entry *list = NULL, **lastp;
	const char *p;

	set_node_proc_entry(np, de);
	lastp = &list;
	for (child = NULL; (child = of_get_next_child(np, child));) {
		p = strrchr(child->full_name, '/');
		if (!p)
@@ -71,9 +102,6 @@ void proc_device_tree_add_node(struct device_node *np,
		ent = proc_mkdir(p, de);
		if (ent == 0)
			break;
		*lastp = ent;
		ent->next = NULL;
		lastp = &ent->next;
		proc_device_tree_add_node(child, ent);
	}
	of_node_put(child);
@@ -84,7 +112,7 @@ void proc_device_tree_add_node(struct device_node *np,
		 * properties are quite unimportant for us though, thus we
		 * simply "skip" them here, but we do have to check.
		 */
		for (ent = list; ent != NULL; ent = ent->next)
		for (ent = de->subdir; ent != NULL; ent = ent->next)
			if (!strcmp(ent->name, pp->name))
				break;
		if (ent != NULL) {
@@ -94,25 +122,10 @@ void proc_device_tree_add_node(struct device_node *np,
			continue;
		}

		/*
		 * Unfortunately proc_register puts each new entry
		 * at the beginning of the list.  So we rearrange them.
		 */
		ent = create_proc_read_entry(pp->name,
					     strncmp(pp->name, "security-", 9)
					     ? S_IRUGO : S_IRUSR, de,
					     property_read_proc, pp);
		ent = __proc_device_tree_add_prop(de, pp);
		if (ent == 0)
			break;
		if (!strncmp(pp->name, "security-", 9))
		     ent->size = 0; /* don't leak number of password chars */
		else
		     ent->size = pp->length;
		ent->next = NULL;
		*lastp = ent;
		lastp = &ent->next;
	}
	de->subdir = list;
}

/*
Loading