* - maybe add timeout to commands ?
* - blocking version of time functions
* - polling version of i2c commands (including timer that works with
- * interrutps off)
+ * interrupts off)
* - maybe avoid some data copies with i2c by directly using the smu cmd
* buffer and a lower level internal interface
* - understand SMU -> CPU events and implement reception of them via
* the userland interface
*/
+#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
u32 cmd_buf_abs; /* command buffer absolute */
struct list_head cmd_list;
struct smu_cmd *cmd_cur; /* pending command */
+ int broken_nap;
struct list_head cmd_i2c_list;
struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */
struct timer_list i2c_timer;
fend = faddr + smu->cmd_buf->length + 2;
flush_inval_dcache_range(faddr, fend);
+
+ /* We also disable NAP mode for the duration of the command
+ * on U3 based machines.
+ * This is slightly racy as it can be written back to 1 by a sysctl
+ * but that never happens in practice. There seem to be an issue with
+ * U3 based machines such as the iMac G5 where napping for the
+ * whole duration of the command prevents the SMU from fetching it
+ * from memory. This might be related to the strange i2c based
+ * mechanism the SMU uses to access memory.
+ */
+ if (smu->broken_nap)
+ powersave_nap = 0;
+
/* This isn't exactly a DMA mapping here, I suspect
* the SMU is actually communicating with us via i2c to the
* northbridge or the CPU to access RAM.
/* CPU might have brought back the cache line, so we need
* to flush again before peeking at the SMU response. We
* flush the entire buffer for now as we haven't read the
- * reply lenght (it's only 2 cache lines anyway)
+ * reply length (it's only 2 cache lines anyway)
*/
faddr = (unsigned long)smu->cmd_buf;
flush_inval_dcache_range(faddr, faddr + 256);
misc = cmd->misc;
mb();
cmd->status = rc;
+
+ /* Re-enable NAP mode */
+ if (smu->broken_nap)
+ powersave_nap = 1;
bail:
/* Start next command if any */
smu_start_cmd();
if (np == NULL)
return -ENODEV;
- printk(KERN_INFO "SMU driver %s %s\n", VERSION, AUTHOR);
+ printk(KERN_INFO "SMU: Driver %s %s\n", VERSION, AUTHOR);
if (smu_cmdbuf_abs == 0) {
printk(KERN_ERR "SMU: Command buffer not allocated !\n");
+ of_node_put(np);
return -EINVAL;
}
smu = alloc_bootmem(sizeof(struct smu_device));
- if (smu == NULL)
+ if (smu == NULL) {
+ of_node_put(np);
return -ENOMEM;
+ }
memset(smu, 0, sizeof(*smu));
spin_lock_init(&smu->lock);
printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
goto fail;
}
- data = get_property(smu->db_node, "reg", NULL);
+ data = of_get_property(smu->db_node, "reg", NULL);
if (data == NULL) {
of_node_put(smu->db_node);
smu->db_node = NULL;
smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt");
if (smu->msg_node == NULL)
break;
- data = get_property(smu->msg_node, "reg", NULL);
+ data = of_get_property(smu->msg_node, "reg", NULL);
if (data == NULL) {
of_node_put(smu->msg_node);
smu->msg_node = NULL;
goto fail;
}
+ /* U3 has an issue with NAP mode when issuing SMU commands */
+ smu->broken_nap = pmac_get_uninorth_variant() < 4;
+ if (smu->broken_nap)
+ printk(KERN_INFO "SMU: using NAP mode workaround\n");
+
sys_ctrler = SYS_CTRLER_SMU;
return 0;
struct device_node *np;
for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;)
- if (device_is_compatible(np, "smu-sensors"))
+ if (of_device_is_compatible(np, "smu-sensors"))
of_platform_device_create(np, "smu-sensors",
&smu->of_dev->dev);
}
prop->name = ((char *)prop) + tlen - 18;
sprintf(prop->name, "sdb-partition-%02x", id);
prop->length = len;
- prop->value = (unsigned char *)hdr;
+ prop->value = hdr;
prop->next = NULL;
/* Read the datablock */
} else
mutex_lock(&smu_part_access);
- part = get_property(smu->of_node, pname, size);
+ part = of_get_property(smu->of_node, pname, size);
if (part == NULL) {
DPRINTK("trying to extract from SMU ...\n");
part = smu_create_sdb_partition(id);
struct smu_private *pp;
unsigned long flags;
- pp = kmalloc(sizeof(struct smu_private), GFP_KERNEL);
+ pp = kzalloc(sizeof(struct smu_private), GFP_KERNEL);
if (pp == 0)
return -ENOMEM;
- memset(pp, 0, sizeof(struct smu_private));
spin_lock_init(&pp->lock);
pp->mode = smu_file_commands;
init_waitqueue_head(&pp->wait);
+ lock_kernel();
spin_lock_irqsave(&smu_clist_lock, flags);
list_add(&pp->list, &smu_clist);
spin_unlock_irqrestore(&smu_clist_lock, flags);
file->private_data = pp;
+ unlock_kernel();
return 0;
}