11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PowerMac G5 SMU driver 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright 2004 J. Mayer <l_indien@magic.fr> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Released under the term of the GNU GPL v2. 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TODO: 120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * - maybe add timeout to commands ? 130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * - blocking version of time functions 140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * - polling version of i2c commands (including timer that works with 15f18816ba20655deb3227da0600d5c34201467993Joe Perches * interrupts off) 160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * - maybe avoid some data copies with i2c by directly using the smu cmd 170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * buffer and a lower level internal interface 180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * - understand SMU -> CPU events and implement reception of them via 190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * the userland interface 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/dmapool.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bootmem.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h> 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/highmem.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/jiffies.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/rtc.h> 320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/completion.h> 330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/miscdevice.h> 340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/delay.h> 350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/poll.h> 3614cc3e2b633bb64063698980974df4535368e98fIngo Molnar#include <linux/mutex.h> 37ad9e05aef7c861280b404d38fb50a8ff11d01e11Stephen Rothwell#include <linux/of_device.h> 385af5073004071cedd0343eee51d77955037ec6f3Rob Herring#include <linux/of_irq.h> 39ad9e05aef7c861280b404d38fb50a8ff11d01e11Stephen Rothwell#include <linux/of_platform.h> 405a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/prom.h> 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/machdep.h> 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/pmac_feature.h> 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/smu.h> 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sections.h> 490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <asm/uaccess.h> 500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 51183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt#define VERSION "0.7" 520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#undef DEBUG_SMU 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_SMU 571beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0) 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DPRINTK(fmt, args...) do { } while (0) 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is the command buffer passed to the SMU hardware 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#define SMU_MAX_DATA 254 660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct smu_cmd_buf { 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 cmd; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 length; 700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 data[SMU_MAX_DATA]; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct smu_device { 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spinlock_t lock; 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct device_node *of_node; 762dc11581376829303b98eadb2de253bee065a56aGrant Likely struct platform_device *of_dev; 770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int doorbell; /* doorbell gpio */ 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u32 __iomem *db_buf; /* doorbell buffer */ 79f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt struct device_node *db_node; 80f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt unsigned int db_irq; 810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int msg; 82f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt struct device_node *msg_node; 83f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt unsigned int msg_irq; 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct smu_cmd_buf *cmd_buf; /* command buffer virtual */ 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u32 cmd_buf_abs; /* command buffer absolute */ 860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head cmd_list; 870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd_cur; /* pending command */ 88592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt int broken_nap; 890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head cmd_i2c_list; 900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */ 910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct timer_list i2c_timer; 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * I don't think there will ever be more than one SMU, so 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for now, just hard code that 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 98d851b6e04ee978b0c1b187bee682592aa72f22eaArnd Bergmannstatic DEFINE_MUTEX(smu_mutex); 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct smu_device *smu; 10014cc3e2b633bb64063698980974df4535368e98fIngo Molnarstatic DEFINE_MUTEX(smu_part_access); 101f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidtstatic int smu_irq_inited; 1020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 103730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidtstatic void smu_i2c_retry(unsigned long data); 104730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * SMU driver low level stuff 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_start_cmd(void) 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long faddr, fend; 1120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd; 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (list_empty(&smu->cmd_list)) 1150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 1160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fetch first command in queue */ 1180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link); 1190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_cur = cmd; 1200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&cmd->link); 1210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, 1230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_len); 124ebd004e4edd5628617c0faed11162b8a095ad852Andy Shevchenko DPRINTK("SMU: data buffer: %8ph\n", cmd->data_buf); 1250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fill the SMU command buffer */ 1270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_buf->cmd = cmd->cmd; 1280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_buf->length = cmd->data_len; 1290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(smu->cmd_buf->data, cmd->data_buf, cmd->data_len); 1300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Flush command and data to RAM */ 1320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fend = faddr + smu->cmd_buf->length + 2; 1340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt flush_inval_dcache_range(faddr, fend); 1350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 136592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt 137592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt /* We also disable NAP mode for the duration of the command 138592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * on U3 based machines. 139592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * This is slightly racy as it can be written back to 1 by a sysctl 140592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * but that never happens in practice. There seem to be an issue with 141592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * U3 based machines such as the iMac G5 where napping for the 142592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * whole duration of the command prevents the SMU from fetching it 143592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * from memory. This might be related to the strange i2c based 144592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt * mechanism the SMU uses to access memory. 145592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt */ 146592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt if (smu->broken_nap) 147592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt powersave_nap = 0; 148592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt 1490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* This isn't exactly a DMA mapping here, I suspect 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the SMU is actually communicating with us via i2c to the 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * northbridge or the CPU to access RAM. 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt writel(smu->cmd_buf_abs, smu->db_buf); 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ring the SMU doorbell */ 1560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, smu->doorbell, 4); 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1607d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t smu_db_intr(int irq, void *arg) 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 1630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd; 1640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc) = NULL; 1650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc = NULL; 1660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 gpio; 1670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc = 0; 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* SMU completed the command, well, we hope, let's make sure 1700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * of it 1710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 1720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 175a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt if ((gpio & 7) != 7) { 176a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 1770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 178a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt } 1790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd = smu->cmd_cur; 1810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_cur = NULL; 1820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd == NULL) 1830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt goto bail; 1840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc == 0) { 1860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long faddr; 1870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int reply_len; 1880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 ack; 1890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* CPU might have brought back the cache line, so we need 1910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * to flush again before peeking at the SMU response. We 1920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * flush the entire buffer for now as we haven't read the 193efad798b9f01300565f65058b153250cc49d58f2Paulius Zaleckas * reply length (it's only 2 cache lines anyway) 1940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 1950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt flush_inval_dcache_range(faddr, faddr + 256); 1970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now check ack */ 1990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ack = (~cmd->cmd) & 0xff; 2000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (ack != smu->cmd_buf->cmd) { 2010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: incorrect ack, want %x got %x\n", 2020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ack, smu->cmd_buf->cmd); 2030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = -EIO; 2040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 2050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len = rc == 0 ? smu->cmd_buf->length : 0; 2060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: reply len: %d\n", reply_len); 2070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (reply_len > cmd->reply_len) { 2080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: reply buffer too small," 2090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "got %d bytes for a %d bytes buffer\n", 2100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len, cmd->reply_len); 2110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len = cmd->reply_len; 2120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 2130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len = reply_len; 2140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->reply_buf && reply_len) 2150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len); 2160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 2170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now complete the command. Write status last in order as we lost 2190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * ownership of the command structure as soon as it's no longer -1 2200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done = cmd->done; 2220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt misc = cmd->misc; 2230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt mb(); 2240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = rc; 225592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt 226592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt /* Re-enable NAP mode */ 227592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt if (smu->broken_nap) 228592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt powersave_nap = 1; 2290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt bail: 2300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Start next command if any */ 2310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 2320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Call command completion handler if any */ 2350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (done) 2360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done(cmd, misc); 2370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2437d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t smu_msg_intr(int irq, void *arg) 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* I don't quite know what to do with this one, we seem to never 2460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * receive it, so I suspect we have to arm it someway in the SMU 2470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * to start getting events that way. 2480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_INFO "SMU: message interrupt !\n"); 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 2540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 2580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Queued command management. 2590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * 2600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_cmd(struct smu_cmd *cmd) 2630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 2640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 2670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 2680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->data_len > SMU_MAX_DATA || 2690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len > SMU_MAX_DATA) 2700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 2710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = 1; 2730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 2740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_list); 2750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 2760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 2770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 279f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt /* Workaround for early calls when irq isn't available */ 280f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (!smu_irq_inited || smu->db_irq == NO_IRQ) 281f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu_spinwait_cmd(cmd); 282f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt 2830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_queue_cmd); 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_simple(struct smu_simple_cmd *scmd, u8 command, 2890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int data_len, 2900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc), 2910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc, ...) 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd = &scmd->cmd; 2940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_list list; 2950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int i; 2960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (data_len > sizeof(scmd->buffer)) 2980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 2990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(scmd, 0, sizeof(*scmd)); 3010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->cmd = command; 3020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_len = data_len; 3030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_buf = scmd->buffer; 3040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len = sizeof(scmd->buffer); 3050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_buf = scmd->buffer; 3060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->done = done; 3070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->misc = misc; 3080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_start(list, misc); 3100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (i = 0; i < data_len; ++i) 3110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->buffer[i] = (u8)va_arg(list, int); 3120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_end(list); 3130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_queue_cmd(cmd); 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_queue_simple); 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_poll(void) 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 gpio; 3220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 3240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 3250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 3270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if ((gpio & 7) == 7) 3287d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells smu_db_intr(smu->db_irq, smu); 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_poll); 3310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_done_complete(struct smu_cmd *cmd, void *misc) 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct completion *comp = misc; 3360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt complete(comp); 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_done_complete); 3400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_spinwait_cmd(struct smu_cmd *cmd) 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt while(cmd->status == 1) 3450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_poll(); 3460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 3470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_spinwait_cmd); 3480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* RTC low level commands */ 3510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic inline int bcd2hex (int n) 3520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 3530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return (((n & 0xf0) >> 4) * 10) + (n & 0xf); 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic inline int hex2bcd (int n) 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return ((n / 10) << 4) + (n % 10); 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf, 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct rtc_time *time) 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->cmd = 0x8e; 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->length = 8; 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[0] = 0x80; 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[1] = hex2bcd(time->tm_sec); 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[2] = hex2bcd(time->tm_min); 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[3] = hex2bcd(time->tm_hour); 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[4] = time->tm_wday; 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[5] = hex2bcd(time->tm_mday); 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1; 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[7] = hex2bcd(time->tm_year - 100); 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_get_rtc_time(struct rtc_time *time, int spinwait) 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(time, 0, sizeof(struct rtc_time)); 3880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 1, NULL, NULL, 3890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SMU_CMD_RTC_GET_DATETIME); 3900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 3910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 3920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_sec = bcd2hex(cmd.buffer[0]); 3950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_min = bcd2hex(cmd.buffer[1]); 3960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_hour = bcd2hex(cmd.buffer[2]); 3970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_wday = bcd2hex(cmd.buffer[3]); 3980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_mday = bcd2hex(cmd.buffer[4]); 3990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_mon = bcd2hex(cmd.buffer[5]) - 1; 4000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_year = bcd2hex(cmd.buffer[6]) + 100; 4010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_set_rtc_time(struct rtc_time *time, int spinwait) 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 8, NULL, NULL, 4150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SMU_CMD_RTC_SET_DATETIME, 4160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_sec), 4170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_min), 4180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_hour), 4190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_wday, 4200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_mday), 4210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_mon) + 1, 4220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_year - 100)); 4230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 4240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 4250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid smu_shutdown(void) 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 9, NULL, NULL, 4390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 'S', 'H', 'U', 'T', 'D', 'O', 'W', 'N', 0)) 4400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 4410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid smu_restart(void) 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, NULL, NULL, 4550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 'R', 'E', 'S', 'T', 'A', 'R', 'T', 0)) 4560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 4570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint smu_present(void) 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return smu != NULL; 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_present); 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 470183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtint __init smu_init (void) 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct device_node *np; 473018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr const u32 *data; 47473f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall int ret = 0; 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds np = of_find_node_by_type(NULL, "smu"); 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (np == NULL) 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 480592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt printk(KERN_INFO "SMU: Driver %s %s\n", VERSION, AUTHOR); 4810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu_cmdbuf_abs == 0) { 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Command buffer not allocated !\n"); 48473f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall ret = -EINVAL; 48573f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall goto fail_np; 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu = alloc_bootmem(sizeof(struct smu_device)); 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&smu->lock); 4910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_list); 4920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_i2c_list); 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->of_node = np; 4940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = NO_IRQ; 4950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = NO_IRQ; 4960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 32 bits value safely 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; 50148817c58066fe61d3dde100e2c0dd7054418ee92Michael Ellerman smu->cmd_buf = __va(smu_cmdbuf_abs); 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 503f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_node = of_find_node_by_name(NULL, "smu-doorbell"); 504f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->db_node == NULL) { 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); 50673f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall ret = -ENXIO; 50773f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall goto fail_bootmem; 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 50901b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell data = of_get_property(smu->db_node, "reg", NULL); 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data == NULL) { 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n"); 51273f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall ret = -ENXIO; 51373f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall goto fail_db_node; 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Current setup has one doorbell GPIO that does both doorbell 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and ack. GPIOs are at 0x50, best would be to find that out 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the device-tree though. 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->doorbell = *data; 5210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->doorbell < 0x50) 5220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->doorbell += 0x50; 5230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now look for the smu-interrupt GPIO */ 5250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt do { 526f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt"); 527f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->msg_node == NULL) 5280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 52901b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell data = of_get_property(smu->msg_node, "reg", NULL); 5300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (data == NULL) { 531f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt of_node_put(smu->msg_node); 532f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_node = NULL; 5330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 5340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg = *data; 5360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->msg < 0x50) 5370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg += 0x50; 5380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } while(0); 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Doorbell buffer is currently hard-coded, I didn't find a proper 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * device-tree entry giving the address. Best would probably to use 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * an offset for K2 base though, but let's do it that way for now. 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->db_buf = ioremap(0x8000860c, 0x1000); 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu->db_buf == NULL) { 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n"); 54773f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall ret = -ENXIO; 54873f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall goto fail_msg_node; 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 551592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt /* U3 has an issue with NAP mode when issuing SMU commands */ 552592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt smu->broken_nap = pmac_get_uninorth_variant() < 4; 553592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt if (smu->broken_nap) 554592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt printk(KERN_INFO "SMU: using NAP mode workaround\n"); 555592a607bbc053bc6f614a0e619326009f4b3829eBenjamin Herrenschmidt 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sys_ctrler = SYS_CTRLER_SMU; 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 55973f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawallfail_msg_node: 56073f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall if (smu->msg_node) 56173f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall of_node_put(smu->msg_node); 56273f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawallfail_db_node: 56373f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall of_node_put(smu->db_node); 56473f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawallfail_bootmem: 56581df9bff2609f07cef4690ac2ebda1611b55b05aJoonsoo Kim free_bootmem(__pa(smu), sizeof(struct smu_device)); 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu = NULL; 56773f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawallfail_np: 56873f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall of_node_put(np); 56973f38fe1b563a9d23ffacbda7b51decf41b0c49cJulia Lawall return ret; 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_late_init(void) 5740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 5750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 5760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 5770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 578730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt init_timer(&smu->i2c_timer); 579730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt smu->i2c_timer.function = smu_i2c_retry; 580730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt smu->i2c_timer.data = (unsigned long)smu; 581730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt 582f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->db_node) { 583f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_irq = irq_of_parse_and_map(smu->db_node, 0); 584f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->db_irq == NO_IRQ) 585f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt printk(KERN_ERR "smu: failed to map irq for node %s\n", 586f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_node->full_name); 587f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt } 588f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->msg_node) { 589f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_irq = irq_of_parse_and_map(smu->msg_node, 0); 590f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->msg_irq == NO_IRQ) 591f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt printk(KERN_ERR "smu: failed to map irq for node %s\n", 592f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_node->full_name); 593f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt } 594f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt 5950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 5960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Try to request the interrupts 5970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 5980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->db_irq != NO_IRQ) { 6000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (request_irq(smu->db_irq, smu_db_intr, 601dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner IRQF_SHARED, "SMU doorbell", smu) < 0) { 6020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 6030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "request interrupt %d\n", 6040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq); 6050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = NO_IRQ; 6060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 6070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 6080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->msg_irq != NO_IRQ) { 6100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (request_irq(smu->msg_irq, smu_msg_intr, 611dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner IRQF_SHARED, "SMU message", smu) < 0) { 6120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 6130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "request interrupt %d\n", 6140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq); 6150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = NO_IRQ; 6160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 6170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 6180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 619f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu_irq_inited = 1; 6200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 622730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt/* This has to be before arch_initcall as the low i2c stuff relies on the 623730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt * above having been done before we reach arch_initcalls 624730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt */ 625730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidtcore_initcall(smu_late_init); 6260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 6280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * sysfs visibility 6290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 631c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void smu_expose_childs(struct work_struct *unused) 6320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 633a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt struct device_node *np; 634a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt 635a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) 63655b61fec22caa3e7872caea6c4100fc75cb8f49bStephen Rothwell if (of_device_is_compatible(np, "smu-sensors")) 637730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt of_platform_device_create(np, "smu-sensors", 638730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt &smu->of_dev->dev); 6390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 641c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic DECLARE_WORK(smu_expose_childs_work, smu_expose_childs); 6420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 643000061245a6797d542854106463b6b20fbdcb12eGrant Likelystatic int smu_platform_probe(struct platform_device* dev) 6440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 6460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 6470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->of_dev = dev; 6480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 6500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Ok, we are matched, now expose all i2c busses. We have to defer 6510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * that unfortunately or it would deadlock inside the device model 6520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule_work(&smu_expose_childs_work); 6540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 65846759a7c132648d79121518d2f7c34edc3f0cf58Márton Némethstatic const struct of_device_id smu_platform_match[] = 6590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt { 6610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .type = "smu", 6620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt }, 6630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt {}, 6640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 6650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 666000061245a6797d542854106463b6b20fbdcb12eGrant Likelystatic struct platform_driver smu_of_platform_driver = 6670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6684018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .driver = { 6694018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .name = "smu", 6704018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .owner = THIS_MODULE, 6714018294b53d1dae026880e45f174c1cc63b5d435Grant Likely .of_match_table = smu_platform_match, 6724018294b53d1dae026880e45f174c1cc63b5d435Grant Likely }, 6730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .probe = smu_platform_probe, 6740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 6750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int __init smu_init_sysfs(void) 6770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 6790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * For now, we don't power manage machines with an SMU chip, 6800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * I'm a bit too far from figuring out how that works with those 6810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * new chipsets, but that will come back and bite us 6820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 683000061245a6797d542854106463b6b20fbdcb12eGrant Likely platform_driver_register(&smu_of_platform_driver); 6840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtdevice_initcall(smu_init_sysfs); 6880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6892dc11581376829303b98eadb2de253bee065a56aGrant Likelystruct platform_device *smu_get_ofdev(void) 6900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 6920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return NULL; 6930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu->of_dev; 6940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL_GPL(smu_get_ofdev); 6970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 6990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * i2c interface 7000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 7010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail) 7030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 7040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_i2c_cmd *cmd, void *misc) = cmd->done; 7050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc = cmd->misc; 7060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 7070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check for read case */ 7090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!fail && cmd->read) { 7100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->pdata[0] < 1) 7110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = 1; 7120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else 7130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(cmd->info.data, &cmd->pdata[1], 7140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.datalen); 7150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: completing, success: %d\n", !fail); 7180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Update status and mark no pending i2c command with lock 7200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * held so nobody comes in while we dequeue an eventual 7210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * pending next i2c command 7220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 7230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 7240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = NULL; 7250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wmb(); 7260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = fail ? -EIO : 0; 7270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Is there another i2c command waiting ? */ 7290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!list_empty(&smu->cmd_i2c_list)) { 7300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *newcmd; 7310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fetch it, new current, remove from list */ 7330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt newcmd = list_entry(smu->cmd_i2c_list.next, 7340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd, link); 7350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = newcmd; 7360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&cmd->link); 7370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Queue with low level smu */ 7390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 7400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 7410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 7420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 7440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Call command completion handler if any */ 7460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (done) 7470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done(cmd, misc); 7480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_retry(unsigned long data) 7530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 754730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt struct smu_i2c_cmd *cmd = smu->cmd_i2c_cur; 7550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, requeuing...\n"); 7570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* requeue command simply by resetting reply_len */ 7590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 760730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 7610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_queue_cmd(&cmd->scmd); 7620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) 7660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 7670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *cmd = misc; 7680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int fail = 0; 7690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c compl. stage=%d status=%x pdata[0]=%x rlen: %x\n", 7710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage, scmd->status, cmd->pdata[0], scmd->reply_len); 7720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check for possible status */ 7740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (scmd->status < 0) 7750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = 1; 7760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (cmd->read) { 7770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->stage == 0) 7780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else 7800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] >= 0x80; 7810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else { 7820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Handle failures by requeuing command, after 5ms interval 7860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 7870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (fail && --cmd->retries > 0) { 7880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, starting timer...\n"); 789730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt BUG_ON(cmd != smu->cmd_i2c_cur); 790f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (!smu_irq_inited) { 791f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt mdelay(5); 792f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu_i2c_retry(0); 793f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt return; 794f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt } 795730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5)); 7960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 7970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* If failure or stage 1, command is complete */ 8000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (fail || cmd->stage != 0) { 8010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_i2c_complete_command(cmd, fail); 8020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 8030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: going to stage 1\n"); 8060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Ok, initial command complete, now poll status */ 8080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->reply_buf = cmd->pdata; 809730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt scmd->reply_len = sizeof(cmd->pdata); 8100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->data_buf = cmd->pdata; 8110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->data_len = 1; 8120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0; 8130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage = 1; 8140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->retries = 20; 8150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_queue_cmd(scmd); 8160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 8170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_i2c(struct smu_i2c_cmd *cmd) 8200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 8210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 8220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 8240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 8250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fill most fields of scmd */ 8270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.cmd = SMU_CMD_I2C_COMMAND; 8280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.done = smu_i2c_low_completion; 8290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.misc = cmd; 8300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.reply_buf = cmd->pdata; 831730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 8320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_buf = (u8 *)(char *)&cmd->info; 8330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.status = 1; 8340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage = 0; 8350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 8360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->retries = 20; 8370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = 1; 8380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check transfer type, sanitize some "info" fields 8400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * based on transfer type and do more checking 8410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.caddr = cmd->info.devaddr; 8430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->read = cmd->info.devaddr & 0x01; 8440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt switch(cmd->info.type) { 8450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_SIMPLE: 8460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(&cmd->info.sublen, 0, 4); 8470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 8480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_COMBINED: 8490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.devaddr &= 0xfe; 8500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_STDSUB: 8510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.sublen > 3) 8520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 8540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt default: 8550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Finish setting up command based on transfer direction 8590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->read) { 8610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_READ_MAX) 8620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(cmd->info.data, 0xff, cmd->info.datalen); 8640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_len = 9; 8650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else { 8660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_WRITE_MAX) 8670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_len = 9 + cmd->info.datalen; 8690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c enqueuing command\n"); 8720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: %s, len=%d bus=%x addr=%x sub0=%x type=%x\n", 8730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->read ? "read" : "write", cmd->info.datalen, 8740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.bus, cmd->info.caddr, 8750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.subaddr[0], cmd->info.type); 8760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Enqueue command in i2c list, and if empty, enqueue also in 8790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * main command list 8800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 8820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_i2c_cur == NULL) { 8830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = cmd; 8840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 8850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 8860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 8870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else 8880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_i2c_list); 8890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 8900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 8920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 8930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 894183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt/* 895183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * Handling of "partitions" 896183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 897183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 898183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len) 899183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 9006e9a4738c9fadb7cbdcabc1e3b415159f3741ed9Peter Zijlstra DECLARE_COMPLETION_ONSTACK(comp); 901183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int chunk; 902183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_cmd cmd; 903183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int rc; 904183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt u8 params[8]; 905183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 906183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* We currently use a chunk size of 0xe. We could check the 907183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * SMU firmware version and use bigger sizes though 908183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 909183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt chunk = 0xe; 910183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 911183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt while (len) { 912183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int clen = min(len, chunk); 913183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 914183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.cmd = SMU_CMD_MISC_ee_COMMAND; 915183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.data_len = 7; 916183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.data_buf = params; 917183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_len = chunk; 918183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_buf = dest; 919183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.done = smu_done_complete; 920183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.misc = ∁ 921183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC; 922183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[1] = 0x4; 923183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt *((u32 *)¶ms[2]) = addr; 924183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[6] = clen; 925183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 926183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt rc = smu_queue_cmd(&cmd); 927183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (rc) 928183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return rc; 929183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt wait_for_completion(&comp); 930183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.status != 0) 931183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return rc; 932183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.reply_len != clen) { 933183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: short read in " 934183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "smu_read_datablock, got: %d, want: %d\n", 935183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_len, clen); 936183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return -EIO; 937183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 938183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt len -= clen; 939183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt addr += clen; 940183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt dest += clen; 941183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 942183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return 0; 943183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 944183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 945183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic struct smu_sdbp_header *smu_create_sdb_partition(int id) 946183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 9476e9a4738c9fadb7cbdcabc1e3b415159f3741ed9Peter Zijlstra DECLARE_COMPLETION_ONSTACK(comp); 948183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_simple_cmd cmd; 949183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int addr, len, tlen; 950183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_sdbp_header *hdr; 951183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct property *prop; 952183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 953183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* First query the partition info */ 9541beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq); 955183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, 956183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt smu_done_complete, &comp, 957183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt SMU_CMD_PARTITION_LATEST, id); 958183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt wait_for_completion(&comp); 9591beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("SMU: done, status: %d, reply_len: %d\n", 9601beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt cmd.cmd.status, cmd.cmd.reply_len); 961183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 962183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Partition doesn't exist (or other error) */ 963183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) 964183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 965183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 966183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Fetch address and length from reply */ 967183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt addr = *((u16 *)cmd.buffer); 968183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt len = cmd.buffer[3] << 2; 969183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Calucluate total length to allocate, including the 17 bytes 970183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * for "sdb-partition-XX" that we append at the end of the buffer 971183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 972183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt tlen = sizeof(struct property) + len + 18; 973183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 974cd86128088554d64fea1679191509f00e6353c5bRobert P. J. Day prop = kzalloc(tlen, GFP_KERNEL); 975183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (prop == NULL) 976183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 977183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt hdr = (struct smu_sdbp_header *)(prop + 1); 978183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->name = ((char *)prop) + tlen - 18; 979183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt sprintf(prop->name, "sdb-partition-%02x", id); 980183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->length = len; 9811a38147ed0737a9c01dbf5f2ca47fd2a0aa5cb55Stephen Rothwell prop->value = hdr; 982183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->next = NULL; 983183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 984183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Read the datablock */ 985183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (smu_read_datablock((u8 *)hdr, addr, len)) { 986183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: datablock read failed while reading " 987183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "partition %02x !\n", id); 988183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 989183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 990183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 991183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Got it, check a few things and create the property */ 992183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (hdr->id != id) { 993183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Reading partition %02x and got " 994183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "%02x !\n", id, hdr->id); 995183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 996183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 99779d1c712958f94372482ad74578b00f44e744c12Nathan Fontenot if (of_add_property(smu->of_node, prop)) { 998183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " 999183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "property !\n", id); 1000183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 1001183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 1002183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 1003183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return hdr; 1004183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt failure: 1005183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt kfree(prop); 1006183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 1007183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 1008183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 1009183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt/* Note: Only allowed to return error code in pointers (using ERR_PTR) 1010183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * when interruptible is 1 1011183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 1012018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerrconst struct smu_sdbp_header *__smu_get_sdb_partition(int id, 1013018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr unsigned int *size, int interruptible) 10144350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt{ 10154350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt char pname[32]; 1016018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr const struct smu_sdbp_header *part; 10174350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt 10184350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt if (!smu) 10194350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt return NULL; 10204350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt 10214350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt sprintf(pname, "sdb-partition-%02x", id); 1022183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 10231beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("smu_get_sdb_partition(%02x)\n", id); 10241beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt 1025183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (interruptible) { 1026183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int rc; 102714cc3e2b633bb64063698980974df4535368e98fIngo Molnar rc = mutex_lock_interruptible(&smu_part_access); 1028183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (rc) 1029183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return ERR_PTR(rc); 1030183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } else 103114cc3e2b633bb64063698980974df4535368e98fIngo Molnar mutex_lock(&smu_part_access); 1032183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 103301b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell part = of_get_property(smu->of_node, pname, size); 1034183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part == NULL) { 10351beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("trying to extract from SMU ...\n"); 1036183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = smu_create_sdb_partition(id); 1037183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part != NULL && size) 1038183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt *size = part->len << 2; 1039183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 104014cc3e2b633bb64063698980974df4535368e98fIngo Molnar mutex_unlock(&smu_part_access); 1041183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return part; 1042183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 1043183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 1044018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerrconst struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) 1045183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 1046183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return __smu_get_sdb_partition(id, size, 0); 10474350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt} 10484350147a816b9c5b40fa59e4fa23f17490630b79Benjamin HerrenschmidtEXPORT_SYMBOL(smu_get_sdb_partition); 10490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 10520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Userland driver interface 10530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 10540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic LIST_HEAD(smu_clist); 10570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic DEFINE_SPINLOCK(smu_clist_lock); 10580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtenum smu_file_mode { 10600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_commands, 10610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_events, 10620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_closing 10630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 10640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstruct smu_private 10660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head list; 10680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt enum smu_file_mode mode; 10690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int busy; 10700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd cmd; 10710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spinlock_t lock; 10720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wait_queue_head_t wait; 10730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 buffer[SMU_MAX_DATA]; 10740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 10750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_open(struct inode *inode, struct file *file) 10780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp; 10800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 10810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1082dd00cc486ab1c17049a535413d1751ef3482141cYoann Padioleau pp = kzalloc(sizeof(struct smu_private), GFP_KERNEL); 10830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 10840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENOMEM; 10850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_init(&pp->lock); 10860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_commands; 10870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt init_waitqueue_head(&pp->wait); 10880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1089d851b6e04ee978b0c1b187bee682592aa72f22eaArnd Bergmann mutex_lock(&smu_mutex); 10900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 10910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add(&pp->list, &smu_clist); 10920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 10930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt file->private_data = pp; 1094d851b6e04ee978b0c1b187bee682592aa72f22eaArnd Bergmann mutex_unlock(&smu_mutex); 10950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 10970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 10980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_user_cmd_done(struct smu_cmd *cmd, void *misc) 11010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = misc; 11030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wake_up_all(&pp->wait); 11050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_write(struct file *file, const char __user *buf, 11090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size_t count, loff_t *ppos) 11100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 11120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 11130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_user_cmd_hdr hdr; 11140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc = 0; 11150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy) 11170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBUSY; 11180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (copy_from_user(&hdr, buf, sizeof(hdr))) 11190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { 11210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_events; 11220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 1123183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { 1124018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr const struct smu_sdbp_header *part; 1125183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); 1126183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part == NULL) 1127183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return -EINVAL; 1128183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt else if (IS_ERR(part)) 1129183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return PTR_ERR(part); 1130183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return 0; 11310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) 11320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 11330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (pp->mode != smu_file_commands) 11340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBADFD; 11350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (hdr.data_len > SMU_MAX_DATA) 11360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 11370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy) { 11400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBUSY; 11420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 1; 11440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.status = 1; 11450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (copy_from_user(pp->buffer, buf + sizeof(hdr), hdr.data_len)) { 11480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 0; 11490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.cmd = hdr.cmd; 11530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.data_len = hdr.data_len; 11540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_len = SMU_MAX_DATA; 11550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.data_buf = pp->buffer; 11560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_buf = pp->buffer; 11570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.done = smu_user_cmd_done; 11580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.misc = pp; 11590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_cmd(&pp->cmd); 11600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc < 0) 11610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return count; 11630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read_command(struct file *file, struct smu_private *pp, 11670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt char __user *buf, size_t count) 11680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 11700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_user_reply_hdr hdr; 11710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 11720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int size, rc = 0; 11730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!pp->busy) 11750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 11760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (count < sizeof(struct smu_user_reply_hdr)) 11770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EOVERFLOW; 11780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status == 1) { 118086e4754ac8fde9a9c913571016bc31257aa2e195Julia Lawall if (file->f_flags & O_NONBLOCK) { 118186e4754ac8fde9a9c913571016bc31257aa2e195Julia Lawall spin_unlock_irqrestore(&pp->lock, flags); 11820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EAGAIN; 118386e4754ac8fde9a9c913571016bc31257aa2e195Julia Lawall } 11840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 11850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (;;) { 11860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_INTERRUPTIBLE); 11870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = 0; 11880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 1) 11890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 11900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = -ERESTARTSYS; 11910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (signal_pending(current)) 11920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 11930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule(); 11950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 11980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 11990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 12020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 12030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 0) 12040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_len = 0; 12050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size = sizeof(hdr) + pp->cmd.reply_len; 12060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (count < size) 12070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size = count; 12080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = size; 12090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hdr.status = pp->cmd.status; 12100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hdr.reply_len = pp->cmd.reply_len; 12110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (copy_to_user(buf, &hdr, sizeof(hdr))) 12120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 12130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size -= sizeof(hdr); 12140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (size && copy_to_user(buf + sizeof(hdr), pp->buffer, size)) 12150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 12160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 0; 12170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 12190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read_events(struct file *file, struct smu_private *pp, 12230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt char __user *buf, size_t count) 12240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Not implemented */ 12260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt msleep_interruptible(1000); 12270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read(struct file *file, char __user *buf, 12320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size_t count, loff_t *ppos) 12330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_commands) 12370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_read_command(file, pp, buf, count); 12380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_events) 12390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_read_events(file, pp, buf, count); 12400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBADFD; 12420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic unsigned int smu_fpoll(struct file *file, poll_table *wait) 12450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int mask = 0; 12480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 12490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 12510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_commands) { 12540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt poll_wait(file, &pp->wait, wait); 12550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy && pp->cmd.status != 1) 12580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt mask |= POLLIN; 12590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12602055fb41ea6bf9507d94b3515528249ce12409e3Rasmus Villemoes } 12612055fb41ea6bf9507d94b3515528249ce12409e3Rasmus Villemoes if (pp->mode == smu_file_events) { 12620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Not yet implemented */ 12630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return mask; 12650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_release(struct inode *inode, struct file *file) 12680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 12710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int busy; 12720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 12740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt file->private_data = NULL; 12770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Mark file as closing to avoid races with new request */ 12790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_closing; 12810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt busy = pp->busy; 12820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Wait for any pending request to complete */ 12840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (busy && pp->cmd.status == 1) { 12850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 12860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 12880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (;;) { 12890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_UNINTERRUPTIBLE); 12900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 1) 12910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 12920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 129394256dd680f837dc14dd7d1377c5326fb3362721Andrew Morton schedule(); 129494256dd680f837dc14dd7d1377c5326fb3362721Andrew Morton spin_lock_irqsave(&pp->lock, flags); 12950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 12970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 12980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 13000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 13010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 13020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&pp->list); 13030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 13040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt kfree(pp); 13050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 13060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 13070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 13080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 13090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1310fa027c2a0a0d6d1df6b29ee99048502c93da0dd4Arjan van de Venstatic const struct file_operations smu_device_fops = { 13110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .llseek = no_llseek, 13120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .read = smu_read, 13130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .write = smu_write, 13140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .poll = smu_fpoll, 13150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .open = smu_open, 13160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .release = smu_release, 13170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 13180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 13196b67f62cf655c80147435544a2f8f6f57e07ec87Stephen Rothwellstatic struct miscdevice pmu_device = { 13200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt MISC_DYNAMIC_MINOR, "smu", &smu_device_fops 13210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 13220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 13230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_device_init(void) 13240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 13250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 13260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 13270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (misc_register(&pmu_device) < 0) 13280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_ERR "via-pmu: cannot register misc device.\n"); 13290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 13300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 13310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtdevice_initcall(smu_device_init); 1332