[PATCH] ppc64: SMU partition recovery

Benjamin Herrenschmidt benh at kernel.crashing.org
Mon Nov 7 14:29:02 EST 2005


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 at kernel.crashing.org>

Index: linux-work/drivers/macintosh/smu.c
===================================================================
--- linux-work.orig/drivers/macintosh/smu.c	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/drivers/macintosh/smu.c	2005-11-07 12:45:44.000000000 +1100
@@ -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 @@
  * 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 @@
 
 	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 @@
 EXPORT_SYMBOL(smu_present);
 
 
-int smu_init (void)
+int __init smu_init (void)
 {
 	struct device_node *np;
 	u32 *data;
@@ -845,16 +847,154 @@
 	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 @@
 	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)
Index: linux-work/arch/ppc64/kernel/prom.c
===================================================================
--- linux-work.orig/arch/ppc64/kernel/prom.c	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/arch/ppc64/kernel/prom.c	2005-11-07 12:45:44.000000000 +1100
@@ -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>
@@ -1894,17 +1895,32 @@
 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
Index: linux-work/fs/proc/proc_devtree.c
===================================================================
--- linux-work.orig/fs/proc/proc_devtree.c	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/fs/proc/proc_devtree.c	2005-11-07 12:45:44.000000000 +1100
@@ -49,6 +49,39 @@
  */
 
 /*
+ * 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.
  */
 void proc_device_tree_add_node(struct device_node *np,
@@ -57,11 +90,9 @@
 	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 @@
 		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 @@
 		 * 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 @@
 			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;
 }
 
 /*
Index: linux-work/include/asm-ppc/prom.h
===================================================================
--- linux-work.orig/include/asm-ppc/prom.h	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/include/asm-ppc/prom.h	2005-11-07 12:45:44.000000000 +1100
@@ -93,7 +93,7 @@
 extern int machine_is_compatible(const char *compat);
 extern unsigned char *get_property(struct device_node *node, const char *name,
 				   int *lenp);
-extern void prom_add_property(struct device_node* np, struct property* prop);
+extern int prom_add_property(struct device_node* np, struct property* prop);
 extern void prom_get_irq_senses(unsigned char *, int, int);
 extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
Index: linux-work/include/asm-ppc64/prom.h
===================================================================
--- linux-work.orig/include/asm-ppc64/prom.h	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/include/asm-ppc64/prom.h	2005-11-07 12:45:44.000000000 +1100
@@ -205,6 +205,6 @@
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_intr_cells(struct device_node* np);
 extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
-extern void prom_add_property(struct device_node* np, struct property* prop);
+extern int prom_add_property(struct device_node* np, struct property* prop);
 
 #endif /* _PPC64_PROM_H */
Index: linux-work/include/linux/proc_fs.h
===================================================================
--- linux-work.orig/include/linux/proc_fs.h	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/include/linux/proc_fs.h	2005-11-07 12:45:44.000000000 +1100
@@ -139,15 +139,12 @@
 /*
  * proc_devtree.c
  */
+#ifdef CONFIG_PROC_DEVICETREE
 struct device_node;
+struct property;
 extern void proc_device_tree_init(void);
-#ifdef CONFIG_PROC_DEVICETREE
 extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
-#else /* !CONFIG_PROC_DEVICETREE */
-static inline void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *pde)
-{
-	return;
-}
+extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop);
 #endif /* CONFIG_PROC_DEVICETREE */
 
 extern struct proc_dir_entry *proc_symlink(const char *,
Index: linux-work/arch/ppc/syslib/prom.c
===================================================================
--- linux-work.orig/arch/ppc/syslib/prom.c	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/arch/ppc/syslib/prom.c	2005-11-07 12:45:44.000000000 +1100
@@ -1165,7 +1165,7 @@
 /*
  * 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 @@
 	while (*next)
 		next = &(*next)->next;
 	*next = prop;
+
+	return 0;
 }
 
 /* I quickly hacked that one, check against spec ! */
Index: linux-work/include/asm-powerpc/smu.h
===================================================================
--- linux-work.orig/include/asm-powerpc/smu.h	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/include/asm-powerpc/smu.h	2005-11-07 12:45:44.000000000 +1100
@@ -20,16 +20,52 @@
 /*
  * Partition info commands
  *
- * I do not know what those are for at this point
+ * These commands are used to retreive the sdb-partition-XX datas from
+ * the SMU. The lenght is always 2. First byte is the subcommand code
+ * and second byte is the partition ID.
+ *
+ * The reply is 6 bytes:
+ *
+ *  - 0..1 : partition address
+ *  - 2    : a byte containing the partition ID
+ *  - 3    : length (maybe other bits are rest of header ?)
+ *
+ * The data must then be obtained with calls to another command:
+ * SMU_CMD_MISC_ee_GET_DATABLOCK_REC (described below).
  */
 #define SMU_CMD_PARTITION_COMMAND		0x3e
+#define   SMU_CMD_PARTITION_LATEST		0x01
+#define   SMU_CMD_PARTITION_BASE		0x02
+#define   SMU_CMD_PARTITION_UPDATE		0x03
 
 
 /*
  * Fan control
  *
- * This is a "mux" for fan control commands, first byte is the
- * "sub" command.
+ * This is a "mux" for fan control commands. The command seem to
+ * act differently based on the number of arguments. With 1 byte
+ * of argument, this seem to be queries for fans status, setpoint,
+ * etc..., while with 0xe arguments, we will set the fans speeds.
+ *
+ * Queries (1 byte arg):
+ * ---------------------
+ *
+ * arg=0x01: read RPM fans status
+ * arg=0x02: read RPM fans setpoint
+ * arg=0x11: read PWM fans status
+ * arg=0x12: read PWM fans setpoint
+ *
+ * the "status" queries return the current speed while the "setpoint" ones
+ * return the programmed/target speed. It _seems_ that the result is a bit
+ * mask in the first byte of active/available fans, followed by 6 words (16
+ * bits) containing the requested speed.
+ *
+ * Setpoint (14 bytes arg):
+ * ------------------------
+ *
+ * first arg byte is 0 for RPM fans and 0x10 for PWM. Second arg byte is the
+ * mask of fans affected by the command. Followed by 6 words containing the
+ * setpoint value for selected fans in the mask (or 0 if mask value is 0)
  */
 #define SMU_CMD_FAN_COMMAND			0x4a
 
@@ -156,6 +192,14 @@
 #define   SMU_CMD_POWER_SHUTDOWN		"SHUTDOWN"
 #define   SMU_CMD_POWER_VOLTAGE_SLEW		"VSLEW"
 
+/*
+ * Read ADC sensors
+ *
+ * This command takes one byte of parameter: the sensor ID (or "reg"
+ * value in the device-tree) and returns a 16 bits value
+ */
+#define SMU_CMD_READ_ADC			0xd8
+
 /* Misc commands
  *
  * This command seem to be a grab bag of various things
@@ -176,6 +220,25 @@
  * Misc commands
  *
  * This command seem to be a grab bag of various things
+ *
+ * SMU_CMD_MISC_ee_GET_DATABLOCK_REC is used, among others, to
+ * transfer blocks of data from the SMU. So far, I've decrypted it's
+ * usage to retreive partition data. In order to do that, you have to
+ * break your transfer in "chunks" since that command cannot transfer
+ * more than a chunk at a time. The chunk size used by OF is 0xe bytes,
+ * but it seems that the darwin driver will let you do 0x1e bytes if
+ * your "PMU" version is >= 0x30. You can get the "PMU" version apparently
+ * either in the last 16 bits of property "smu-version-pmu" or as the 16
+ * bytes at offset 1 of "smu-version-info"
+ *
+ * For each chunk, the command takes 7 bytes of arguments:
+ *  byte 0: subcommand code (0x02)
+ *  byte 1: 0x04 (always, I don't know what it means, maybe the address
+ *                space to use or some other nicety. It's hard coded in OF)
+ *  byte 2..5: SMU address of the chunk (big endian 32 bits)
+ *  byte 6: size to transfer (up to max chunk size)
+ *
+ * The data is returned directly
  */
 #define SMU_CMD_MISC_ee_COMMAND			0xee
 #define   SMU_CMD_MISC_ee_GET_DATABLOCK_REC	0x02
@@ -353,21 +416,26 @@
 	__u8	flags;
 };
 
-/*
- * 32 bits integers are usually encoded with 2x16 bits swapped,
- * this demangles them
+
+ /*
+ * demangle 16 and 32 bits integer in some SMU partitions
+ * (currently, afaik, this concerns only the FVT partition
+ * (0x12)
  */
-#define SMU_U32_MIX(x)	((((x) << 16) & 0xffff0000u) | (((x) >> 16) & 0xffffu))
+#define SMU_U16_MIX(x)	le16_to_cpu(x);
+#define SMU_U32_MIX(x)  ((((x) & 0xff00ff00u) >> 8)|(((x) & 0x00ff00ffu) << 8))
+
 
 /* This is the definition of the SMU sdb-partition-0x12 table (called
  * CPU F/V/T operating points in Darwin). The definition for all those
  * SMU tables should be moved to some separate file
  */
-#define SMU_SDB_FVT_ID		0x12
+#define SMU_SDB_FVT_ID			0x12
 
 struct smu_sdbp_fvt {
 	__u32	sysclk;			/* Base SysClk frequency in Hz for
-					 * this operating point
+					 * this operating point. Value need to
+					 * be unmixed with SMU_U32_MIX()
 					 */
 	__u8	pad;
 	__u8	maxtemp;		/* Max temp. supported by this
@@ -376,10 +444,73 @@
 
 	__u16	volts[3];		/* CPU core voltage for the 3
 					 * PowerTune modes, a mode with
-					 * 0V = not supported.
+					 * 0V = not supported. Value need
+					 * to be unmixed with SMU_U16_MIX()
 					 */
 };
 
+/* This partition contains voltage & current sensor calibration
+ * informations
+ */
+#define SMU_SDB_CPUVCP_ID		0x21
+
+struct smu_sdbp_cpuvcp {
+	__u16	volt_scale;		/* u4.12 fixed point */
+	__s16	volt_offset;		/* s4.12 fixed point */
+	__u16	curr_scale;		/* u4.12 fixed point */
+	__s16	curr_offset;		/* s4.12 fixed point */
+	__s32	power_quads[3];		/* s4.28 fixed point */
+};
+
+/* This partition contains CPU thermal diode calibration
+ */
+#define SMU_SDB_CPUDIODE_ID		0x18
+
+struct smu_sdbp_cpudiode {
+	__u16	m_value;		/* u1.15 fixed point */
+	__s16	b_value;		/* s10.6 fixed point */
+
+};
+
+/* This partition contains Slots power calibration
+ */
+#define SMU_SDB_SLOTSPOW_ID		0x78
+
+struct smu_sdbp_slotspow {
+	__u16	pow_scale;		/* u4.12 fixed point */
+	__s16	pow_offset;		/* s4.12 fixed point */
+};
+
+/* This partition contains machine specific version information about
+ * the sensor/control layout
+ */
+#define SMU_SDB_SENSORTREE_ID		0x25
+
+struct smu_sdbp_sensortree {
+	u8	model_id;
+	u8	unknown[3];
+};
+
+/* This partition contains CPU thermal control PID informations. So far
+ * only single CPU machines have been seen with an SMU, so we assume this
+ * carries only informations for those
+ */
+#define SMU_SDB_CPUPIDDATA_ID		0x17
+
+struct smu_sdbp_cpupiddata {
+	u8	unknown1;
+	u8	target_temp_delta;
+	u8	unknown2;
+	u8	history_len;
+	s16	power_adj;
+	u16	max_power;
+	s32	gp,gr,gd;
+};
+
+
+/* Other partitions without known structures */
+#define SMU_SDB_DEBUG_SWITCHES_ID	0x05
+
 #ifdef __KERNEL__
 /*
  * This returns the pointer to an SMU "sdb" partition data or NULL
@@ -423,8 +554,10 @@
 	__u32		cmdtype;
 #define SMU_CMDTYPE_SMU			0	/* SMU command */
 #define SMU_CMDTYPE_WANTS_EVENTS	1	/* switch fd to events mode */
+#define SMU_CMDTYPE_GET_PARTITION	2	/* retreive an sdb partition */
 
 	__u8		cmd;			/* SMU command byte */
+	__u8		pad[3];			/* padding */
 	__u32		data_len;		/* Lenght of data following */
 };
 
Index: linux-work/arch/powerpc/kernel/prom.c
===================================================================
--- linux-work.orig/arch/powerpc/kernel/prom.c	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/arch/powerpc/kernel/prom.c	2005-11-07 12:46:45.000000000 +1100
@@ -1986,14 +1986,29 @@
 /*
  * 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 ! */
Index: linux-work/include/asm-powerpc/prom.h
===================================================================
--- linux-work.orig/include/asm-powerpc/prom.h	2005-11-07 12:45:42.000000000 +1100
+++ linux-work/include/asm-powerpc/prom.h	2005-11-07 12:45:44.000000000 +1100
@@ -195,7 +195,7 @@
 extern int prom_n_size_cells(struct device_node* np);
 extern int prom_n_intr_cells(struct device_node* np);
 extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
-extern void prom_add_property(struct device_node* np, struct property* prop);
+extern int prom_add_property(struct device_node* np, struct property* prop);
 
 #ifdef CONFIG_PPC32
 /*





More information about the Linuxppc64-dev mailing list