sky2: backout NAPI reschedule
[powerpc.git] / drivers / macintosh / smu.c
index 34f3c7e..f4516ca 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/delay.h>
 #include <linux/sysdev.h>
 #include <linux/poll.h>
+#include <linux/mutex.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -47,7 +48,7 @@
 #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
@@ -92,7 +93,9 @@ struct smu_device {
  * for now, just hard code that
  */
 static struct smu_device       *smu;
+static DEFINE_MUTEX(smu_part_access);
 
+static void smu_i2c_retry(unsigned long data);
 
 /*
  * SMU driver low level stuff
@@ -113,9 +116,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 +445,7 @@ int smu_present(void)
 EXPORT_SYMBOL(smu_present);
 
 
-int smu_init (void)
+int __init smu_init (void)
 {
        struct device_node *np;
        u32 *data;
@@ -467,7 +472,6 @@ int smu_init (void)
        smu->of_node = np;
        smu->db_irq = NO_IRQ;
        smu->msg_irq = NO_IRQ;
-       init_timer(&smu->i2c_timer);
 
        /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a
         * 32 bits value safely
@@ -542,6 +546,10 @@ static int smu_late_init(void)
        if (!smu)
                return 0;
 
+       init_timer(&smu->i2c_timer);
+       smu->i2c_timer.function = smu_i2c_retry;
+       smu->i2c_timer.data = (unsigned long)smu;
+
        /*
         * Try to request the interrupts
         */
@@ -568,7 +576,10 @@ static int smu_late_init(void)
 
        return 0;
 }
-arch_initcall(smu_late_init);
+/* This has to be before arch_initcall as the low i2c stuff relies on the
+ * above having been done before we reach arch_initcalls
+ */
+core_initcall(smu_late_init);
 
 /*
  * sysfs visibility
@@ -578,18 +589,10 @@ static void smu_expose_childs(void *unused)
 {
        struct device_node *np;
 
-       for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) {
-               if (device_is_compatible(np, "smu-i2c")) {
-                       char name[32];
-                       u32 *reg = (u32 *)get_property(np, "reg", NULL);
-
-                       if (reg == NULL)
-                               continue;
-                       sprintf(name, "smu-i2c-%02x", *reg);
-                       of_platform_device_create(np, name, &smu->of_dev->dev);
-               }
-       }
-
+       for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;)
+               if (device_is_compatible(np, "smu-sensors"))
+                       of_platform_device_create(np, "smu-sensors",
+                                                 &smu->of_dev->dev);
 }
 
 static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs, NULL);
@@ -627,8 +630,6 @@ static struct of_platform_driver smu_of_platform_driver =
 
 static int __init smu_init_sysfs(void)
 {
-       int rc;
-
        /*
         * Due to sysfs bogosity, a sysdev is not a real device, so
         * we should in fact create both if we want sysdev semantics
@@ -637,7 +638,7 @@ static int __init smu_init_sysfs(void)
         * I'm a bit too far from figuring out how that works with those
         * new chipsets, but that will come back and bite us
         */
-       rc = of_register_driver(&smu_of_platform_driver);
+       of_register_driver(&smu_of_platform_driver);
        return 0;
 }
 
@@ -708,13 +709,13 @@ static void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail)
 
 static void smu_i2c_retry(unsigned long data)
 {
-       struct smu_i2c_cmd      *cmd = (struct smu_i2c_cmd *)data;
+       struct smu_i2c_cmd      *cmd = smu->cmd_i2c_cur;
 
        DPRINTK("SMU: i2c failure, requeuing...\n");
 
        /* requeue command simply by resetting reply_len */
        cmd->pdata[0] = 0xff;
-       cmd->scmd.reply_len = 0x10;
+       cmd->scmd.reply_len = sizeof(cmd->pdata);
        smu_queue_cmd(&cmd->scmd);
 }
 
@@ -743,10 +744,8 @@ static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc)
         */
        if (fail && --cmd->retries > 0) {
                DPRINTK("SMU: i2c failure, starting timer...\n");
-               smu->i2c_timer.function = smu_i2c_retry;
-               smu->i2c_timer.data = (unsigned long)cmd;
-               smu->i2c_timer.expires = jiffies + msecs_to_jiffies(5);
-               add_timer(&smu->i2c_timer);
+               BUG_ON(cmd != smu->cmd_i2c_cur);
+               mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5));
                return;
        }
 
@@ -760,7 +759,7 @@ static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc)
 
        /* Ok, initial command complete, now poll status */
        scmd->reply_buf = cmd->pdata;
-       scmd->reply_len = 0x10;
+       scmd->reply_len = sizeof(cmd->pdata);
        scmd->data_buf = cmd->pdata;
        scmd->data_len = 1;
        cmd->pdata[0] = 0;
@@ -782,7 +781,7 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
        cmd->scmd.done = smu_i2c_low_completion;
        cmd->scmd.misc = cmd;
        cmd->scmd.reply_buf = cmd->pdata;
-       cmd->scmd.reply_len = 0x10;
+       cmd->scmd.reply_len = sizeof(cmd->pdata);
        cmd->scmd.data_buf = (u8 *)(char *)&cmd->info;
        cmd->scmd.status = 1;
        cmd->stage = 0;
@@ -845,6 +844,162 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
        return 0;
 }
 
+/*
+ * 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 */
+       DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq);
+       smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2,
+                        smu_done_complete, &comp,
+                        SMU_CMD_PARTITION_LATEST, id);
+       wait_for_completion(&comp);
+       DPRINTK("SMU: done, status: %d, reply_len: %d\n",
+               cmd.cmd.status, cmd.cmd.reply_len);
+
+       /* 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);
+
+       DPRINTK("smu_get_sdb_partition(%02x)\n", id);
+
+       if (interruptible) {
+               int rc;
+               rc = mutex_lock_interruptible(&smu_part_access);
+               if (rc)
+                       return ERR_PTR(rc);
+       } else
+               mutex_lock(&smu_part_access);
+
+       part = (struct smu_sdbp_header *)get_property(smu->of_node,
+                                                     pname, size);
+       if (part == NULL) {
+               DPRINTK("trying to extract from SMU ...\n");
+               part = smu_create_sdb_partition(id);
+               if (part != NULL && size)
+                       *size = part->len << 2;
+       }
+       mutex_unlock(&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);
 
 
 /*
@@ -918,6 +1073,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)