12c162f9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * PowerMac G5 SMU driver 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright 2004 J. Mayer <l_indien@magic.fr> 61da177e4SLinus Torvalds * Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds /* 101da177e4SLinus Torvalds * TODO: 110365ba7fSBenjamin Herrenschmidt * - maybe add timeout to commands ? 120365ba7fSBenjamin Herrenschmidt * - blocking version of time functions 130365ba7fSBenjamin Herrenschmidt * - polling version of i2c commands (including timer that works with 14f18816baSJoe Perches * interrupts off) 150365ba7fSBenjamin Herrenschmidt * - maybe avoid some data copies with i2c by directly using the smu cmd 160365ba7fSBenjamin Herrenschmidt * buffer and a lower level internal interface 170365ba7fSBenjamin Herrenschmidt * - understand SMU -> CPU events and implement reception of them via 180365ba7fSBenjamin Herrenschmidt * the userland interface 191da177e4SLinus Torvalds */ 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds #include <linux/types.h> 221da177e4SLinus Torvalds #include <linux/kernel.h> 231da177e4SLinus Torvalds #include <linux/device.h> 241da177e4SLinus Torvalds #include <linux/dmapool.h> 2557c8a661SMike Rapoport #include <linux/memblock.h> 261da177e4SLinus Torvalds #include <linux/vmalloc.h> 271da177e4SLinus Torvalds #include <linux/highmem.h> 281da177e4SLinus Torvalds #include <linux/jiffies.h> 291da177e4SLinus Torvalds #include <linux/interrupt.h> 301da177e4SLinus Torvalds #include <linux/rtc.h> 310365ba7fSBenjamin Herrenschmidt #include <linux/completion.h> 320365ba7fSBenjamin Herrenschmidt #include <linux/miscdevice.h> 330365ba7fSBenjamin Herrenschmidt #include <linux/delay.h> 340365ba7fSBenjamin Herrenschmidt #include <linux/poll.h> 3514cc3e2bSIngo Molnar #include <linux/mutex.h> 366f3bdbbeSRob Herring #include <linux/of.h> 376f3bdbbeSRob Herring #include <linux/of_address.h> 385af50730SRob Herring #include <linux/of_irq.h> 39ad9e05aeSStephen Rothwell #include <linux/of_platform.h> 40*233d687dSRob Herring #include <linux/platform_device.h> 415a0e3ad6STejun Heo #include <linux/slab.h> 42174cd4b1SIngo Molnar #include <linux/sched/signal.h> 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds #include <asm/byteorder.h> 451da177e4SLinus Torvalds #include <asm/io.h> 461da177e4SLinus Torvalds #include <asm/machdep.h> 471da177e4SLinus Torvalds #include <asm/pmac_feature.h> 481da177e4SLinus Torvalds #include <asm/smu.h> 491da177e4SLinus Torvalds #include <asm/sections.h> 507c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 511da177e4SLinus Torvalds 52183d0202SBenjamin Herrenschmidt #define VERSION "0.7" 530365ba7fSBenjamin Herrenschmidt #define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." 540365ba7fSBenjamin Herrenschmidt 550365ba7fSBenjamin Herrenschmidt #undef DEBUG_SMU 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds #ifdef DEBUG_SMU 581beb6a7dSBenjamin Herrenschmidt #define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0) 591da177e4SLinus Torvalds #else 601da177e4SLinus Torvalds #define DPRINTK(fmt, args...) do { } while (0) 611da177e4SLinus Torvalds #endif 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* 641da177e4SLinus Torvalds * This is the command buffer passed to the SMU hardware 651da177e4SLinus Torvalds */ 660365ba7fSBenjamin Herrenschmidt #define SMU_MAX_DATA 254 670365ba7fSBenjamin Herrenschmidt 681da177e4SLinus Torvalds struct smu_cmd_buf { 691da177e4SLinus Torvalds u8 cmd; 701da177e4SLinus Torvalds u8 length; 710365ba7fSBenjamin Herrenschmidt u8 data[SMU_MAX_DATA]; 721da177e4SLinus Torvalds }; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds struct smu_device { 751da177e4SLinus Torvalds spinlock_t lock; 761da177e4SLinus Torvalds struct device_node *of_node; 772dc11581SGrant Likely struct platform_device *of_dev; 780365ba7fSBenjamin Herrenschmidt int doorbell; /* doorbell gpio */ 791da177e4SLinus Torvalds u32 __iomem *db_buf; /* doorbell buffer */ 80f620753bSBenjamin Herrenschmidt struct device_node *db_node; 81f620753bSBenjamin Herrenschmidt unsigned int db_irq; 820365ba7fSBenjamin Herrenschmidt int msg; 83f620753bSBenjamin Herrenschmidt struct device_node *msg_node; 84f620753bSBenjamin Herrenschmidt unsigned int msg_irq; 851da177e4SLinus Torvalds struct smu_cmd_buf *cmd_buf; /* command buffer virtual */ 861da177e4SLinus Torvalds u32 cmd_buf_abs; /* command buffer absolute */ 870365ba7fSBenjamin Herrenschmidt struct list_head cmd_list; 880365ba7fSBenjamin Herrenschmidt struct smu_cmd *cmd_cur; /* pending command */ 89592a607bSBenjamin Herrenschmidt int broken_nap; 900365ba7fSBenjamin Herrenschmidt struct list_head cmd_i2c_list; 910365ba7fSBenjamin Herrenschmidt struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */ 920365ba7fSBenjamin Herrenschmidt struct timer_list i2c_timer; 931da177e4SLinus Torvalds }; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds /* 961da177e4SLinus Torvalds * I don't think there will ever be more than one SMU, so 971da177e4SLinus Torvalds * for now, just hard code that 981da177e4SLinus Torvalds */ 99d851b6e0SArnd Bergmann static DEFINE_MUTEX(smu_mutex); 1001da177e4SLinus Torvalds static struct smu_device *smu; 10114cc3e2bSIngo Molnar static DEFINE_MUTEX(smu_part_access); 102f620753bSBenjamin Herrenschmidt static int smu_irq_inited; 10391b6fad5SBenjamin Herrenschmidt static unsigned long smu_cmdbuf_abs; 1040365ba7fSBenjamin Herrenschmidt 1050788f285SKees Cook static void smu_i2c_retry(struct timer_list *t); 106730745a5SBenjamin Herrenschmidt 1071da177e4SLinus Torvalds /* 1080365ba7fSBenjamin Herrenschmidt * SMU driver low level stuff 1091da177e4SLinus Torvalds */ 1101da177e4SLinus Torvalds 1110365ba7fSBenjamin Herrenschmidt static void smu_start_cmd(void) 1121da177e4SLinus Torvalds { 1130365ba7fSBenjamin Herrenschmidt unsigned long faddr, fend; 1140365ba7fSBenjamin Herrenschmidt struct smu_cmd *cmd; 1151da177e4SLinus Torvalds 1160365ba7fSBenjamin Herrenschmidt if (list_empty(&smu->cmd_list)) 1170365ba7fSBenjamin Herrenschmidt return; 1180365ba7fSBenjamin Herrenschmidt 1190365ba7fSBenjamin Herrenschmidt /* Fetch first command in queue */ 1200365ba7fSBenjamin Herrenschmidt cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link); 1210365ba7fSBenjamin Herrenschmidt smu->cmd_cur = cmd; 1220365ba7fSBenjamin Herrenschmidt list_del(&cmd->link); 1230365ba7fSBenjamin Herrenschmidt 1240365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, 1250365ba7fSBenjamin Herrenschmidt cmd->data_len); 126ebd004e4SAndy Shevchenko DPRINTK("SMU: data buffer: %8ph\n", cmd->data_buf); 1270365ba7fSBenjamin Herrenschmidt 1280365ba7fSBenjamin Herrenschmidt /* Fill the SMU command buffer */ 1290365ba7fSBenjamin Herrenschmidt smu->cmd_buf->cmd = cmd->cmd; 1300365ba7fSBenjamin Herrenschmidt smu->cmd_buf->length = cmd->data_len; 1310365ba7fSBenjamin Herrenschmidt memcpy(smu->cmd_buf->data, cmd->data_buf, cmd->data_len); 1320365ba7fSBenjamin Herrenschmidt 1330365ba7fSBenjamin Herrenschmidt /* Flush command and data to RAM */ 1340365ba7fSBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1350365ba7fSBenjamin Herrenschmidt fend = faddr + smu->cmd_buf->length + 2; 1361cfb725fSChristophe Leroy flush_dcache_range(faddr, fend); 1370365ba7fSBenjamin Herrenschmidt 138592a607bSBenjamin Herrenschmidt 139592a607bSBenjamin Herrenschmidt /* We also disable NAP mode for the duration of the command 140592a607bSBenjamin Herrenschmidt * on U3 based machines. 141592a607bSBenjamin Herrenschmidt * This is slightly racy as it can be written back to 1 by a sysctl 142592a607bSBenjamin Herrenschmidt * but that never happens in practice. There seem to be an issue with 143592a607bSBenjamin Herrenschmidt * U3 based machines such as the iMac G5 where napping for the 144592a607bSBenjamin Herrenschmidt * whole duration of the command prevents the SMU from fetching it 145592a607bSBenjamin Herrenschmidt * from memory. This might be related to the strange i2c based 146592a607bSBenjamin Herrenschmidt * mechanism the SMU uses to access memory. 147592a607bSBenjamin Herrenschmidt */ 148592a607bSBenjamin Herrenschmidt if (smu->broken_nap) 149592a607bSBenjamin Herrenschmidt powersave_nap = 0; 150592a607bSBenjamin Herrenschmidt 1510365ba7fSBenjamin Herrenschmidt /* This isn't exactly a DMA mapping here, I suspect 1521da177e4SLinus Torvalds * the SMU is actually communicating with us via i2c to the 1531da177e4SLinus Torvalds * northbridge or the CPU to access RAM. 1541da177e4SLinus Torvalds */ 1550365ba7fSBenjamin Herrenschmidt writel(smu->cmd_buf_abs, smu->db_buf); 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds /* Ring the SMU doorbell */ 1580365ba7fSBenjamin Herrenschmidt pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, smu->doorbell, 4); 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds 1627d12e780SDavid Howells static irqreturn_t smu_db_intr(int irq, void *arg) 1630365ba7fSBenjamin Herrenschmidt { 1640365ba7fSBenjamin Herrenschmidt unsigned long flags; 1650365ba7fSBenjamin Herrenschmidt struct smu_cmd *cmd; 1660365ba7fSBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc) = NULL; 1670365ba7fSBenjamin Herrenschmidt void *misc = NULL; 1680365ba7fSBenjamin Herrenschmidt u8 gpio; 1690365ba7fSBenjamin Herrenschmidt int rc = 0; 1700365ba7fSBenjamin Herrenschmidt 1710365ba7fSBenjamin Herrenschmidt /* SMU completed the command, well, we hope, let's make sure 1720365ba7fSBenjamin Herrenschmidt * of it 1730365ba7fSBenjamin Herrenschmidt */ 1740365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 1750365ba7fSBenjamin Herrenschmidt 1760365ba7fSBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 177a44fe13eSBenjamin Herrenschmidt if ((gpio & 7) != 7) { 178a44fe13eSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 1790365ba7fSBenjamin Herrenschmidt return IRQ_HANDLED; 180a44fe13eSBenjamin Herrenschmidt } 1810365ba7fSBenjamin Herrenschmidt 1820365ba7fSBenjamin Herrenschmidt cmd = smu->cmd_cur; 1830365ba7fSBenjamin Herrenschmidt smu->cmd_cur = NULL; 1840365ba7fSBenjamin Herrenschmidt if (cmd == NULL) 1850365ba7fSBenjamin Herrenschmidt goto bail; 1860365ba7fSBenjamin Herrenschmidt 1870365ba7fSBenjamin Herrenschmidt if (rc == 0) { 1880365ba7fSBenjamin Herrenschmidt unsigned long faddr; 1890365ba7fSBenjamin Herrenschmidt int reply_len; 1900365ba7fSBenjamin Herrenschmidt u8 ack; 1910365ba7fSBenjamin Herrenschmidt 1920365ba7fSBenjamin Herrenschmidt /* CPU might have brought back the cache line, so we need 1930365ba7fSBenjamin Herrenschmidt * to flush again before peeking at the SMU response. We 1940365ba7fSBenjamin Herrenschmidt * flush the entire buffer for now as we haven't read the 195efad798bSPaulius Zaleckas * reply length (it's only 2 cache lines anyway) 1960365ba7fSBenjamin Herrenschmidt */ 1970365ba7fSBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1981cfb725fSChristophe Leroy flush_dcache_range(faddr, faddr + 256); 1990365ba7fSBenjamin Herrenschmidt 2000365ba7fSBenjamin Herrenschmidt /* Now check ack */ 2010365ba7fSBenjamin Herrenschmidt ack = (~cmd->cmd) & 0xff; 2020365ba7fSBenjamin Herrenschmidt if (ack != smu->cmd_buf->cmd) { 2030365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: incorrect ack, want %x got %x\n", 2040365ba7fSBenjamin Herrenschmidt ack, smu->cmd_buf->cmd); 2050365ba7fSBenjamin Herrenschmidt rc = -EIO; 2060365ba7fSBenjamin Herrenschmidt } 2070365ba7fSBenjamin Herrenschmidt reply_len = rc == 0 ? smu->cmd_buf->length : 0; 2080365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: reply len: %d\n", reply_len); 2090365ba7fSBenjamin Herrenschmidt if (reply_len > cmd->reply_len) { 2100365ba7fSBenjamin Herrenschmidt printk(KERN_WARNING "SMU: reply buffer too small," 2110365ba7fSBenjamin Herrenschmidt "got %d bytes for a %d bytes buffer\n", 2120365ba7fSBenjamin Herrenschmidt reply_len, cmd->reply_len); 2130365ba7fSBenjamin Herrenschmidt reply_len = cmd->reply_len; 2140365ba7fSBenjamin Herrenschmidt } 2150365ba7fSBenjamin Herrenschmidt cmd->reply_len = reply_len; 2160365ba7fSBenjamin Herrenschmidt if (cmd->reply_buf && reply_len) 2170365ba7fSBenjamin Herrenschmidt memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len); 2180365ba7fSBenjamin Herrenschmidt } 2190365ba7fSBenjamin Herrenschmidt 2200365ba7fSBenjamin Herrenschmidt /* Now complete the command. Write status last in order as we lost 2210365ba7fSBenjamin Herrenschmidt * ownership of the command structure as soon as it's no longer -1 2220365ba7fSBenjamin Herrenschmidt */ 2230365ba7fSBenjamin Herrenschmidt done = cmd->done; 2240365ba7fSBenjamin Herrenschmidt misc = cmd->misc; 2250365ba7fSBenjamin Herrenschmidt mb(); 2260365ba7fSBenjamin Herrenschmidt cmd->status = rc; 227592a607bSBenjamin Herrenschmidt 228592a607bSBenjamin Herrenschmidt /* Re-enable NAP mode */ 229592a607bSBenjamin Herrenschmidt if (smu->broken_nap) 230592a607bSBenjamin Herrenschmidt powersave_nap = 1; 2310365ba7fSBenjamin Herrenschmidt bail: 2320365ba7fSBenjamin Herrenschmidt /* Start next command if any */ 2330365ba7fSBenjamin Herrenschmidt smu_start_cmd(); 2340365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2350365ba7fSBenjamin Herrenschmidt 2360365ba7fSBenjamin Herrenschmidt /* Call command completion handler if any */ 2370365ba7fSBenjamin Herrenschmidt if (done) 2380365ba7fSBenjamin Herrenschmidt done(cmd, misc); 2390365ba7fSBenjamin Herrenschmidt 2400365ba7fSBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2410365ba7fSBenjamin Herrenschmidt return IRQ_HANDLED; 2420365ba7fSBenjamin Herrenschmidt } 2430365ba7fSBenjamin Herrenschmidt 2440365ba7fSBenjamin Herrenschmidt 2457d12e780SDavid Howells static irqreturn_t smu_msg_intr(int irq, void *arg) 2460365ba7fSBenjamin Herrenschmidt { 2470365ba7fSBenjamin Herrenschmidt /* I don't quite know what to do with this one, we seem to never 2480365ba7fSBenjamin Herrenschmidt * receive it, so I suspect we have to arm it someway in the SMU 2490365ba7fSBenjamin Herrenschmidt * to start getting events that way. 2500365ba7fSBenjamin Herrenschmidt */ 2510365ba7fSBenjamin Herrenschmidt 2520365ba7fSBenjamin Herrenschmidt printk(KERN_INFO "SMU: message interrupt !\n"); 2530365ba7fSBenjamin Herrenschmidt 2540365ba7fSBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2550365ba7fSBenjamin Herrenschmidt return IRQ_HANDLED; 2560365ba7fSBenjamin Herrenschmidt } 2570365ba7fSBenjamin Herrenschmidt 2580365ba7fSBenjamin Herrenschmidt 2590365ba7fSBenjamin Herrenschmidt /* 2600365ba7fSBenjamin Herrenschmidt * Queued command management. 2610365ba7fSBenjamin Herrenschmidt * 2620365ba7fSBenjamin Herrenschmidt */ 2630365ba7fSBenjamin Herrenschmidt 2640365ba7fSBenjamin Herrenschmidt int smu_queue_cmd(struct smu_cmd *cmd) 2650365ba7fSBenjamin Herrenschmidt { 2660365ba7fSBenjamin Herrenschmidt unsigned long flags; 2670365ba7fSBenjamin Herrenschmidt 2680365ba7fSBenjamin Herrenschmidt if (smu == NULL) 2690365ba7fSBenjamin Herrenschmidt return -ENODEV; 2700365ba7fSBenjamin Herrenschmidt if (cmd->data_len > SMU_MAX_DATA || 2710365ba7fSBenjamin Herrenschmidt cmd->reply_len > SMU_MAX_DATA) 2720365ba7fSBenjamin Herrenschmidt return -EINVAL; 2730365ba7fSBenjamin Herrenschmidt 2740365ba7fSBenjamin Herrenschmidt cmd->status = 1; 2750365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 2760365ba7fSBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_list); 2770365ba7fSBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 2780365ba7fSBenjamin Herrenschmidt smu_start_cmd(); 2790365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2800365ba7fSBenjamin Herrenschmidt 281f620753bSBenjamin Herrenschmidt /* Workaround for early calls when irq isn't available */ 282ef24ba70SMichael Ellerman if (!smu_irq_inited || !smu->db_irq) 283f620753bSBenjamin Herrenschmidt smu_spinwait_cmd(cmd); 284f620753bSBenjamin Herrenschmidt 2851da177e4SLinus Torvalds return 0; 2861da177e4SLinus Torvalds } 2870365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_queue_cmd); 2881da177e4SLinus Torvalds 2890365ba7fSBenjamin Herrenschmidt 2900365ba7fSBenjamin Herrenschmidt int smu_queue_simple(struct smu_simple_cmd *scmd, u8 command, 2910365ba7fSBenjamin Herrenschmidt unsigned int data_len, 2920365ba7fSBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc), 2930365ba7fSBenjamin Herrenschmidt void *misc, ...) 2941da177e4SLinus Torvalds { 2950365ba7fSBenjamin Herrenschmidt struct smu_cmd *cmd = &scmd->cmd; 2960365ba7fSBenjamin Herrenschmidt va_list list; 2970365ba7fSBenjamin Herrenschmidt int i; 2981da177e4SLinus Torvalds 2990365ba7fSBenjamin Herrenschmidt if (data_len > sizeof(scmd->buffer)) 3000365ba7fSBenjamin Herrenschmidt return -EINVAL; 3011da177e4SLinus Torvalds 3020365ba7fSBenjamin Herrenschmidt memset(scmd, 0, sizeof(*scmd)); 3030365ba7fSBenjamin Herrenschmidt cmd->cmd = command; 3040365ba7fSBenjamin Herrenschmidt cmd->data_len = data_len; 3050365ba7fSBenjamin Herrenschmidt cmd->data_buf = scmd->buffer; 3060365ba7fSBenjamin Herrenschmidt cmd->reply_len = sizeof(scmd->buffer); 3070365ba7fSBenjamin Herrenschmidt cmd->reply_buf = scmd->buffer; 3080365ba7fSBenjamin Herrenschmidt cmd->done = done; 3090365ba7fSBenjamin Herrenschmidt cmd->misc = misc; 3101da177e4SLinus Torvalds 3110365ba7fSBenjamin Herrenschmidt va_start(list, misc); 3120365ba7fSBenjamin Herrenschmidt for (i = 0; i < data_len; ++i) 3130365ba7fSBenjamin Herrenschmidt scmd->buffer[i] = (u8)va_arg(list, int); 3140365ba7fSBenjamin Herrenschmidt va_end(list); 3151da177e4SLinus Torvalds 3160365ba7fSBenjamin Herrenschmidt return smu_queue_cmd(cmd); 3171da177e4SLinus Torvalds } 3180365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_queue_simple); 3190365ba7fSBenjamin Herrenschmidt 3200365ba7fSBenjamin Herrenschmidt 3210365ba7fSBenjamin Herrenschmidt void smu_poll(void) 3220365ba7fSBenjamin Herrenschmidt { 3230365ba7fSBenjamin Herrenschmidt u8 gpio; 3240365ba7fSBenjamin Herrenschmidt 3250365ba7fSBenjamin Herrenschmidt if (smu == NULL) 3260365ba7fSBenjamin Herrenschmidt return; 3270365ba7fSBenjamin Herrenschmidt 3280365ba7fSBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 3290365ba7fSBenjamin Herrenschmidt if ((gpio & 7) == 7) 3307d12e780SDavid Howells smu_db_intr(smu->db_irq, smu); 3310365ba7fSBenjamin Herrenschmidt } 3320365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_poll); 3330365ba7fSBenjamin Herrenschmidt 3340365ba7fSBenjamin Herrenschmidt 3350365ba7fSBenjamin Herrenschmidt void smu_done_complete(struct smu_cmd *cmd, void *misc) 3360365ba7fSBenjamin Herrenschmidt { 3370365ba7fSBenjamin Herrenschmidt struct completion *comp = misc; 3380365ba7fSBenjamin Herrenschmidt 3390365ba7fSBenjamin Herrenschmidt complete(comp); 3400365ba7fSBenjamin Herrenschmidt } 3410365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_done_complete); 3420365ba7fSBenjamin Herrenschmidt 3430365ba7fSBenjamin Herrenschmidt 3440365ba7fSBenjamin Herrenschmidt void smu_spinwait_cmd(struct smu_cmd *cmd) 3450365ba7fSBenjamin Herrenschmidt { 3460365ba7fSBenjamin Herrenschmidt while(cmd->status == 1) 3470365ba7fSBenjamin Herrenschmidt smu_poll(); 3480365ba7fSBenjamin Herrenschmidt } 3490365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_spinwait_cmd); 3500365ba7fSBenjamin Herrenschmidt 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds /* RTC low level commands */ 3531da177e4SLinus Torvalds static inline int bcd2hex (int n) 3541da177e4SLinus Torvalds { 3551da177e4SLinus Torvalds return (((n & 0xf0) >> 4) * 10) + (n & 0xf); 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 3580365ba7fSBenjamin Herrenschmidt 3591da177e4SLinus Torvalds static inline int hex2bcd (int n) 3601da177e4SLinus Torvalds { 3611da177e4SLinus Torvalds return ((n / 10) << 4) + (n % 10); 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf, 3661da177e4SLinus Torvalds struct rtc_time *time) 3671da177e4SLinus Torvalds { 3681da177e4SLinus Torvalds cmd_buf->cmd = 0x8e; 3691da177e4SLinus Torvalds cmd_buf->length = 8; 3701da177e4SLinus Torvalds cmd_buf->data[0] = 0x80; 3711da177e4SLinus Torvalds cmd_buf->data[1] = hex2bcd(time->tm_sec); 3721da177e4SLinus Torvalds cmd_buf->data[2] = hex2bcd(time->tm_min); 3731da177e4SLinus Torvalds cmd_buf->data[3] = hex2bcd(time->tm_hour); 3741da177e4SLinus Torvalds cmd_buf->data[4] = time->tm_wday; 3751da177e4SLinus Torvalds cmd_buf->data[5] = hex2bcd(time->tm_mday); 3761da177e4SLinus Torvalds cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1; 3771da177e4SLinus Torvalds cmd_buf->data[7] = hex2bcd(time->tm_year - 100); 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds 3810365ba7fSBenjamin Herrenschmidt int smu_get_rtc_time(struct rtc_time *time, int spinwait) 3821da177e4SLinus Torvalds { 3830365ba7fSBenjamin Herrenschmidt struct smu_simple_cmd cmd; 3841da177e4SLinus Torvalds int rc; 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds if (smu == NULL) 3871da177e4SLinus Torvalds return -ENODEV; 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds memset(time, 0, sizeof(struct rtc_time)); 3900365ba7fSBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 1, NULL, NULL, 3910365ba7fSBenjamin Herrenschmidt SMU_CMD_RTC_GET_DATETIME); 3920365ba7fSBenjamin Herrenschmidt if (rc) 3931da177e4SLinus Torvalds return rc; 3940365ba7fSBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 3950365ba7fSBenjamin Herrenschmidt 3960365ba7fSBenjamin Herrenschmidt time->tm_sec = bcd2hex(cmd.buffer[0]); 3970365ba7fSBenjamin Herrenschmidt time->tm_min = bcd2hex(cmd.buffer[1]); 3980365ba7fSBenjamin Herrenschmidt time->tm_hour = bcd2hex(cmd.buffer[2]); 3990365ba7fSBenjamin Herrenschmidt time->tm_wday = bcd2hex(cmd.buffer[3]); 4000365ba7fSBenjamin Herrenschmidt time->tm_mday = bcd2hex(cmd.buffer[4]); 4010365ba7fSBenjamin Herrenschmidt time->tm_mon = bcd2hex(cmd.buffer[5]) - 1; 4020365ba7fSBenjamin Herrenschmidt time->tm_year = bcd2hex(cmd.buffer[6]) + 100; 4030365ba7fSBenjamin Herrenschmidt 4040365ba7fSBenjamin Herrenschmidt return 0; 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds 4070365ba7fSBenjamin Herrenschmidt 4080365ba7fSBenjamin Herrenschmidt int smu_set_rtc_time(struct rtc_time *time, int spinwait) 4091da177e4SLinus Torvalds { 4100365ba7fSBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4111da177e4SLinus Torvalds int rc; 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds if (smu == NULL) 4141da177e4SLinus Torvalds return -ENODEV; 4151da177e4SLinus Torvalds 4160365ba7fSBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 8, NULL, NULL, 4170365ba7fSBenjamin Herrenschmidt SMU_CMD_RTC_SET_DATETIME, 4180365ba7fSBenjamin Herrenschmidt hex2bcd(time->tm_sec), 4190365ba7fSBenjamin Herrenschmidt hex2bcd(time->tm_min), 4200365ba7fSBenjamin Herrenschmidt hex2bcd(time->tm_hour), 4210365ba7fSBenjamin Herrenschmidt time->tm_wday, 4220365ba7fSBenjamin Herrenschmidt hex2bcd(time->tm_mday), 4230365ba7fSBenjamin Herrenschmidt hex2bcd(time->tm_mon) + 1, 4240365ba7fSBenjamin Herrenschmidt hex2bcd(time->tm_year - 100)); 4250365ba7fSBenjamin Herrenschmidt if (rc) 4261da177e4SLinus Torvalds return rc; 4270365ba7fSBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4280365ba7fSBenjamin Herrenschmidt 4290365ba7fSBenjamin Herrenschmidt return 0; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 4320365ba7fSBenjamin Herrenschmidt 4331da177e4SLinus Torvalds void smu_shutdown(void) 4341da177e4SLinus Torvalds { 4350365ba7fSBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds if (smu == NULL) 4381da177e4SLinus Torvalds return; 4391da177e4SLinus Torvalds 4400365ba7fSBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 9, NULL, NULL, 4410365ba7fSBenjamin Herrenschmidt 'S', 'H', 'U', 'T', 'D', 'O', 'W', 'N', 0)) 4420365ba7fSBenjamin Herrenschmidt return; 4430365ba7fSBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4441da177e4SLinus Torvalds for (;;) 4451da177e4SLinus Torvalds ; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 4480365ba7fSBenjamin Herrenschmidt 4491da177e4SLinus Torvalds void smu_restart(void) 4501da177e4SLinus Torvalds { 4510365ba7fSBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds if (smu == NULL) 4541da177e4SLinus Torvalds return; 4551da177e4SLinus Torvalds 4560365ba7fSBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, NULL, NULL, 4570365ba7fSBenjamin Herrenschmidt 'R', 'E', 'S', 'T', 'A', 'R', 'T', 0)) 4580365ba7fSBenjamin Herrenschmidt return; 4590365ba7fSBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4601da177e4SLinus Torvalds for (;;) 4611da177e4SLinus Torvalds ; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4640365ba7fSBenjamin Herrenschmidt 4651da177e4SLinus Torvalds int smu_present(void) 4661da177e4SLinus Torvalds { 4671da177e4SLinus Torvalds return smu != NULL; 4681da177e4SLinus Torvalds } 4690365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_present); 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds 472183d0202SBenjamin Herrenschmidt int __init smu_init (void) 4731da177e4SLinus Torvalds { 4741da177e4SLinus Torvalds struct device_node *np; 4756f3bdbbeSRob Herring u64 data; 47673f38fe1SJulia Lawall int ret = 0; 4771da177e4SLinus Torvalds 4781da177e4SLinus Torvalds np = of_find_node_by_type(NULL, "smu"); 4791da177e4SLinus Torvalds if (np == NULL) 4801da177e4SLinus Torvalds return -ENODEV; 4811da177e4SLinus Torvalds 482592a607bSBenjamin Herrenschmidt printk(KERN_INFO "SMU: Driver %s %s\n", VERSION, AUTHOR); 4830365ba7fSBenjamin Herrenschmidt 48491b6fad5SBenjamin Herrenschmidt /* 48591b6fad5SBenjamin Herrenschmidt * SMU based G5s need some memory below 2Gb. Thankfully this is 48691b6fad5SBenjamin Herrenschmidt * called at a time where memblock is still available. 48791b6fad5SBenjamin Herrenschmidt */ 4880ba9e6edSMike Rapoport smu_cmdbuf_abs = memblock_phys_alloc_range(4096, 4096, 0, 0x80000000UL); 4891da177e4SLinus Torvalds if (smu_cmdbuf_abs == 0) { 49091b6fad5SBenjamin Herrenschmidt printk(KERN_ERR "SMU: Command buffer allocation failed !\n"); 49173f38fe1SJulia Lawall ret = -EINVAL; 49273f38fe1SJulia Lawall goto fail_np; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds 4957e1c4e27SMike Rapoport smu = memblock_alloc(sizeof(struct smu_device), SMP_CACHE_BYTES); 4968a7f97b9SMike Rapoport if (!smu) 4978a7f97b9SMike Rapoport panic("%s: Failed to allocate %zu bytes\n", __func__, 4988a7f97b9SMike Rapoport sizeof(struct smu_device)); 4991da177e4SLinus Torvalds 5001da177e4SLinus Torvalds spin_lock_init(&smu->lock); 5010365ba7fSBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_list); 5020365ba7fSBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_i2c_list); 5031da177e4SLinus Torvalds smu->of_node = np; 504ef24ba70SMichael Ellerman smu->db_irq = 0; 505ef24ba70SMichael Ellerman smu->msg_irq = 0; 5060365ba7fSBenjamin Herrenschmidt 5071da177e4SLinus Torvalds /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a 5081da177e4SLinus Torvalds * 32 bits value safely 5091da177e4SLinus Torvalds */ 5101da177e4SLinus Torvalds smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; 51148817c58SMichael Ellerman smu->cmd_buf = __va(smu_cmdbuf_abs); 5121da177e4SLinus Torvalds 513f620753bSBenjamin Herrenschmidt smu->db_node = of_find_node_by_name(NULL, "smu-doorbell"); 514f620753bSBenjamin Herrenschmidt if (smu->db_node == NULL) { 5151da177e4SLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); 51673f38fe1SJulia Lawall ret = -ENXIO; 51773f38fe1SJulia Lawall goto fail_bootmem; 5181da177e4SLinus Torvalds } 5196f3bdbbeSRob Herring if (of_property_read_reg(smu->db_node, 0, &data, NULL)) { 5201da177e4SLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n"); 52173f38fe1SJulia Lawall ret = -ENXIO; 52273f38fe1SJulia Lawall goto fail_db_node; 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds /* Current setup has one doorbell GPIO that does both doorbell 5261da177e4SLinus Torvalds * and ack. GPIOs are at 0x50, best would be to find that out 5271da177e4SLinus Torvalds * in the device-tree though. 5281da177e4SLinus Torvalds */ 5296f3bdbbeSRob Herring smu->doorbell = data; 5300365ba7fSBenjamin Herrenschmidt if (smu->doorbell < 0x50) 5310365ba7fSBenjamin Herrenschmidt smu->doorbell += 0x50; 5320365ba7fSBenjamin Herrenschmidt 5330365ba7fSBenjamin Herrenschmidt /* Now look for the smu-interrupt GPIO */ 5340365ba7fSBenjamin Herrenschmidt do { 535f620753bSBenjamin Herrenschmidt smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt"); 536f620753bSBenjamin Herrenschmidt if (smu->msg_node == NULL) 5370365ba7fSBenjamin Herrenschmidt break; 5386f3bdbbeSRob Herring if (of_property_read_reg(smu->msg_node, 0, &data, NULL)) { 539f620753bSBenjamin Herrenschmidt of_node_put(smu->msg_node); 540f620753bSBenjamin Herrenschmidt smu->msg_node = NULL; 5410365ba7fSBenjamin Herrenschmidt break; 5420365ba7fSBenjamin Herrenschmidt } 5436f3bdbbeSRob Herring smu->msg = data; 5440365ba7fSBenjamin Herrenschmidt if (smu->msg < 0x50) 5450365ba7fSBenjamin Herrenschmidt smu->msg += 0x50; 5460365ba7fSBenjamin Herrenschmidt } while(0); 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds /* Doorbell buffer is currently hard-coded, I didn't find a proper 5491da177e4SLinus Torvalds * device-tree entry giving the address. Best would probably to use 5501da177e4SLinus Torvalds * an offset for K2 base though, but let's do it that way for now. 5511da177e4SLinus Torvalds */ 5521da177e4SLinus Torvalds smu->db_buf = ioremap(0x8000860c, 0x1000); 5531da177e4SLinus Torvalds if (smu->db_buf == NULL) { 5541da177e4SLinus Torvalds printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n"); 55573f38fe1SJulia Lawall ret = -ENXIO; 55673f38fe1SJulia Lawall goto fail_msg_node; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 559592a607bSBenjamin Herrenschmidt /* U3 has an issue with NAP mode when issuing SMU commands */ 560592a607bSBenjamin Herrenschmidt smu->broken_nap = pmac_get_uninorth_variant() < 4; 561592a607bSBenjamin Herrenschmidt if (smu->broken_nap) 562592a607bSBenjamin Herrenschmidt printk(KERN_INFO "SMU: using NAP mode workaround\n"); 563592a607bSBenjamin Herrenschmidt 5641da177e4SLinus Torvalds sys_ctrler = SYS_CTRLER_SMU; 5651da177e4SLinus Torvalds return 0; 5661da177e4SLinus Torvalds 56773f38fe1SJulia Lawall fail_msg_node: 56873f38fe1SJulia Lawall of_node_put(smu->msg_node); 56973f38fe1SJulia Lawall fail_db_node: 57073f38fe1SJulia Lawall of_node_put(smu->db_node); 57173f38fe1SJulia Lawall fail_bootmem: 5724421cca0SMike Rapoport memblock_free(smu, sizeof(struct smu_device)); 5731da177e4SLinus Torvalds smu = NULL; 57473f38fe1SJulia Lawall fail_np: 57573f38fe1SJulia Lawall of_node_put(np); 57673f38fe1SJulia Lawall return ret; 5771da177e4SLinus Torvalds } 5780365ba7fSBenjamin Herrenschmidt 5790365ba7fSBenjamin Herrenschmidt 5800365ba7fSBenjamin Herrenschmidt static int smu_late_init(void) 5810365ba7fSBenjamin Herrenschmidt { 5820365ba7fSBenjamin Herrenschmidt if (!smu) 5830365ba7fSBenjamin Herrenschmidt return 0; 5840365ba7fSBenjamin Herrenschmidt 5850788f285SKees Cook timer_setup(&smu->i2c_timer, smu_i2c_retry, 0); 586730745a5SBenjamin Herrenschmidt 587f620753bSBenjamin Herrenschmidt if (smu->db_node) { 588f620753bSBenjamin Herrenschmidt smu->db_irq = irq_of_parse_and_map(smu->db_node, 0); 589ef24ba70SMichael Ellerman if (!smu->db_irq) 590b6a945aeSRob Herring printk(KERN_ERR "smu: failed to map irq for node %pOF\n", 591b6a945aeSRob Herring smu->db_node); 592f620753bSBenjamin Herrenschmidt } 593f620753bSBenjamin Herrenschmidt if (smu->msg_node) { 594f620753bSBenjamin Herrenschmidt smu->msg_irq = irq_of_parse_and_map(smu->msg_node, 0); 595ef24ba70SMichael Ellerman if (!smu->msg_irq) 596b6a945aeSRob Herring printk(KERN_ERR "smu: failed to map irq for node %pOF\n", 597b6a945aeSRob Herring smu->msg_node); 598f620753bSBenjamin Herrenschmidt } 599f620753bSBenjamin Herrenschmidt 6000365ba7fSBenjamin Herrenschmidt /* 6010365ba7fSBenjamin Herrenschmidt * Try to request the interrupts 6020365ba7fSBenjamin Herrenschmidt */ 6030365ba7fSBenjamin Herrenschmidt 604ef24ba70SMichael Ellerman if (smu->db_irq) { 6050365ba7fSBenjamin Herrenschmidt if (request_irq(smu->db_irq, smu_db_intr, 606dace1453SThomas Gleixner IRQF_SHARED, "SMU doorbell", smu) < 0) { 6070365ba7fSBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 6080365ba7fSBenjamin Herrenschmidt "request interrupt %d\n", 6090365ba7fSBenjamin Herrenschmidt smu->db_irq); 610ef24ba70SMichael Ellerman smu->db_irq = 0; 6110365ba7fSBenjamin Herrenschmidt } 6120365ba7fSBenjamin Herrenschmidt } 6130365ba7fSBenjamin Herrenschmidt 614ef24ba70SMichael Ellerman if (smu->msg_irq) { 6150365ba7fSBenjamin Herrenschmidt if (request_irq(smu->msg_irq, smu_msg_intr, 616dace1453SThomas Gleixner IRQF_SHARED, "SMU message", smu) < 0) { 6170365ba7fSBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 6180365ba7fSBenjamin Herrenschmidt "request interrupt %d\n", 6190365ba7fSBenjamin Herrenschmidt smu->msg_irq); 620ef24ba70SMichael Ellerman smu->msg_irq = 0; 6210365ba7fSBenjamin Herrenschmidt } 6220365ba7fSBenjamin Herrenschmidt } 6230365ba7fSBenjamin Herrenschmidt 624f620753bSBenjamin Herrenschmidt smu_irq_inited = 1; 6250365ba7fSBenjamin Herrenschmidt return 0; 6260365ba7fSBenjamin Herrenschmidt } 627730745a5SBenjamin Herrenschmidt /* This has to be before arch_initcall as the low i2c stuff relies on the 628730745a5SBenjamin Herrenschmidt * above having been done before we reach arch_initcalls 629730745a5SBenjamin Herrenschmidt */ 630730745a5SBenjamin Herrenschmidt core_initcall(smu_late_init); 6310365ba7fSBenjamin Herrenschmidt 6320365ba7fSBenjamin Herrenschmidt /* 6330365ba7fSBenjamin Herrenschmidt * sysfs visibility 6340365ba7fSBenjamin Herrenschmidt */ 6350365ba7fSBenjamin Herrenschmidt 636c4028958SDavid Howells static void smu_expose_childs(struct work_struct *unused) 637730745a5SBenjamin Herrenschmidt { 638a28d3af2SBenjamin Herrenschmidt struct device_node *np; 639730745a5SBenjamin Herrenschmidt 6409c826d31SQinglang Miao for_each_child_of_node(smu->of_node, np) 64155b61fecSStephen Rothwell if (of_device_is_compatible(np, "smu-sensors")) 642730745a5SBenjamin Herrenschmidt of_platform_device_create(np, "smu-sensors", 643730745a5SBenjamin Herrenschmidt &smu->of_dev->dev); 6440365ba7fSBenjamin Herrenschmidt } 6450365ba7fSBenjamin Herrenschmidt 646c4028958SDavid Howells static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs); 6470365ba7fSBenjamin Herrenschmidt 64800006124SGrant Likely static int smu_platform_probe(struct platform_device* dev) 6490365ba7fSBenjamin Herrenschmidt { 6500365ba7fSBenjamin Herrenschmidt if (!smu) 6510365ba7fSBenjamin Herrenschmidt return -ENODEV; 6520365ba7fSBenjamin Herrenschmidt smu->of_dev = dev; 6530365ba7fSBenjamin Herrenschmidt 6540365ba7fSBenjamin Herrenschmidt /* 6550365ba7fSBenjamin Herrenschmidt * Ok, we are matched, now expose all i2c busses. We have to defer 6560365ba7fSBenjamin Herrenschmidt * that unfortunately or it would deadlock inside the device model 6570365ba7fSBenjamin Herrenschmidt */ 6580365ba7fSBenjamin Herrenschmidt schedule_work(&smu_expose_childs_work); 6590365ba7fSBenjamin Herrenschmidt 6600365ba7fSBenjamin Herrenschmidt return 0; 6610365ba7fSBenjamin Herrenschmidt } 6620365ba7fSBenjamin Herrenschmidt 66346759a7cSMárton Németh static const struct of_device_id smu_platform_match[] = 6640365ba7fSBenjamin Herrenschmidt { 6650365ba7fSBenjamin Herrenschmidt { 6660365ba7fSBenjamin Herrenschmidt .type = "smu", 6670365ba7fSBenjamin Herrenschmidt }, 6680365ba7fSBenjamin Herrenschmidt {}, 6690365ba7fSBenjamin Herrenschmidt }; 6700365ba7fSBenjamin Herrenschmidt 67100006124SGrant Likely static struct platform_driver smu_of_platform_driver = 6720365ba7fSBenjamin Herrenschmidt { 6734018294bSGrant Likely .driver = { 6740365ba7fSBenjamin Herrenschmidt .name = "smu", 6754018294bSGrant Likely .of_match_table = smu_platform_match, 6764018294bSGrant Likely }, 6770365ba7fSBenjamin Herrenschmidt .probe = smu_platform_probe, 6780365ba7fSBenjamin Herrenschmidt }; 6790365ba7fSBenjamin Herrenschmidt 6800365ba7fSBenjamin Herrenschmidt static int __init smu_init_sysfs(void) 6810365ba7fSBenjamin Herrenschmidt { 6820365ba7fSBenjamin Herrenschmidt /* 6830365ba7fSBenjamin Herrenschmidt * For now, we don't power manage machines with an SMU chip, 6840365ba7fSBenjamin Herrenschmidt * I'm a bit too far from figuring out how that works with those 6850365ba7fSBenjamin Herrenschmidt * new chipsets, but that will come back and bite us 6860365ba7fSBenjamin Herrenschmidt */ 68700006124SGrant Likely platform_driver_register(&smu_of_platform_driver); 6880365ba7fSBenjamin Herrenschmidt return 0; 6890365ba7fSBenjamin Herrenschmidt } 6900365ba7fSBenjamin Herrenschmidt 6910365ba7fSBenjamin Herrenschmidt device_initcall(smu_init_sysfs); 6920365ba7fSBenjamin Herrenschmidt 6932dc11581SGrant Likely struct platform_device *smu_get_ofdev(void) 6940365ba7fSBenjamin Herrenschmidt { 6950365ba7fSBenjamin Herrenschmidt if (!smu) 6960365ba7fSBenjamin Herrenschmidt return NULL; 6970365ba7fSBenjamin Herrenschmidt return smu->of_dev; 6980365ba7fSBenjamin Herrenschmidt } 6990365ba7fSBenjamin Herrenschmidt 7000365ba7fSBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(smu_get_ofdev); 7010365ba7fSBenjamin Herrenschmidt 7020365ba7fSBenjamin Herrenschmidt /* 7030365ba7fSBenjamin Herrenschmidt * i2c interface 7040365ba7fSBenjamin Herrenschmidt */ 7050365ba7fSBenjamin Herrenschmidt 7060365ba7fSBenjamin Herrenschmidt static void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail) 7070365ba7fSBenjamin Herrenschmidt { 7080365ba7fSBenjamin Herrenschmidt void (*done)(struct smu_i2c_cmd *cmd, void *misc) = cmd->done; 7090365ba7fSBenjamin Herrenschmidt void *misc = cmd->misc; 7100365ba7fSBenjamin Herrenschmidt unsigned long flags; 7110365ba7fSBenjamin Herrenschmidt 7120365ba7fSBenjamin Herrenschmidt /* Check for read case */ 7130365ba7fSBenjamin Herrenschmidt if (!fail && cmd->read) { 7140365ba7fSBenjamin Herrenschmidt if (cmd->pdata[0] < 1) 7150365ba7fSBenjamin Herrenschmidt fail = 1; 7160365ba7fSBenjamin Herrenschmidt else 7170365ba7fSBenjamin Herrenschmidt memcpy(cmd->info.data, &cmd->pdata[1], 7180365ba7fSBenjamin Herrenschmidt cmd->info.datalen); 7190365ba7fSBenjamin Herrenschmidt } 7200365ba7fSBenjamin Herrenschmidt 7210365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: completing, success: %d\n", !fail); 7220365ba7fSBenjamin Herrenschmidt 7230365ba7fSBenjamin Herrenschmidt /* Update status and mark no pending i2c command with lock 7240365ba7fSBenjamin Herrenschmidt * held so nobody comes in while we dequeue an eventual 7250365ba7fSBenjamin Herrenschmidt * pending next i2c command 7260365ba7fSBenjamin Herrenschmidt */ 7270365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 7280365ba7fSBenjamin Herrenschmidt smu->cmd_i2c_cur = NULL; 7290365ba7fSBenjamin Herrenschmidt wmb(); 7300365ba7fSBenjamin Herrenschmidt cmd->status = fail ? -EIO : 0; 7310365ba7fSBenjamin Herrenschmidt 7320365ba7fSBenjamin Herrenschmidt /* Is there another i2c command waiting ? */ 7330365ba7fSBenjamin Herrenschmidt if (!list_empty(&smu->cmd_i2c_list)) { 7340365ba7fSBenjamin Herrenschmidt struct smu_i2c_cmd *newcmd; 7350365ba7fSBenjamin Herrenschmidt 7360365ba7fSBenjamin Herrenschmidt /* Fetch it, new current, remove from list */ 7370365ba7fSBenjamin Herrenschmidt newcmd = list_entry(smu->cmd_i2c_list.next, 7380365ba7fSBenjamin Herrenschmidt struct smu_i2c_cmd, link); 7390365ba7fSBenjamin Herrenschmidt smu->cmd_i2c_cur = newcmd; 7400365ba7fSBenjamin Herrenschmidt list_del(&cmd->link); 7410365ba7fSBenjamin Herrenschmidt 7420365ba7fSBenjamin Herrenschmidt /* Queue with low level smu */ 7430365ba7fSBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 7440365ba7fSBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 7450365ba7fSBenjamin Herrenschmidt smu_start_cmd(); 7460365ba7fSBenjamin Herrenschmidt } 7470365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 7480365ba7fSBenjamin Herrenschmidt 7490365ba7fSBenjamin Herrenschmidt /* Call command completion handler if any */ 7500365ba7fSBenjamin Herrenschmidt if (done) 7510365ba7fSBenjamin Herrenschmidt done(cmd, misc); 7520365ba7fSBenjamin Herrenschmidt 7530365ba7fSBenjamin Herrenschmidt } 7540365ba7fSBenjamin Herrenschmidt 7550365ba7fSBenjamin Herrenschmidt 7560788f285SKees Cook static void smu_i2c_retry(struct timer_list *unused) 7570365ba7fSBenjamin Herrenschmidt { 758730745a5SBenjamin Herrenschmidt struct smu_i2c_cmd *cmd = smu->cmd_i2c_cur; 7590365ba7fSBenjamin Herrenschmidt 7600365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, requeuing...\n"); 7610365ba7fSBenjamin Herrenschmidt 7620365ba7fSBenjamin Herrenschmidt /* requeue command simply by resetting reply_len */ 7630365ba7fSBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 764730745a5SBenjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 7650365ba7fSBenjamin Herrenschmidt smu_queue_cmd(&cmd->scmd); 7660365ba7fSBenjamin Herrenschmidt } 7670365ba7fSBenjamin Herrenschmidt 7680365ba7fSBenjamin Herrenschmidt 7690365ba7fSBenjamin Herrenschmidt static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) 7700365ba7fSBenjamin Herrenschmidt { 7710365ba7fSBenjamin Herrenschmidt struct smu_i2c_cmd *cmd = misc; 7720365ba7fSBenjamin Herrenschmidt int fail = 0; 7730365ba7fSBenjamin Herrenschmidt 7740365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: i2c compl. stage=%d status=%x pdata[0]=%x rlen: %x\n", 7750365ba7fSBenjamin Herrenschmidt cmd->stage, scmd->status, cmd->pdata[0], scmd->reply_len); 7760365ba7fSBenjamin Herrenschmidt 7770365ba7fSBenjamin Herrenschmidt /* Check for possible status */ 7780365ba7fSBenjamin Herrenschmidt if (scmd->status < 0) 7790365ba7fSBenjamin Herrenschmidt fail = 1; 7800365ba7fSBenjamin Herrenschmidt else if (cmd->read) { 7810365ba7fSBenjamin Herrenschmidt if (cmd->stage == 0) 7820365ba7fSBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7830365ba7fSBenjamin Herrenschmidt else 7840365ba7fSBenjamin Herrenschmidt fail = cmd->pdata[0] >= 0x80; 7850365ba7fSBenjamin Herrenschmidt } else { 7860365ba7fSBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7870365ba7fSBenjamin Herrenschmidt } 7880365ba7fSBenjamin Herrenschmidt 7890365ba7fSBenjamin Herrenschmidt /* Handle failures by requeuing command, after 5ms interval 7900365ba7fSBenjamin Herrenschmidt */ 7910365ba7fSBenjamin Herrenschmidt if (fail && --cmd->retries > 0) { 7920365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, starting timer...\n"); 793730745a5SBenjamin Herrenschmidt BUG_ON(cmd != smu->cmd_i2c_cur); 794f620753bSBenjamin Herrenschmidt if (!smu_irq_inited) { 795f620753bSBenjamin Herrenschmidt mdelay(5); 7960788f285SKees Cook smu_i2c_retry(NULL); 797f620753bSBenjamin Herrenschmidt return; 798f620753bSBenjamin Herrenschmidt } 799730745a5SBenjamin Herrenschmidt mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5)); 8000365ba7fSBenjamin Herrenschmidt return; 8010365ba7fSBenjamin Herrenschmidt } 8020365ba7fSBenjamin Herrenschmidt 8030365ba7fSBenjamin Herrenschmidt /* If failure or stage 1, command is complete */ 8040365ba7fSBenjamin Herrenschmidt if (fail || cmd->stage != 0) { 8050365ba7fSBenjamin Herrenschmidt smu_i2c_complete_command(cmd, fail); 8060365ba7fSBenjamin Herrenschmidt return; 8070365ba7fSBenjamin Herrenschmidt } 8080365ba7fSBenjamin Herrenschmidt 8090365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: going to stage 1\n"); 8100365ba7fSBenjamin Herrenschmidt 8110365ba7fSBenjamin Herrenschmidt /* Ok, initial command complete, now poll status */ 8120365ba7fSBenjamin Herrenschmidt scmd->reply_buf = cmd->pdata; 813730745a5SBenjamin Herrenschmidt scmd->reply_len = sizeof(cmd->pdata); 8140365ba7fSBenjamin Herrenschmidt scmd->data_buf = cmd->pdata; 8150365ba7fSBenjamin Herrenschmidt scmd->data_len = 1; 8160365ba7fSBenjamin Herrenschmidt cmd->pdata[0] = 0; 8170365ba7fSBenjamin Herrenschmidt cmd->stage = 1; 8180365ba7fSBenjamin Herrenschmidt cmd->retries = 20; 8190365ba7fSBenjamin Herrenschmidt smu_queue_cmd(scmd); 8200365ba7fSBenjamin Herrenschmidt } 8210365ba7fSBenjamin Herrenschmidt 8220365ba7fSBenjamin Herrenschmidt 8230365ba7fSBenjamin Herrenschmidt int smu_queue_i2c(struct smu_i2c_cmd *cmd) 8240365ba7fSBenjamin Herrenschmidt { 8250365ba7fSBenjamin Herrenschmidt unsigned long flags; 8260365ba7fSBenjamin Herrenschmidt 8270365ba7fSBenjamin Herrenschmidt if (smu == NULL) 8280365ba7fSBenjamin Herrenschmidt return -ENODEV; 8290365ba7fSBenjamin Herrenschmidt 8300365ba7fSBenjamin Herrenschmidt /* Fill most fields of scmd */ 8310365ba7fSBenjamin Herrenschmidt cmd->scmd.cmd = SMU_CMD_I2C_COMMAND; 8320365ba7fSBenjamin Herrenschmidt cmd->scmd.done = smu_i2c_low_completion; 8330365ba7fSBenjamin Herrenschmidt cmd->scmd.misc = cmd; 8340365ba7fSBenjamin Herrenschmidt cmd->scmd.reply_buf = cmd->pdata; 835730745a5SBenjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 8360365ba7fSBenjamin Herrenschmidt cmd->scmd.data_buf = (u8 *)(char *)&cmd->info; 8370365ba7fSBenjamin Herrenschmidt cmd->scmd.status = 1; 8380365ba7fSBenjamin Herrenschmidt cmd->stage = 0; 8390365ba7fSBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 8400365ba7fSBenjamin Herrenschmidt cmd->retries = 20; 8410365ba7fSBenjamin Herrenschmidt cmd->status = 1; 8420365ba7fSBenjamin Herrenschmidt 8430365ba7fSBenjamin Herrenschmidt /* Check transfer type, sanitize some "info" fields 8440365ba7fSBenjamin Herrenschmidt * based on transfer type and do more checking 8450365ba7fSBenjamin Herrenschmidt */ 8460365ba7fSBenjamin Herrenschmidt cmd->info.caddr = cmd->info.devaddr; 8470365ba7fSBenjamin Herrenschmidt cmd->read = cmd->info.devaddr & 0x01; 8480365ba7fSBenjamin Herrenschmidt switch(cmd->info.type) { 8490365ba7fSBenjamin Herrenschmidt case SMU_I2C_TRANSFER_SIMPLE: 8500e17ad87SKees Cook cmd->info.sublen = 0; 8510e17ad87SKees Cook memset(cmd->info.subaddr, 0, sizeof(cmd->info.subaddr)); 8520365ba7fSBenjamin Herrenschmidt break; 8530365ba7fSBenjamin Herrenschmidt case SMU_I2C_TRANSFER_COMBINED: 8540365ba7fSBenjamin Herrenschmidt cmd->info.devaddr &= 0xfe; 855df561f66SGustavo A. R. Silva fallthrough; 8560365ba7fSBenjamin Herrenschmidt case SMU_I2C_TRANSFER_STDSUB: 8570365ba7fSBenjamin Herrenschmidt if (cmd->info.sublen > 3) 8580365ba7fSBenjamin Herrenschmidt return -EINVAL; 8590365ba7fSBenjamin Herrenschmidt break; 8600365ba7fSBenjamin Herrenschmidt default: 8610365ba7fSBenjamin Herrenschmidt return -EINVAL; 8620365ba7fSBenjamin Herrenschmidt } 8630365ba7fSBenjamin Herrenschmidt 8640365ba7fSBenjamin Herrenschmidt /* Finish setting up command based on transfer direction 8650365ba7fSBenjamin Herrenschmidt */ 8660365ba7fSBenjamin Herrenschmidt if (cmd->read) { 8670365ba7fSBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_READ_MAX) 8680365ba7fSBenjamin Herrenschmidt return -EINVAL; 8690365ba7fSBenjamin Herrenschmidt memset(cmd->info.data, 0xff, cmd->info.datalen); 8700365ba7fSBenjamin Herrenschmidt cmd->scmd.data_len = 9; 8710365ba7fSBenjamin Herrenschmidt } else { 8720365ba7fSBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_WRITE_MAX) 8730365ba7fSBenjamin Herrenschmidt return -EINVAL; 8740365ba7fSBenjamin Herrenschmidt cmd->scmd.data_len = 9 + cmd->info.datalen; 8750365ba7fSBenjamin Herrenschmidt } 8760365ba7fSBenjamin Herrenschmidt 8770365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: i2c enqueuing command\n"); 8780365ba7fSBenjamin Herrenschmidt DPRINTK("SMU: %s, len=%d bus=%x addr=%x sub0=%x type=%x\n", 8790365ba7fSBenjamin Herrenschmidt cmd->read ? "read" : "write", cmd->info.datalen, 8800365ba7fSBenjamin Herrenschmidt cmd->info.bus, cmd->info.caddr, 8810365ba7fSBenjamin Herrenschmidt cmd->info.subaddr[0], cmd->info.type); 8820365ba7fSBenjamin Herrenschmidt 8830365ba7fSBenjamin Herrenschmidt 8840365ba7fSBenjamin Herrenschmidt /* Enqueue command in i2c list, and if empty, enqueue also in 8850365ba7fSBenjamin Herrenschmidt * main command list 8860365ba7fSBenjamin Herrenschmidt */ 8870365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 8880365ba7fSBenjamin Herrenschmidt if (smu->cmd_i2c_cur == NULL) { 8890365ba7fSBenjamin Herrenschmidt smu->cmd_i2c_cur = cmd; 8900365ba7fSBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 8910365ba7fSBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 8920365ba7fSBenjamin Herrenschmidt smu_start_cmd(); 8930365ba7fSBenjamin Herrenschmidt } else 8940365ba7fSBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_i2c_list); 8950365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 8960365ba7fSBenjamin Herrenschmidt 8970365ba7fSBenjamin Herrenschmidt return 0; 8980365ba7fSBenjamin Herrenschmidt } 8990365ba7fSBenjamin Herrenschmidt 900183d0202SBenjamin Herrenschmidt /* 901183d0202SBenjamin Herrenschmidt * Handling of "partitions" 902183d0202SBenjamin Herrenschmidt */ 903183d0202SBenjamin Herrenschmidt 904183d0202SBenjamin Herrenschmidt static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len) 905183d0202SBenjamin Herrenschmidt { 9066e9a4738SPeter Zijlstra DECLARE_COMPLETION_ONSTACK(comp); 907183d0202SBenjamin Herrenschmidt unsigned int chunk; 908183d0202SBenjamin Herrenschmidt struct smu_cmd cmd; 909183d0202SBenjamin Herrenschmidt int rc; 910183d0202SBenjamin Herrenschmidt u8 params[8]; 911183d0202SBenjamin Herrenschmidt 912183d0202SBenjamin Herrenschmidt /* We currently use a chunk size of 0xe. We could check the 913183d0202SBenjamin Herrenschmidt * SMU firmware version and use bigger sizes though 914183d0202SBenjamin Herrenschmidt */ 915183d0202SBenjamin Herrenschmidt chunk = 0xe; 916183d0202SBenjamin Herrenschmidt 917183d0202SBenjamin Herrenschmidt while (len) { 918183d0202SBenjamin Herrenschmidt unsigned int clen = min(len, chunk); 919183d0202SBenjamin Herrenschmidt 920183d0202SBenjamin Herrenschmidt cmd.cmd = SMU_CMD_MISC_ee_COMMAND; 921183d0202SBenjamin Herrenschmidt cmd.data_len = 7; 922183d0202SBenjamin Herrenschmidt cmd.data_buf = params; 923183d0202SBenjamin Herrenschmidt cmd.reply_len = chunk; 924183d0202SBenjamin Herrenschmidt cmd.reply_buf = dest; 925183d0202SBenjamin Herrenschmidt cmd.done = smu_done_complete; 926183d0202SBenjamin Herrenschmidt cmd.misc = ∁ 927183d0202SBenjamin Herrenschmidt params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC; 928183d0202SBenjamin Herrenschmidt params[1] = 0x4; 929183d0202SBenjamin Herrenschmidt *((u32 *)¶ms[2]) = addr; 930183d0202SBenjamin Herrenschmidt params[6] = clen; 931183d0202SBenjamin Herrenschmidt 932183d0202SBenjamin Herrenschmidt rc = smu_queue_cmd(&cmd); 933183d0202SBenjamin Herrenschmidt if (rc) 934183d0202SBenjamin Herrenschmidt return rc; 935183d0202SBenjamin Herrenschmidt wait_for_completion(&comp); 936183d0202SBenjamin Herrenschmidt if (cmd.status != 0) 937183d0202SBenjamin Herrenschmidt return rc; 938183d0202SBenjamin Herrenschmidt if (cmd.reply_len != clen) { 939183d0202SBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: short read in " 940183d0202SBenjamin Herrenschmidt "smu_read_datablock, got: %d, want: %d\n", 941183d0202SBenjamin Herrenschmidt cmd.reply_len, clen); 942183d0202SBenjamin Herrenschmidt return -EIO; 943183d0202SBenjamin Herrenschmidt } 944183d0202SBenjamin Herrenschmidt len -= clen; 945183d0202SBenjamin Herrenschmidt addr += clen; 946183d0202SBenjamin Herrenschmidt dest += clen; 947183d0202SBenjamin Herrenschmidt } 948183d0202SBenjamin Herrenschmidt return 0; 949183d0202SBenjamin Herrenschmidt } 950183d0202SBenjamin Herrenschmidt 951183d0202SBenjamin Herrenschmidt static struct smu_sdbp_header *smu_create_sdb_partition(int id) 952183d0202SBenjamin Herrenschmidt { 9536e9a4738SPeter Zijlstra DECLARE_COMPLETION_ONSTACK(comp); 954183d0202SBenjamin Herrenschmidt struct smu_simple_cmd cmd; 955183d0202SBenjamin Herrenschmidt unsigned int addr, len, tlen; 956183d0202SBenjamin Herrenschmidt struct smu_sdbp_header *hdr; 957183d0202SBenjamin Herrenschmidt struct property *prop; 958183d0202SBenjamin Herrenschmidt 959183d0202SBenjamin Herrenschmidt /* First query the partition info */ 9601beb6a7dSBenjamin Herrenschmidt DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq); 961183d0202SBenjamin Herrenschmidt smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, 962183d0202SBenjamin Herrenschmidt smu_done_complete, &comp, 963183d0202SBenjamin Herrenschmidt SMU_CMD_PARTITION_LATEST, id); 964183d0202SBenjamin Herrenschmidt wait_for_completion(&comp); 9651beb6a7dSBenjamin Herrenschmidt DPRINTK("SMU: done, status: %d, reply_len: %d\n", 9661beb6a7dSBenjamin Herrenschmidt cmd.cmd.status, cmd.cmd.reply_len); 967183d0202SBenjamin Herrenschmidt 968183d0202SBenjamin Herrenschmidt /* Partition doesn't exist (or other error) */ 969183d0202SBenjamin Herrenschmidt if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) 970183d0202SBenjamin Herrenschmidt return NULL; 971183d0202SBenjamin Herrenschmidt 972183d0202SBenjamin Herrenschmidt /* Fetch address and length from reply */ 973183d0202SBenjamin Herrenschmidt addr = *((u16 *)cmd.buffer); 974183d0202SBenjamin Herrenschmidt len = cmd.buffer[3] << 2; 975183d0202SBenjamin Herrenschmidt /* Calucluate total length to allocate, including the 17 bytes 976183d0202SBenjamin Herrenschmidt * for "sdb-partition-XX" that we append at the end of the buffer 977183d0202SBenjamin Herrenschmidt */ 978183d0202SBenjamin Herrenschmidt tlen = sizeof(struct property) + len + 18; 979183d0202SBenjamin Herrenschmidt 980cd861280SRobert P. J. Day prop = kzalloc(tlen, GFP_KERNEL); 981183d0202SBenjamin Herrenschmidt if (prop == NULL) 982183d0202SBenjamin Herrenschmidt return NULL; 983183d0202SBenjamin Herrenschmidt hdr = (struct smu_sdbp_header *)(prop + 1); 984183d0202SBenjamin Herrenschmidt prop->name = ((char *)prop) + tlen - 18; 985183d0202SBenjamin Herrenschmidt sprintf(prop->name, "sdb-partition-%02x", id); 986183d0202SBenjamin Herrenschmidt prop->length = len; 9871a38147eSStephen Rothwell prop->value = hdr; 988183d0202SBenjamin Herrenschmidt prop->next = NULL; 989183d0202SBenjamin Herrenschmidt 990183d0202SBenjamin Herrenschmidt /* Read the datablock */ 991183d0202SBenjamin Herrenschmidt if (smu_read_datablock((u8 *)hdr, addr, len)) { 992183d0202SBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: datablock read failed while reading " 993183d0202SBenjamin Herrenschmidt "partition %02x !\n", id); 994183d0202SBenjamin Herrenschmidt goto failure; 995183d0202SBenjamin Herrenschmidt } 996183d0202SBenjamin Herrenschmidt 997183d0202SBenjamin Herrenschmidt /* Got it, check a few things and create the property */ 998183d0202SBenjamin Herrenschmidt if (hdr->id != id) { 999183d0202SBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Reading partition %02x and got " 1000183d0202SBenjamin Herrenschmidt "%02x !\n", id, hdr->id); 1001183d0202SBenjamin Herrenschmidt goto failure; 1002183d0202SBenjamin Herrenschmidt } 100379d1c712SNathan Fontenot if (of_add_property(smu->of_node, prop)) { 1004183d0202SBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " 1005183d0202SBenjamin Herrenschmidt "property !\n", id); 1006183d0202SBenjamin Herrenschmidt goto failure; 1007183d0202SBenjamin Herrenschmidt } 1008183d0202SBenjamin Herrenschmidt 1009183d0202SBenjamin Herrenschmidt return hdr; 1010183d0202SBenjamin Herrenschmidt failure: 1011183d0202SBenjamin Herrenschmidt kfree(prop); 1012183d0202SBenjamin Herrenschmidt return NULL; 1013183d0202SBenjamin Herrenschmidt } 1014183d0202SBenjamin Herrenschmidt 1015183d0202SBenjamin Herrenschmidt /* Note: Only allowed to return error code in pointers (using ERR_PTR) 1016183d0202SBenjamin Herrenschmidt * when interruptible is 1 1017183d0202SBenjamin Herrenschmidt */ 10183db8715eSWang Wensheng static const struct smu_sdbp_header *__smu_get_sdb_partition(int id, 1019018a3d1dSJeremy Kerr unsigned int *size, int interruptible) 10204350147aSBenjamin Herrenschmidt { 10214350147aSBenjamin Herrenschmidt char pname[32]; 1022018a3d1dSJeremy Kerr const struct smu_sdbp_header *part; 10234350147aSBenjamin Herrenschmidt 10244350147aSBenjamin Herrenschmidt if (!smu) 10254350147aSBenjamin Herrenschmidt return NULL; 10264350147aSBenjamin Herrenschmidt 10274350147aSBenjamin Herrenschmidt sprintf(pname, "sdb-partition-%02x", id); 1028183d0202SBenjamin Herrenschmidt 10291beb6a7dSBenjamin Herrenschmidt DPRINTK("smu_get_sdb_partition(%02x)\n", id); 10301beb6a7dSBenjamin Herrenschmidt 1031183d0202SBenjamin Herrenschmidt if (interruptible) { 1032183d0202SBenjamin Herrenschmidt int rc; 103314cc3e2bSIngo Molnar rc = mutex_lock_interruptible(&smu_part_access); 1034183d0202SBenjamin Herrenschmidt if (rc) 1035183d0202SBenjamin Herrenschmidt return ERR_PTR(rc); 1036183d0202SBenjamin Herrenschmidt } else 103714cc3e2bSIngo Molnar mutex_lock(&smu_part_access); 1038183d0202SBenjamin Herrenschmidt 103901b2726dSStephen Rothwell part = of_get_property(smu->of_node, pname, size); 1040183d0202SBenjamin Herrenschmidt if (part == NULL) { 10411beb6a7dSBenjamin Herrenschmidt DPRINTK("trying to extract from SMU ...\n"); 1042183d0202SBenjamin Herrenschmidt part = smu_create_sdb_partition(id); 1043183d0202SBenjamin Herrenschmidt if (part != NULL && size) 1044183d0202SBenjamin Herrenschmidt *size = part->len << 2; 1045183d0202SBenjamin Herrenschmidt } 104614cc3e2bSIngo Molnar mutex_unlock(&smu_part_access); 1047183d0202SBenjamin Herrenschmidt return part; 1048183d0202SBenjamin Herrenschmidt } 1049183d0202SBenjamin Herrenschmidt 1050018a3d1dSJeremy Kerr const struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) 1051183d0202SBenjamin Herrenschmidt { 1052183d0202SBenjamin Herrenschmidt return __smu_get_sdb_partition(id, size, 0); 10534350147aSBenjamin Herrenschmidt } 10544350147aSBenjamin Herrenschmidt EXPORT_SYMBOL(smu_get_sdb_partition); 10550365ba7fSBenjamin Herrenschmidt 10560365ba7fSBenjamin Herrenschmidt 10570365ba7fSBenjamin Herrenschmidt /* 10580365ba7fSBenjamin Herrenschmidt * Userland driver interface 10590365ba7fSBenjamin Herrenschmidt */ 10600365ba7fSBenjamin Herrenschmidt 10610365ba7fSBenjamin Herrenschmidt 10620365ba7fSBenjamin Herrenschmidt static LIST_HEAD(smu_clist); 10630365ba7fSBenjamin Herrenschmidt static DEFINE_SPINLOCK(smu_clist_lock); 10640365ba7fSBenjamin Herrenschmidt 10650365ba7fSBenjamin Herrenschmidt enum smu_file_mode { 10660365ba7fSBenjamin Herrenschmidt smu_file_commands, 10670365ba7fSBenjamin Herrenschmidt smu_file_events, 10680365ba7fSBenjamin Herrenschmidt smu_file_closing 10690365ba7fSBenjamin Herrenschmidt }; 10700365ba7fSBenjamin Herrenschmidt 10710365ba7fSBenjamin Herrenschmidt struct smu_private 10720365ba7fSBenjamin Herrenschmidt { 10730365ba7fSBenjamin Herrenschmidt struct list_head list; 10740365ba7fSBenjamin Herrenschmidt enum smu_file_mode mode; 10750365ba7fSBenjamin Herrenschmidt int busy; 10760365ba7fSBenjamin Herrenschmidt struct smu_cmd cmd; 10770365ba7fSBenjamin Herrenschmidt spinlock_t lock; 10780365ba7fSBenjamin Herrenschmidt wait_queue_head_t wait; 10790365ba7fSBenjamin Herrenschmidt u8 buffer[SMU_MAX_DATA]; 10800365ba7fSBenjamin Herrenschmidt }; 10810365ba7fSBenjamin Herrenschmidt 10820365ba7fSBenjamin Herrenschmidt 10830365ba7fSBenjamin Herrenschmidt static int smu_open(struct inode *inode, struct file *file) 10840365ba7fSBenjamin Herrenschmidt { 10850365ba7fSBenjamin Herrenschmidt struct smu_private *pp; 10860365ba7fSBenjamin Herrenschmidt unsigned long flags; 10870365ba7fSBenjamin Herrenschmidt 1088dd00cc48SYoann Padioleau pp = kzalloc(sizeof(struct smu_private), GFP_KERNEL); 10898cd1d2e9SJing Yangyang if (!pp) 10900365ba7fSBenjamin Herrenschmidt return -ENOMEM; 10910365ba7fSBenjamin Herrenschmidt spin_lock_init(&pp->lock); 10920365ba7fSBenjamin Herrenschmidt pp->mode = smu_file_commands; 10930365ba7fSBenjamin Herrenschmidt init_waitqueue_head(&pp->wait); 10940365ba7fSBenjamin Herrenschmidt 1095d851b6e0SArnd Bergmann mutex_lock(&smu_mutex); 10960365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 10970365ba7fSBenjamin Herrenschmidt list_add(&pp->list, &smu_clist); 10980365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 10990365ba7fSBenjamin Herrenschmidt file->private_data = pp; 1100d851b6e0SArnd Bergmann mutex_unlock(&smu_mutex); 11010365ba7fSBenjamin Herrenschmidt 11020365ba7fSBenjamin Herrenschmidt return 0; 11030365ba7fSBenjamin Herrenschmidt } 11040365ba7fSBenjamin Herrenschmidt 11050365ba7fSBenjamin Herrenschmidt 11060365ba7fSBenjamin Herrenschmidt static void smu_user_cmd_done(struct smu_cmd *cmd, void *misc) 11070365ba7fSBenjamin Herrenschmidt { 11080365ba7fSBenjamin Herrenschmidt struct smu_private *pp = misc; 11090365ba7fSBenjamin Herrenschmidt 11100365ba7fSBenjamin Herrenschmidt wake_up_all(&pp->wait); 11110365ba7fSBenjamin Herrenschmidt } 11120365ba7fSBenjamin Herrenschmidt 11130365ba7fSBenjamin Herrenschmidt 11140365ba7fSBenjamin Herrenschmidt static ssize_t smu_write(struct file *file, const char __user *buf, 11150365ba7fSBenjamin Herrenschmidt size_t count, loff_t *ppos) 11160365ba7fSBenjamin Herrenschmidt { 11170365ba7fSBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 11180365ba7fSBenjamin Herrenschmidt unsigned long flags; 11190365ba7fSBenjamin Herrenschmidt struct smu_user_cmd_hdr hdr; 11200365ba7fSBenjamin Herrenschmidt int rc = 0; 11210365ba7fSBenjamin Herrenschmidt 11220365ba7fSBenjamin Herrenschmidt if (pp->busy) 11230365ba7fSBenjamin Herrenschmidt return -EBUSY; 11240365ba7fSBenjamin Herrenschmidt else if (copy_from_user(&hdr, buf, sizeof(hdr))) 11250365ba7fSBenjamin Herrenschmidt return -EFAULT; 11260365ba7fSBenjamin Herrenschmidt else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { 11270365ba7fSBenjamin Herrenschmidt pp->mode = smu_file_events; 11280365ba7fSBenjamin Herrenschmidt return 0; 1129183d0202SBenjamin Herrenschmidt } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { 1130018a3d1dSJeremy Kerr const struct smu_sdbp_header *part; 1131183d0202SBenjamin Herrenschmidt part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); 1132183d0202SBenjamin Herrenschmidt if (part == NULL) 1133183d0202SBenjamin Herrenschmidt return -EINVAL; 1134183d0202SBenjamin Herrenschmidt else if (IS_ERR(part)) 1135183d0202SBenjamin Herrenschmidt return PTR_ERR(part); 1136183d0202SBenjamin Herrenschmidt return 0; 11370365ba7fSBenjamin Herrenschmidt } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) 11380365ba7fSBenjamin Herrenschmidt return -EINVAL; 11390365ba7fSBenjamin Herrenschmidt else if (pp->mode != smu_file_commands) 11400365ba7fSBenjamin Herrenschmidt return -EBADFD; 11410365ba7fSBenjamin Herrenschmidt else if (hdr.data_len > SMU_MAX_DATA) 11420365ba7fSBenjamin Herrenschmidt return -EINVAL; 11430365ba7fSBenjamin Herrenschmidt 11440365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11450365ba7fSBenjamin Herrenschmidt if (pp->busy) { 11460365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11470365ba7fSBenjamin Herrenschmidt return -EBUSY; 11480365ba7fSBenjamin Herrenschmidt } 11490365ba7fSBenjamin Herrenschmidt pp->busy = 1; 11500365ba7fSBenjamin Herrenschmidt pp->cmd.status = 1; 11510365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11520365ba7fSBenjamin Herrenschmidt 11530365ba7fSBenjamin Herrenschmidt if (copy_from_user(pp->buffer, buf + sizeof(hdr), hdr.data_len)) { 11540365ba7fSBenjamin Herrenschmidt pp->busy = 0; 11550365ba7fSBenjamin Herrenschmidt return -EFAULT; 11560365ba7fSBenjamin Herrenschmidt } 11570365ba7fSBenjamin Herrenschmidt 11580365ba7fSBenjamin Herrenschmidt pp->cmd.cmd = hdr.cmd; 11590365ba7fSBenjamin Herrenschmidt pp->cmd.data_len = hdr.data_len; 11600365ba7fSBenjamin Herrenschmidt pp->cmd.reply_len = SMU_MAX_DATA; 11610365ba7fSBenjamin Herrenschmidt pp->cmd.data_buf = pp->buffer; 11620365ba7fSBenjamin Herrenschmidt pp->cmd.reply_buf = pp->buffer; 11630365ba7fSBenjamin Herrenschmidt pp->cmd.done = smu_user_cmd_done; 11640365ba7fSBenjamin Herrenschmidt pp->cmd.misc = pp; 11650365ba7fSBenjamin Herrenschmidt rc = smu_queue_cmd(&pp->cmd); 11660365ba7fSBenjamin Herrenschmidt if (rc < 0) 11670365ba7fSBenjamin Herrenschmidt return rc; 11680365ba7fSBenjamin Herrenschmidt return count; 11690365ba7fSBenjamin Herrenschmidt } 11700365ba7fSBenjamin Herrenschmidt 11710365ba7fSBenjamin Herrenschmidt 11720365ba7fSBenjamin Herrenschmidt static ssize_t smu_read_command(struct file *file, struct smu_private *pp, 11730365ba7fSBenjamin Herrenschmidt char __user *buf, size_t count) 11740365ba7fSBenjamin Herrenschmidt { 11750365ba7fSBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 11760365ba7fSBenjamin Herrenschmidt struct smu_user_reply_hdr hdr; 11770365ba7fSBenjamin Herrenschmidt unsigned long flags; 11780365ba7fSBenjamin Herrenschmidt int size, rc = 0; 11790365ba7fSBenjamin Herrenschmidt 11800365ba7fSBenjamin Herrenschmidt if (!pp->busy) 11810365ba7fSBenjamin Herrenschmidt return 0; 11820365ba7fSBenjamin Herrenschmidt if (count < sizeof(struct smu_user_reply_hdr)) 11830365ba7fSBenjamin Herrenschmidt return -EOVERFLOW; 11840365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11850365ba7fSBenjamin Herrenschmidt if (pp->cmd.status == 1) { 118686e4754aSJulia Lawall if (file->f_flags & O_NONBLOCK) { 118786e4754aSJulia Lawall spin_unlock_irqrestore(&pp->lock, flags); 11880365ba7fSBenjamin Herrenschmidt return -EAGAIN; 118986e4754aSJulia Lawall } 11900365ba7fSBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 11910365ba7fSBenjamin Herrenschmidt for (;;) { 11920365ba7fSBenjamin Herrenschmidt set_current_state(TASK_INTERRUPTIBLE); 11930365ba7fSBenjamin Herrenschmidt rc = 0; 11940365ba7fSBenjamin Herrenschmidt if (pp->cmd.status != 1) 11950365ba7fSBenjamin Herrenschmidt break; 11960365ba7fSBenjamin Herrenschmidt rc = -ERESTARTSYS; 11970365ba7fSBenjamin Herrenschmidt if (signal_pending(current)) 11980365ba7fSBenjamin Herrenschmidt break; 11990365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12000365ba7fSBenjamin Herrenschmidt schedule(); 12010365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12020365ba7fSBenjamin Herrenschmidt } 12030365ba7fSBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 12040365ba7fSBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 12050365ba7fSBenjamin Herrenschmidt } 12060365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12070365ba7fSBenjamin Herrenschmidt if (rc) 12080365ba7fSBenjamin Herrenschmidt return rc; 12090365ba7fSBenjamin Herrenschmidt if (pp->cmd.status != 0) 12100365ba7fSBenjamin Herrenschmidt pp->cmd.reply_len = 0; 12110365ba7fSBenjamin Herrenschmidt size = sizeof(hdr) + pp->cmd.reply_len; 12120365ba7fSBenjamin Herrenschmidt if (count < size) 12130365ba7fSBenjamin Herrenschmidt size = count; 12140365ba7fSBenjamin Herrenschmidt rc = size; 12150365ba7fSBenjamin Herrenschmidt hdr.status = pp->cmd.status; 12160365ba7fSBenjamin Herrenschmidt hdr.reply_len = pp->cmd.reply_len; 12170365ba7fSBenjamin Herrenschmidt if (copy_to_user(buf, &hdr, sizeof(hdr))) 12180365ba7fSBenjamin Herrenschmidt return -EFAULT; 12190365ba7fSBenjamin Herrenschmidt size -= sizeof(hdr); 12200365ba7fSBenjamin Herrenschmidt if (size && copy_to_user(buf + sizeof(hdr), pp->buffer, size)) 12210365ba7fSBenjamin Herrenschmidt return -EFAULT; 12220365ba7fSBenjamin Herrenschmidt pp->busy = 0; 12230365ba7fSBenjamin Herrenschmidt 12240365ba7fSBenjamin Herrenschmidt return rc; 12250365ba7fSBenjamin Herrenschmidt } 12260365ba7fSBenjamin Herrenschmidt 12270365ba7fSBenjamin Herrenschmidt 12280365ba7fSBenjamin Herrenschmidt static ssize_t smu_read_events(struct file *file, struct smu_private *pp, 12290365ba7fSBenjamin Herrenschmidt char __user *buf, size_t count) 12300365ba7fSBenjamin Herrenschmidt { 12310365ba7fSBenjamin Herrenschmidt /* Not implemented */ 12320365ba7fSBenjamin Herrenschmidt msleep_interruptible(1000); 12330365ba7fSBenjamin Herrenschmidt return 0; 12340365ba7fSBenjamin Herrenschmidt } 12350365ba7fSBenjamin Herrenschmidt 12360365ba7fSBenjamin Herrenschmidt 12370365ba7fSBenjamin Herrenschmidt static ssize_t smu_read(struct file *file, char __user *buf, 12380365ba7fSBenjamin Herrenschmidt size_t count, loff_t *ppos) 12390365ba7fSBenjamin Herrenschmidt { 12400365ba7fSBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12410365ba7fSBenjamin Herrenschmidt 12420365ba7fSBenjamin Herrenschmidt if (pp->mode == smu_file_commands) 12430365ba7fSBenjamin Herrenschmidt return smu_read_command(file, pp, buf, count); 12440365ba7fSBenjamin Herrenschmidt if (pp->mode == smu_file_events) 12450365ba7fSBenjamin Herrenschmidt return smu_read_events(file, pp, buf, count); 12460365ba7fSBenjamin Herrenschmidt 12470365ba7fSBenjamin Herrenschmidt return -EBADFD; 12480365ba7fSBenjamin Herrenschmidt } 12490365ba7fSBenjamin Herrenschmidt 1250afc9a42bSAl Viro static __poll_t smu_fpoll(struct file *file, poll_table *wait) 12510365ba7fSBenjamin Herrenschmidt { 12520365ba7fSBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 1253afc9a42bSAl Viro __poll_t mask = 0; 12540365ba7fSBenjamin Herrenschmidt unsigned long flags; 12550365ba7fSBenjamin Herrenschmidt 12568cd1d2e9SJing Yangyang if (!pp) 12570365ba7fSBenjamin Herrenschmidt return 0; 12580365ba7fSBenjamin Herrenschmidt 12590365ba7fSBenjamin Herrenschmidt if (pp->mode == smu_file_commands) { 12600365ba7fSBenjamin Herrenschmidt poll_wait(file, &pp->wait, wait); 12610365ba7fSBenjamin Herrenschmidt 12620365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12630365ba7fSBenjamin Herrenschmidt if (pp->busy && pp->cmd.status != 1) 1264a9a08845SLinus Torvalds mask |= EPOLLIN; 12650365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12662055fb41SRasmus Villemoes } 12672055fb41SRasmus Villemoes if (pp->mode == smu_file_events) { 12680365ba7fSBenjamin Herrenschmidt /* Not yet implemented */ 12690365ba7fSBenjamin Herrenschmidt } 12700365ba7fSBenjamin Herrenschmidt return mask; 12710365ba7fSBenjamin Herrenschmidt } 12720365ba7fSBenjamin Herrenschmidt 12730365ba7fSBenjamin Herrenschmidt static int smu_release(struct inode *inode, struct file *file) 12740365ba7fSBenjamin Herrenschmidt { 12750365ba7fSBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12760365ba7fSBenjamin Herrenschmidt unsigned long flags; 12770365ba7fSBenjamin Herrenschmidt unsigned int busy; 12780365ba7fSBenjamin Herrenschmidt 12798cd1d2e9SJing Yangyang if (!pp) 12800365ba7fSBenjamin Herrenschmidt return 0; 12810365ba7fSBenjamin Herrenschmidt 12820365ba7fSBenjamin Herrenschmidt file->private_data = NULL; 12830365ba7fSBenjamin Herrenschmidt 12840365ba7fSBenjamin Herrenschmidt /* Mark file as closing to avoid races with new request */ 12850365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12860365ba7fSBenjamin Herrenschmidt pp->mode = smu_file_closing; 12870365ba7fSBenjamin Herrenschmidt busy = pp->busy; 12880365ba7fSBenjamin Herrenschmidt 12890365ba7fSBenjamin Herrenschmidt /* Wait for any pending request to complete */ 12900365ba7fSBenjamin Herrenschmidt if (busy && pp->cmd.status == 1) { 12910365ba7fSBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 12920365ba7fSBenjamin Herrenschmidt 12930365ba7fSBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 12940365ba7fSBenjamin Herrenschmidt for (;;) { 12950365ba7fSBenjamin Herrenschmidt set_current_state(TASK_UNINTERRUPTIBLE); 12960365ba7fSBenjamin Herrenschmidt if (pp->cmd.status != 1) 12970365ba7fSBenjamin Herrenschmidt break; 12980365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 129994256dd6SAndrew Morton schedule(); 130094256dd6SAndrew Morton spin_lock_irqsave(&pp->lock, flags); 13010365ba7fSBenjamin Herrenschmidt } 13020365ba7fSBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 13030365ba7fSBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 13040365ba7fSBenjamin Herrenschmidt } 13050365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 13060365ba7fSBenjamin Herrenschmidt 13070365ba7fSBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 13080365ba7fSBenjamin Herrenschmidt list_del(&pp->list); 13090365ba7fSBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 13100365ba7fSBenjamin Herrenschmidt kfree(pp); 13110365ba7fSBenjamin Herrenschmidt 13120365ba7fSBenjamin Herrenschmidt return 0; 13130365ba7fSBenjamin Herrenschmidt } 13140365ba7fSBenjamin Herrenschmidt 13150365ba7fSBenjamin Herrenschmidt 1316fa027c2aSArjan van de Ven static const struct file_operations smu_device_fops = { 13170365ba7fSBenjamin Herrenschmidt .read = smu_read, 13180365ba7fSBenjamin Herrenschmidt .write = smu_write, 13190365ba7fSBenjamin Herrenschmidt .poll = smu_fpoll, 13200365ba7fSBenjamin Herrenschmidt .open = smu_open, 13210365ba7fSBenjamin Herrenschmidt .release = smu_release, 13220365ba7fSBenjamin Herrenschmidt }; 13230365ba7fSBenjamin Herrenschmidt 13246b67f62cSStephen Rothwell static struct miscdevice pmu_device = { 13250365ba7fSBenjamin Herrenschmidt MISC_DYNAMIC_MINOR, "smu", &smu_device_fops 13260365ba7fSBenjamin Herrenschmidt }; 13270365ba7fSBenjamin Herrenschmidt 13280365ba7fSBenjamin Herrenschmidt static int smu_device_init(void) 13290365ba7fSBenjamin Herrenschmidt { 13300365ba7fSBenjamin Herrenschmidt if (!smu) 13310365ba7fSBenjamin Herrenschmidt return -ENODEV; 13320365ba7fSBenjamin Herrenschmidt if (misc_register(&pmu_device) < 0) 13330365ba7fSBenjamin Herrenschmidt printk(KERN_ERR "via-pmu: cannot register misc device.\n"); 13340365ba7fSBenjamin Herrenschmidt return 0; 13350365ba7fSBenjamin Herrenschmidt } 13360365ba7fSBenjamin Herrenschmidt device_initcall(smu_device_init); 1337