[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:
parent
4350147a81
commit
183d020258
|
@ -1974,14 +1974,29 @@ EXPORT_SYMBOL(get_property);
|
||||||
/*
|
/*
|
||||||
* Add a property to a node
|
* 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;
|
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 = &(*next)->next;
|
||||||
|
}
|
||||||
*next = prop;
|
*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 ! */
|
/* I quickly hacked that one, check against spec ! */
|
||||||
|
|
|
@ -1165,7 +1165,7 @@ get_property(struct device_node *np, const char *name, int *lenp)
|
||||||
/*
|
/*
|
||||||
* Add a property to a node
|
* Add a property to a node
|
||||||
*/
|
*/
|
||||||
void
|
int
|
||||||
prom_add_property(struct device_node* np, struct property* prop)
|
prom_add_property(struct device_node* np, struct property* prop)
|
||||||
{
|
{
|
||||||
struct property **next = &np->properties;
|
struct property **next = &np->properties;
|
||||||
|
@ -1174,6 +1174,8 @@ prom_add_property(struct device_node* np, struct property* prop)
|
||||||
while (*next)
|
while (*next)
|
||||||
next = &(*next)->next;
|
next = &(*next)->next;
|
||||||
*next = prop;
|
*next = prop;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I quickly hacked that one, check against spec ! */
|
/* I quickly hacked that one, check against spec ! */
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <linux/initrd.h>
|
#include <linux/initrd.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
#include <asm/rtas.h>
|
#include <asm/rtas.h>
|
||||||
|
@ -1865,17 +1866,32 @@ get_property(struct device_node *np, const char *name, int *lenp)
|
||||||
EXPORT_SYMBOL(get_property);
|
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)
|
prom_add_property(struct device_node* np, struct property* prop)
|
||||||
{
|
{
|
||||||
struct property **next = &np->properties;
|
struct property **next;
|
||||||
|
|
||||||
prop->next = NULL;
|
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 = &(*next)->next;
|
||||||
|
}
|
||||||
*next = prop;
|
*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
|
#if 0
|
||||||
|
|
|
@ -47,13 +47,13 @@
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/of_device.h>
|
#include <asm/of_device.h>
|
||||||
|
|
||||||
#define VERSION "0.6"
|
#define VERSION "0.7"
|
||||||
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
|
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
|
||||||
|
|
||||||
#undef DEBUG_SMU
|
#undef DEBUG_SMU
|
||||||
|
|
||||||
#ifdef 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
|
#else
|
||||||
#define DPRINTK(fmt, args...) do { } while (0)
|
#define DPRINTK(fmt, args...) do { } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -92,7 +92,7 @@ struct smu_device {
|
||||||
* for now, just hard code that
|
* for now, just hard code that
|
||||||
*/
|
*/
|
||||||
static struct smu_device *smu;
|
static struct smu_device *smu;
|
||||||
|
static DECLARE_MUTEX(smu_part_access);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SMU driver low level stuff
|
* 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,
|
DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
|
||||||
cmd->data_len);
|
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)[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 */
|
/* Fill the SMU command buffer */
|
||||||
smu->cmd_buf->cmd = cmd->cmd;
|
smu->cmd_buf->cmd = cmd->cmd;
|
||||||
|
@ -440,7 +442,7 @@ int smu_present(void)
|
||||||
EXPORT_SYMBOL(smu_present);
|
EXPORT_SYMBOL(smu_present);
|
||||||
|
|
||||||
|
|
||||||
int smu_init (void)
|
int __init smu_init (void)
|
||||||
{
|
{
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
u32 *data;
|
u32 *data;
|
||||||
|
@ -845,16 +847,154 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
|
||||||
return 0;
|
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];
|
char pname[32];
|
||||||
|
struct smu_sdbp_header *part;
|
||||||
|
|
||||||
if (!smu)
|
if (!smu)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sprintf(pname, "sdb-partition-%02x", id);
|
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);
|
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);
|
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) {
|
else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
|
||||||
pp->mode = smu_file_events;
|
pp->mode = smu_file_events;
|
||||||
return 0;
|
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)
|
} else if (hdr.cmdtype != SMU_CMDTYPE_SMU)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
else if (pp->mode != smu_file_commands)
|
else if (pp->mode != smu_file_commands)
|
||||||
|
|
|
@ -48,6 +48,39 @@ static int property_read_proc(char *page, char **start, off_t off,
|
||||||
* and "@10" to it.
|
* 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.
|
* 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 property *pp;
|
||||||
struct proc_dir_entry *ent;
|
struct proc_dir_entry *ent;
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
struct proc_dir_entry *list = NULL, **lastp;
|
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
set_node_proc_entry(np, de);
|
set_node_proc_entry(np, de);
|
||||||
lastp = &list;
|
|
||||||
for (child = NULL; (child = of_get_next_child(np, child));) {
|
for (child = NULL; (child = of_get_next_child(np, child));) {
|
||||||
p = strrchr(child->full_name, '/');
|
p = strrchr(child->full_name, '/');
|
||||||
if (!p)
|
if (!p)
|
||||||
|
@ -71,9 +102,6 @@ void proc_device_tree_add_node(struct device_node *np,
|
||||||
ent = proc_mkdir(p, de);
|
ent = proc_mkdir(p, de);
|
||||||
if (ent == 0)
|
if (ent == 0)
|
||||||
break;
|
break;
|
||||||
*lastp = ent;
|
|
||||||
ent->next = NULL;
|
|
||||||
lastp = &ent->next;
|
|
||||||
proc_device_tree_add_node(child, ent);
|
proc_device_tree_add_node(child, ent);
|
||||||
}
|
}
|
||||||
of_node_put(child);
|
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
|
* properties are quite unimportant for us though, thus we
|
||||||
* simply "skip" them here, but we do have to check.
|
* 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))
|
if (!strcmp(ent->name, pp->name))
|
||||||
break;
|
break;
|
||||||
if (ent != NULL) {
|
if (ent != NULL) {
|
||||||
|
@ -94,25 +122,10 @@ void proc_device_tree_add_node(struct device_node *np,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
ent = __proc_device_tree_add_prop(de, pp);
|
||||||
* 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 == 0)
|
if (ent == 0)
|
||||||
break;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -203,7 +203,7 @@ extern int prom_n_addr_cells(struct device_node* np);
|
||||||
extern int prom_n_size_cells(struct device_node* np);
|
extern int prom_n_size_cells(struct device_node* np);
|
||||||
extern int prom_n_intr_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_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
|
#ifdef CONFIG_PPC32
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -20,16 +20,52 @@
|
||||||
/*
|
/*
|
||||||
* Partition info commands
|
* 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_COMMAND 0x3e
|
||||||
|
#define SMU_CMD_PARTITION_LATEST 0x01
|
||||||
|
#define SMU_CMD_PARTITION_BASE 0x02
|
||||||
|
#define SMU_CMD_PARTITION_UPDATE 0x03
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fan control
|
* Fan control
|
||||||
*
|
*
|
||||||
* This is a "mux" for fan control commands, first byte is the
|
* This is a "mux" for fan control commands. The command seem to
|
||||||
* "sub" command.
|
* 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
|
#define SMU_CMD_FAN_COMMAND 0x4a
|
||||||
|
|
||||||
|
@ -156,6 +192,14 @@
|
||||||
#define SMU_CMD_POWER_SHUTDOWN "SHUTDOWN"
|
#define SMU_CMD_POWER_SHUTDOWN "SHUTDOWN"
|
||||||
#define SMU_CMD_POWER_VOLTAGE_SLEW "VSLEW"
|
#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
|
/* Misc commands
|
||||||
*
|
*
|
||||||
* This command seem to be a grab bag of various things
|
* This command seem to be a grab bag of various things
|
||||||
|
@ -176,6 +220,25 @@
|
||||||
* Misc commands
|
* Misc commands
|
||||||
*
|
*
|
||||||
* This command seem to be a grab bag of various things
|
* 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_COMMAND 0xee
|
||||||
#define SMU_CMD_MISC_ee_GET_DATABLOCK_REC 0x02
|
#define SMU_CMD_MISC_ee_GET_DATABLOCK_REC 0x02
|
||||||
|
@ -353,11 +416,15 @@ struct smu_sdbp_header {
|
||||||
__u8 flags;
|
__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
|
/* 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
|
* CPU F/V/T operating points in Darwin). The definition for all those
|
||||||
|
@ -367,7 +434,8 @@ struct smu_sdbp_header {
|
||||||
|
|
||||||
struct smu_sdbp_fvt {
|
struct smu_sdbp_fvt {
|
||||||
__u32 sysclk; /* Base SysClk frequency in Hz for
|
__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 pad;
|
||||||
__u8 maxtemp; /* Max temp. supported by this
|
__u8 maxtemp; /* Max temp. supported by this
|
||||||
|
@ -376,10 +444,73 @@ struct smu_sdbp_fvt {
|
||||||
|
|
||||||
__u16 volts[3]; /* CPU core voltage for the 3
|
__u16 volts[3]; /* CPU core voltage for the 3
|
||||||
* PowerTune modes, a mode with
|
* 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__
|
#ifdef __KERNEL__
|
||||||
/*
|
/*
|
||||||
* This returns the pointer to an SMU "sdb" partition data or NULL
|
* This returns the pointer to an SMU "sdb" partition data or NULL
|
||||||
|
@ -423,8 +554,10 @@ struct smu_user_cmd_hdr
|
||||||
__u32 cmdtype;
|
__u32 cmdtype;
|
||||||
#define SMU_CMDTYPE_SMU 0 /* SMU command */
|
#define SMU_CMDTYPE_SMU 0 /* SMU command */
|
||||||
#define SMU_CMDTYPE_WANTS_EVENTS 1 /* switch fd to events mode */
|
#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 cmd; /* SMU command byte */
|
||||||
|
__u8 pad[3]; /* padding */
|
||||||
__u32 data_len; /* Lenght of data following */
|
__u32 data_len; /* Lenght of data following */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ extern int device_is_compatible(struct device_node *device, const char *);
|
||||||
extern int machine_is_compatible(const char *compat);
|
extern int machine_is_compatible(const char *compat);
|
||||||
extern unsigned char *get_property(struct device_node *node, const char *name,
|
extern unsigned char *get_property(struct device_node *node, const char *name,
|
||||||
int *lenp);
|
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 void prom_get_irq_senses(unsigned char *, int, int);
|
||||||
extern int prom_n_addr_cells(struct device_node* np);
|
extern int prom_n_addr_cells(struct device_node* np);
|
||||||
extern int prom_n_size_cells(struct device_node* np);
|
extern int prom_n_size_cells(struct device_node* np);
|
||||||
|
|
|
@ -213,6 +213,6 @@ extern int prom_n_addr_cells(struct device_node* np);
|
||||||
extern int prom_n_size_cells(struct device_node* np);
|
extern int prom_n_size_cells(struct device_node* np);
|
||||||
extern int prom_n_intr_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_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 */
|
#endif /* _PPC64_PROM_H */
|
||||||
|
|
|
@ -139,15 +139,12 @@ extern void proc_tty_unregister_driver(struct tty_driver *driver);
|
||||||
/*
|
/*
|
||||||
* proc_devtree.c
|
* proc_devtree.c
|
||||||
*/
|
*/
|
||||||
struct device_node;
|
|
||||||
extern void proc_device_tree_init(void);
|
|
||||||
#ifdef CONFIG_PROC_DEVICETREE
|
#ifdef CONFIG_PROC_DEVICETREE
|
||||||
|
struct device_node;
|
||||||
|
struct property;
|
||||||
|
extern void proc_device_tree_init(void);
|
||||||
extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
|
extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *);
|
||||||
#else /* !CONFIG_PROC_DEVICETREE */
|
extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop);
|
||||||
static inline void proc_device_tree_add_node(struct device_node *np, struct proc_dir_entry *pde)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_PROC_DEVICETREE */
|
#endif /* CONFIG_PROC_DEVICETREE */
|
||||||
|
|
||||||
extern struct proc_dir_entry *proc_symlink(const char *,
|
extern struct proc_dir_entry *proc_symlink(const char *,
|
||||||
|
|
Loading…
Reference in New Issue