ppc64: Support retreiving missing SMU partitions
Subsystems (2)
PATCH 1/1 ppc64: Support retreiving missing SMU partitions
The SMU chip has an EEPROM that contains various informations about the motherboard, like thermal calibration infos, etc... This EEPROM is divided in "partitions", and the firmware only extracts some of these and publish them in the device-tree. This patch adds a mecanism to retreive the missing ones which is necessary for the upcoming thermal control patch. In order to make this accessible to userland as well, the patch adds the ability to the /proc/device-tree code to get new properties added at runtime and simplify the code.
Signed-off-by Benjamin Herrenschmidt <[email protected]>
drivers/macintosh/smu.c +156 -8
@@ -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;
@@ -438,7 +440,7 @@EXPORT_SYMBOL (smu_present); -int smu_init (void ) +int __init smu_init (void ) {struct device_node *np;u32 *data;
@@ -843,16 +845,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 = ∁ + 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);
@@ -928,6 +1068,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)
include/asm-ppc64/smu.h +47 -4
96 lines not on this page · raw message
arch/ppc64/kernel/prom.c +20 -4
@@ -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>
@@ -1893,17 +1894,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
fs/proc/proc_devtree.c +35 -22
93 lines not on this page · raw message
include/asm-ppc/prom.h +1 -1
@@ -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);
include/asm-ppc64/prom.h +1 -1
@@ -201,6 +201,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 */
include/linux/proc_fs.h +3 -6
@@ -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 *,
arch/ppc/syslib/prom.c +3 -1
@@ -1165,7 +1165,7 @@/* * Add a property to a node */ -void __openfirmware +int __openfirmwareprom_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 ! */