smu.c revision efad798b9f01300565f65058b153250cc49d58f2
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 150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * interrutps 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/sysdev.h> 360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/poll.h> 3714cc3e2b633bb64063698980974df4535368e98fIngo Molnar#include <linux/mutex.h> 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/prom.h> 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/machdep.h> 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/pmac_feature.h> 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/smu.h> 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sections.h> 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/abs_addr.h> 470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <asm/uaccess.h> 480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <asm/of_device.h> 497eebde700fe6fd6573e80bd8e5ed82b4ae705575Benjamin Herrenschmidt#include <asm/of_platform.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; 760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct of_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 */ 880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head cmd_i2c_list; 890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */ 900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct timer_list i2c_timer; 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * I don't think there will ever be more than one SMU, so 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for now, just hard code that 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct smu_device *smu; 9814cc3e2b633bb64063698980974df4535368e98fIngo Molnarstatic DEFINE_MUTEX(smu_part_access); 99f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidtstatic int smu_irq_inited; 1000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 101730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidtstatic void smu_i2c_retry(unsigned long data); 102730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * SMU driver low level stuff 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_start_cmd(void) 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long faddr, fend; 1100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (list_empty(&smu->cmd_list)) 1130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 1140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fetch first command in queue */ 1160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link); 1170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_cur = cmd; 1180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&cmd->link); 1190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, 1210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_len); 122183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n", 1230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1], 124183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3], 125183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5], 126183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]); 1270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fill the SMU command buffer */ 1290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_buf->cmd = cmd->cmd; 1300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_buf->length = cmd->data_len; 1310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(smu->cmd_buf->data, cmd->data_buf, cmd->data_len); 1320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Flush command and data to RAM */ 1340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fend = faddr + smu->cmd_buf->length + 2; 1360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt flush_inval_dcache_range(faddr, fend); 1370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* This isn't exactly a DMA mapping here, I suspect 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the SMU is actually communicating with us via i2c to the 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * northbridge or the CPU to access RAM. 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt writel(smu->cmd_buf_abs, smu->db_buf); 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ring the SMU doorbell */ 1450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, smu->doorbell, 4); 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1497d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t smu_db_intr(int irq, void *arg) 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 1520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd; 1530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc) = NULL; 1540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc = NULL; 1550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 gpio; 1560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc = 0; 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* SMU completed the command, well, we hope, let's make sure 1590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * of it 1600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 1610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 164a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt if ((gpio & 7) != 7) { 165a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 1660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 167a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt } 1680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd = smu->cmd_cur; 1700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_cur = NULL; 1710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd == NULL) 1720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt goto bail; 1730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc == 0) { 1750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long faddr; 1760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int reply_len; 1770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 ack; 1780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* CPU might have brought back the cache line, so we need 1800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * to flush again before peeking at the SMU response. We 1810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * flush the entire buffer for now as we haven't read the 182efad798b9f01300565f65058b153250cc49d58f2Paulius Zaleckas * reply length (it's only 2 cache lines anyway) 1830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 1840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt flush_inval_dcache_range(faddr, faddr + 256); 1860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now check ack */ 1880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ack = (~cmd->cmd) & 0xff; 1890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (ack != smu->cmd_buf->cmd) { 1900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: incorrect ack, want %x got %x\n", 1910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ack, smu->cmd_buf->cmd); 1920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = -EIO; 1930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 1940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len = rc == 0 ? smu->cmd_buf->length : 0; 1950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: reply len: %d\n", reply_len); 1960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (reply_len > cmd->reply_len) { 1970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: reply buffer too small," 1980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "got %d bytes for a %d bytes buffer\n", 1990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len, cmd->reply_len); 2000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len = cmd->reply_len; 2010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 2020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len = reply_len; 2030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->reply_buf && reply_len) 2040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len); 2050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 2060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now complete the command. Write status last in order as we lost 2080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * ownership of the command structure as soon as it's no longer -1 2090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done = cmd->done; 2110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt misc = cmd->misc; 2120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt mb(); 2130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = rc; 2140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt bail: 2150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Start next command if any */ 2160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 2170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Call command completion handler if any */ 2200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (done) 2210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done(cmd, misc); 2220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2287d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t smu_msg_intr(int irq, void *arg) 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* I don't quite know what to do with this one, we seem to never 2310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * receive it, so I suspect we have to arm it someway in the SMU 2320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * to start getting events that way. 2330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_INFO "SMU: message interrupt !\n"); 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 2390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 2430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Queued command management. 2440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * 2450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_cmd(struct smu_cmd *cmd) 2480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 2490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 2520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 2530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->data_len > SMU_MAX_DATA || 2540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len > SMU_MAX_DATA) 2550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 2560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = 1; 2580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 2590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_list); 2600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 2610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 2620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 264f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt /* Workaround for early calls when irq isn't available */ 265f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (!smu_irq_inited || smu->db_irq == NO_IRQ) 266f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu_spinwait_cmd(cmd); 267f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt 2680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_queue_cmd); 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_simple(struct smu_simple_cmd *scmd, u8 command, 2740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int data_len, 2750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc), 2760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc, ...) 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd = &scmd->cmd; 2790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_list list; 2800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int i; 2810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (data_len > sizeof(scmd->buffer)) 2830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 2840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(scmd, 0, sizeof(*scmd)); 2860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->cmd = command; 2870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_len = data_len; 2880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_buf = scmd->buffer; 2890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len = sizeof(scmd->buffer); 2900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_buf = scmd->buffer; 2910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->done = done; 2920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->misc = misc; 2930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_start(list, misc); 2950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (i = 0; i < data_len; ++i) 2960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->buffer[i] = (u8)va_arg(list, int); 2970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_end(list); 2980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_queue_cmd(cmd); 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_queue_simple); 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_poll(void) 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 gpio; 3070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 3090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 3100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 3120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if ((gpio & 7) == 7) 3137d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells smu_db_intr(smu->db_irq, smu); 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_poll); 3160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_done_complete(struct smu_cmd *cmd, void *misc) 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct completion *comp = misc; 3210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt complete(comp); 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_done_complete); 3250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_spinwait_cmd(struct smu_cmd *cmd) 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt while(cmd->status == 1) 3300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_poll(); 3310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 3320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_spinwait_cmd); 3330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* RTC low level commands */ 3360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic inline int bcd2hex (int n) 3370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 3380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return (((n & 0xf0) >> 4) * 10) + (n & 0xf); 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic inline int hex2bcd (int n) 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return ((n / 10) << 4) + (n % 10); 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf, 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct rtc_time *time) 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->cmd = 0x8e; 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->length = 8; 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[0] = 0x80; 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[1] = hex2bcd(time->tm_sec); 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[2] = hex2bcd(time->tm_min); 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[3] = hex2bcd(time->tm_hour); 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[4] = time->tm_wday; 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[5] = hex2bcd(time->tm_mday); 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1; 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[7] = hex2bcd(time->tm_year - 100); 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_get_rtc_time(struct rtc_time *time, int spinwait) 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(time, 0, sizeof(struct rtc_time)); 3730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 1, NULL, NULL, 3740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SMU_CMD_RTC_GET_DATETIME); 3750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 3760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 3770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_sec = bcd2hex(cmd.buffer[0]); 3800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_min = bcd2hex(cmd.buffer[1]); 3810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_hour = bcd2hex(cmd.buffer[2]); 3820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_wday = bcd2hex(cmd.buffer[3]); 3830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_mday = bcd2hex(cmd.buffer[4]); 3840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_mon = bcd2hex(cmd.buffer[5]) - 1; 3850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_year = bcd2hex(cmd.buffer[6]) + 100; 3860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_set_rtc_time(struct rtc_time *time, int spinwait) 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 8, NULL, NULL, 4000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SMU_CMD_RTC_SET_DATETIME, 4010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_sec), 4020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_min), 4030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_hour), 4040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_wday, 4050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_mday), 4060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_mon) + 1, 4070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_year - 100)); 4080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 4090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 4100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid smu_shutdown(void) 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 9, NULL, NULL, 4240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 'S', 'H', 'U', 'T', 'D', 'O', 'W', 'N', 0)) 4250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 4260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid smu_restart(void) 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, NULL, NULL, 4400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 'R', 'E', 'S', 'T', 'A', 'R', 'T', 0)) 4410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 4420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint smu_present(void) 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return smu != NULL; 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_present); 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 455183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtint __init smu_init (void) 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct device_node *np; 458018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr const u32 *data; 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds np = of_find_node_by_type(NULL, "smu"); 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (np == NULL) 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_INFO "SMU driver %s %s\n", VERSION, AUTHOR); 4650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu_cmdbuf_abs == 0) { 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Command buffer not allocated !\n"); 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu = alloc_bootmem(sizeof(struct smu_device)); 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(smu, 0, sizeof(*smu)); 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&smu->lock); 4770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_list); 4780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_i2c_list); 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->of_node = np; 4800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = NO_IRQ; 4810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = NO_IRQ; 4820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 32 bits value safely 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs); 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 489f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_node = of_find_node_by_name(NULL, "smu-doorbell"); 490f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->db_node == NULL) { 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto fail; 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 49401b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell data = of_get_property(smu->db_node, "reg", NULL); 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data == NULL) { 496f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt of_node_put(smu->db_node); 497f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_node = NULL; 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n"); 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto fail; 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Current setup has one doorbell GPIO that does both doorbell 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and ack. GPIOs are at 0x50, best would be to find that out 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the device-tree though. 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->doorbell = *data; 5070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->doorbell < 0x50) 5080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->doorbell += 0x50; 5090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now look for the smu-interrupt GPIO */ 5110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt do { 512f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt"); 513f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->msg_node == NULL) 5140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 51501b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell data = of_get_property(smu->msg_node, "reg", NULL); 5160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (data == NULL) { 517f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt of_node_put(smu->msg_node); 518f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_node = NULL; 5190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 5200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg = *data; 5220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->msg < 0x50) 5230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg += 0x50; 5240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } while(0); 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Doorbell buffer is currently hard-coded, I didn't find a proper 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * device-tree entry giving the address. Best would probably to use 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * an offset for K2 base though, but let's do it that way for now. 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->db_buf = ioremap(0x8000860c, 0x1000); 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu->db_buf == NULL) { 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n"); 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto fail; 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sys_ctrler = SYS_CTRLER_SMU; 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fail: 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu = NULL; 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENXIO; 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_late_init(void) 5470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 5480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 5490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 5500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 551730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt init_timer(&smu->i2c_timer); 552730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt smu->i2c_timer.function = smu_i2c_retry; 553730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt smu->i2c_timer.data = (unsigned long)smu; 554730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt 555f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->db_node) { 556f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_irq = irq_of_parse_and_map(smu->db_node, 0); 557f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->db_irq == NO_IRQ) 558f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt printk(KERN_ERR "smu: failed to map irq for node %s\n", 559f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->db_node->full_name); 560f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt } 561f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->msg_node) { 562f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_irq = irq_of_parse_and_map(smu->msg_node, 0); 563f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (smu->msg_irq == NO_IRQ) 564f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt printk(KERN_ERR "smu: failed to map irq for node %s\n", 565f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu->msg_node->full_name); 566f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt } 567f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt 5680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 5690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Try to request the interrupts 5700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 5710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->db_irq != NO_IRQ) { 5730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (request_irq(smu->db_irq, smu_db_intr, 574dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner IRQF_SHARED, "SMU doorbell", smu) < 0) { 5750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 5760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "request interrupt %d\n", 5770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq); 5780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = NO_IRQ; 5790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->msg_irq != NO_IRQ) { 5830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (request_irq(smu->msg_irq, smu_msg_intr, 584dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner IRQF_SHARED, "SMU message", smu) < 0) { 5850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 5860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "request interrupt %d\n", 5870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq); 5880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = NO_IRQ; 5890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 592f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu_irq_inited = 1; 5930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 5940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 595730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt/* This has to be before arch_initcall as the low i2c stuff relies on the 596730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt * above having been done before we reach arch_initcalls 597730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt */ 598730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidtcore_initcall(smu_late_init); 5990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 6010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * sysfs visibility 6020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 604c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void smu_expose_childs(struct work_struct *unused) 6050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 606a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt struct device_node *np; 607a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt 608a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) 60955b61fec22caa3e7872caea6c4100fc75cb8f49bStephen Rothwell if (of_device_is_compatible(np, "smu-sensors")) 610730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt of_platform_device_create(np, "smu-sensors", 611730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt &smu->of_dev->dev); 6120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 614c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic DECLARE_WORK(smu_expose_childs_work, smu_expose_childs); 6150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_platform_probe(struct of_device* dev, 6170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt const struct of_device_id *match) 6180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 6200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 6210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->of_dev = dev; 6220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 6240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Ok, we are matched, now expose all i2c busses. We have to defer 6250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * that unfortunately or it would deadlock inside the device model 6260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule_work(&smu_expose_childs_work); 6280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic struct of_device_id smu_platform_match[] = 6330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt { 6350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .type = "smu", 6360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt }, 6370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt {}, 6380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 6390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic struct of_platform_driver smu_of_platform_driver = 6410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .name = "smu", 6430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .match_table = smu_platform_match, 6440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .probe = smu_platform_probe, 6450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 6460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int __init smu_init_sysfs(void) 6480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 6500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Due to sysfs bogosity, a sysdev is not a real device, so 6510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * we should in fact create both if we want sysdev semantics 6520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * for power management. 6530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * For now, we don't power manage machines with an SMU chip, 6540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * I'm a bit too far from figuring out how that works with those 6550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * new chipsets, but that will come back and bite us 6560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6577eebde700fe6fd6573e80bd8e5ed82b4ae705575Benjamin Herrenschmidt of_register_platform_driver(&smu_of_platform_driver); 6580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtdevice_initcall(smu_init_sysfs); 6620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstruct of_device *smu_get_ofdev(void) 6640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 6660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return NULL; 6670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu->of_dev; 6680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL_GPL(smu_get_ofdev); 6710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 6730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * i2c interface 6740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail) 6770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_i2c_cmd *cmd, void *misc) = cmd->done; 6790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc = cmd->misc; 6800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 6810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check for read case */ 6830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!fail && cmd->read) { 6840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->pdata[0] < 1) 6850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = 1; 6860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else 6870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(cmd->info.data, &cmd->pdata[1], 6880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.datalen); 6890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 6900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: completing, success: %d\n", !fail); 6920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Update status and mark no pending i2c command with lock 6940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * held so nobody comes in while we dequeue an eventual 6950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * pending next i2c command 6960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 6980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = NULL; 6990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wmb(); 7000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = fail ? -EIO : 0; 7010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Is there another i2c command waiting ? */ 7030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!list_empty(&smu->cmd_i2c_list)) { 7040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *newcmd; 7050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fetch it, new current, remove from list */ 7070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt newcmd = list_entry(smu->cmd_i2c_list.next, 7080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd, link); 7090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = newcmd; 7100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&cmd->link); 7110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Queue with low level smu */ 7130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 7140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 7150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 7160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 7180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Call command completion handler if any */ 7200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (done) 7210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done(cmd, misc); 7220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_retry(unsigned long data) 7270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 728730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt struct smu_i2c_cmd *cmd = smu->cmd_i2c_cur; 7290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, requeuing...\n"); 7310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* requeue command simply by resetting reply_len */ 7330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 734730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 7350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_queue_cmd(&cmd->scmd); 7360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) 7400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 7410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *cmd = misc; 7420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int fail = 0; 7430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c compl. stage=%d status=%x pdata[0]=%x rlen: %x\n", 7450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage, scmd->status, cmd->pdata[0], scmd->reply_len); 7460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check for possible status */ 7480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (scmd->status < 0) 7490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = 1; 7500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (cmd->read) { 7510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->stage == 0) 7520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else 7540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] >= 0x80; 7550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else { 7560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Handle failures by requeuing command, after 5ms interval 7600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 7610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (fail && --cmd->retries > 0) { 7620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, starting timer...\n"); 763730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt BUG_ON(cmd != smu->cmd_i2c_cur); 764f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt if (!smu_irq_inited) { 765f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt mdelay(5); 766f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt smu_i2c_retry(0); 767f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt return; 768f620753b9584558cd3bcc1712fca16663aacdce4Benjamin Herrenschmidt } 769730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5)); 7700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 7710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* If failure or stage 1, command is complete */ 7740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (fail || cmd->stage != 0) { 7750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_i2c_complete_command(cmd, fail); 7760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 7770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: going to stage 1\n"); 7800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Ok, initial command complete, now poll status */ 7820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->reply_buf = cmd->pdata; 783730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt scmd->reply_len = sizeof(cmd->pdata); 7840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->data_buf = cmd->pdata; 7850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->data_len = 1; 7860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0; 7870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage = 1; 7880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->retries = 20; 7890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_queue_cmd(scmd); 7900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_i2c(struct smu_i2c_cmd *cmd) 7940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 7950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 7960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 7980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 7990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fill most fields of scmd */ 8010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.cmd = SMU_CMD_I2C_COMMAND; 8020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.done = smu_i2c_low_completion; 8030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.misc = cmd; 8040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.reply_buf = cmd->pdata; 805730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 8060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_buf = (u8 *)(char *)&cmd->info; 8070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.status = 1; 8080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage = 0; 8090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 8100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->retries = 20; 8110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = 1; 8120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check transfer type, sanitize some "info" fields 8140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * based on transfer type and do more checking 8150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.caddr = cmd->info.devaddr; 8170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->read = cmd->info.devaddr & 0x01; 8180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt switch(cmd->info.type) { 8190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_SIMPLE: 8200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(&cmd->info.sublen, 0, 4); 8210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 8220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_COMBINED: 8230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.devaddr &= 0xfe; 8240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_STDSUB: 8250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.sublen > 3) 8260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 8280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt default: 8290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Finish setting up command based on transfer direction 8330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->read) { 8350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_READ_MAX) 8360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(cmd->info.data, 0xff, cmd->info.datalen); 8380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_len = 9; 8390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else { 8400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_WRITE_MAX) 8410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_len = 9 + cmd->info.datalen; 8430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c enqueuing command\n"); 8460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: %s, len=%d bus=%x addr=%x sub0=%x type=%x\n", 8470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->read ? "read" : "write", cmd->info.datalen, 8480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.bus, cmd->info.caddr, 8490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.subaddr[0], cmd->info.type); 8500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Enqueue command in i2c list, and if empty, enqueue also in 8530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * main command list 8540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 8560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_i2c_cur == NULL) { 8570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = cmd; 8580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 8590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 8600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 8610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else 8620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_i2c_list); 8630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 8640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 8660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 8670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 868183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt/* 869183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * Handling of "partitions" 870183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 871183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 872183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len) 873183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 8746e9a4738c9fadb7cbdcabc1e3b415159f3741ed9Peter Zijlstra DECLARE_COMPLETION_ONSTACK(comp); 875183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int chunk; 876183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_cmd cmd; 877183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int rc; 878183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt u8 params[8]; 879183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 880183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* We currently use a chunk size of 0xe. We could check the 881183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * SMU firmware version and use bigger sizes though 882183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 883183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt chunk = 0xe; 884183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 885183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt while (len) { 886183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int clen = min(len, chunk); 887183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 888183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.cmd = SMU_CMD_MISC_ee_COMMAND; 889183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.data_len = 7; 890183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.data_buf = params; 891183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_len = chunk; 892183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_buf = dest; 893183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.done = smu_done_complete; 894183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.misc = ∁ 895183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC; 896183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[1] = 0x4; 897183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt *((u32 *)¶ms[2]) = addr; 898183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[6] = clen; 899183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 900183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt rc = smu_queue_cmd(&cmd); 901183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (rc) 902183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return rc; 903183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt wait_for_completion(&comp); 904183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.status != 0) 905183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return rc; 906183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.reply_len != clen) { 907183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: short read in " 908183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "smu_read_datablock, got: %d, want: %d\n", 909183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_len, clen); 910183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return -EIO; 911183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 912183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt len -= clen; 913183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt addr += clen; 914183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt dest += clen; 915183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 916183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return 0; 917183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 918183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 919183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic struct smu_sdbp_header *smu_create_sdb_partition(int id) 920183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 9216e9a4738c9fadb7cbdcabc1e3b415159f3741ed9Peter Zijlstra DECLARE_COMPLETION_ONSTACK(comp); 922183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_simple_cmd cmd; 923183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int addr, len, tlen; 924183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_sdbp_header *hdr; 925183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct property *prop; 926183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 927183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* First query the partition info */ 9281beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq); 929183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, 930183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt smu_done_complete, &comp, 931183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt SMU_CMD_PARTITION_LATEST, id); 932183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt wait_for_completion(&comp); 9331beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("SMU: done, status: %d, reply_len: %d\n", 9341beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt cmd.cmd.status, cmd.cmd.reply_len); 935183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 936183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Partition doesn't exist (or other error) */ 937183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) 938183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 939183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 940183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Fetch address and length from reply */ 941183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt addr = *((u16 *)cmd.buffer); 942183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt len = cmd.buffer[3] << 2; 943183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Calucluate total length to allocate, including the 17 bytes 944183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * for "sdb-partition-XX" that we append at the end of the buffer 945183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 946183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt tlen = sizeof(struct property) + len + 18; 947183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 948cd86128088554d64fea1679191509f00e6353c5bRobert P. J. Day prop = kzalloc(tlen, GFP_KERNEL); 949183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (prop == NULL) 950183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 951183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt hdr = (struct smu_sdbp_header *)(prop + 1); 952183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->name = ((char *)prop) + tlen - 18; 953183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt sprintf(prop->name, "sdb-partition-%02x", id); 954183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->length = len; 9551a38147ed0737a9c01dbf5f2ca47fd2a0aa5cb55Stephen Rothwell prop->value = hdr; 956183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->next = NULL; 957183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 958183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Read the datablock */ 959183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (smu_read_datablock((u8 *)hdr, addr, len)) { 960183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: datablock read failed while reading " 961183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "partition %02x !\n", id); 962183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 963183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 964183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 965183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Got it, check a few things and create the property */ 966183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (hdr->id != id) { 967183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Reading partition %02x and got " 968183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "%02x !\n", id, hdr->id); 969183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 970183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 971183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (prom_add_property(smu->of_node, prop)) { 972183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " 973183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "property !\n", id); 974183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 975183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 976183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 977183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return hdr; 978183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt failure: 979183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt kfree(prop); 980183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 981183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 982183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 983183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt/* Note: Only allowed to return error code in pointers (using ERR_PTR) 984183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * when interruptible is 1 985183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 986018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerrconst struct smu_sdbp_header *__smu_get_sdb_partition(int id, 987018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr unsigned int *size, int interruptible) 9884350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt{ 9894350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt char pname[32]; 990018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr const struct smu_sdbp_header *part; 9914350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt 9924350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt if (!smu) 9934350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt return NULL; 9944350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt 9954350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt sprintf(pname, "sdb-partition-%02x", id); 996183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 9971beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("smu_get_sdb_partition(%02x)\n", id); 9981beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt 999183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (interruptible) { 1000183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int rc; 100114cc3e2b633bb64063698980974df4535368e98fIngo Molnar rc = mutex_lock_interruptible(&smu_part_access); 1002183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (rc) 1003183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return ERR_PTR(rc); 1004183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } else 100514cc3e2b633bb64063698980974df4535368e98fIngo Molnar mutex_lock(&smu_part_access); 1006183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 100701b2726dd11ef198ac6cf8f88974b4427d40ffdbStephen Rothwell part = of_get_property(smu->of_node, pname, size); 1008183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part == NULL) { 10091beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("trying to extract from SMU ...\n"); 1010183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = smu_create_sdb_partition(id); 1011183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part != NULL && size) 1012183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt *size = part->len << 2; 1013183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 101414cc3e2b633bb64063698980974df4535368e98fIngo Molnar mutex_unlock(&smu_part_access); 1015183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return part; 1016183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 1017183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 1018018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerrconst struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) 1019183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 1020183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return __smu_get_sdb_partition(id, size, 0); 10214350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt} 10224350147a816b9c5b40fa59e4fa23f17490630b79Benjamin HerrenschmidtEXPORT_SYMBOL(smu_get_sdb_partition); 10230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 10260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Userland driver interface 10270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 10280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic LIST_HEAD(smu_clist); 10310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic DEFINE_SPINLOCK(smu_clist_lock); 10320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtenum smu_file_mode { 10340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_commands, 10350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_events, 10360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_closing 10370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 10380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstruct smu_private 10400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head list; 10420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt enum smu_file_mode mode; 10430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int busy; 10440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd cmd; 10450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spinlock_t lock; 10460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wait_queue_head_t wait; 10470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 buffer[SMU_MAX_DATA]; 10480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 10490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_open(struct inode *inode, struct file *file) 10520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp; 10540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 10550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1056dd00cc486ab1c17049a535413d1751ef3482141cYoann Padioleau pp = kzalloc(sizeof(struct smu_private), GFP_KERNEL); 10570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 10580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENOMEM; 10590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_init(&pp->lock); 10600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_commands; 10610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt init_waitqueue_head(&pp->wait); 10620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 10640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add(&pp->list, &smu_clist); 10650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 10660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt file->private_data = pp; 10670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 10690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 10700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_user_cmd_done(struct smu_cmd *cmd, void *misc) 10730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = misc; 10750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wake_up_all(&pp->wait); 10770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 10780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_write(struct file *file, const char __user *buf, 10810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size_t count, loff_t *ppos) 10820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 10840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 10850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_user_cmd_hdr hdr; 10860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc = 0; 10870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy) 10890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBUSY; 10900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (copy_from_user(&hdr, buf, sizeof(hdr))) 10910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 10920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { 10930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_events; 10940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 1095183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { 1096018a3d1db7cdb6127656c1622ee1d2302e16436dJeremy Kerr const struct smu_sdbp_header *part; 1097183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); 1098183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part == NULL) 1099183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return -EINVAL; 1100183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt else if (IS_ERR(part)) 1101183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return PTR_ERR(part); 1102183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return 0; 11030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) 11040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 11050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (pp->mode != smu_file_commands) 11060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBADFD; 11070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (hdr.data_len > SMU_MAX_DATA) 11080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 11090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy) { 11120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBUSY; 11140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 1; 11160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.status = 1; 11170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (copy_from_user(pp->buffer, buf + sizeof(hdr), hdr.data_len)) { 11200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 0; 11210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.cmd = hdr.cmd; 11250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.data_len = hdr.data_len; 11260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_len = SMU_MAX_DATA; 11270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.data_buf = pp->buffer; 11280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_buf = pp->buffer; 11290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.done = smu_user_cmd_done; 11300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.misc = pp; 11310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_cmd(&pp->cmd); 11320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc < 0) 11330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return count; 11350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read_command(struct file *file, struct smu_private *pp, 11390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt char __user *buf, size_t count) 11400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 11420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_user_reply_hdr hdr; 11430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 11440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int size, rc = 0; 11450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!pp->busy) 11470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 11480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (count < sizeof(struct smu_user_reply_hdr)) 11490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EOVERFLOW; 11500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status == 1) { 11520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (file->f_flags & O_NONBLOCK) 11530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EAGAIN; 11540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 11550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (;;) { 11560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_INTERRUPTIBLE); 11570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = 0; 11580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 1) 11590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 11600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = -ERESTARTSYS; 11610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (signal_pending(current)) 11620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 11630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule(); 11650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 11680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 11690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 11720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 0) 11740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_len = 0; 11750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size = sizeof(hdr) + pp->cmd.reply_len; 11760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (count < size) 11770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size = count; 11780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = size; 11790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hdr.status = pp->cmd.status; 11800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hdr.reply_len = pp->cmd.reply_len; 11810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (copy_to_user(buf, &hdr, sizeof(hdr))) 11820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size -= sizeof(hdr); 11840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (size && copy_to_user(buf + sizeof(hdr), pp->buffer, size)) 11850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 0; 11870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read_events(struct file *file, struct smu_private *pp, 11930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt char __user *buf, size_t count) 11940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Not implemented */ 11960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt msleep_interruptible(1000); 11970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 11980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read(struct file *file, char __user *buf, 12020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size_t count, loff_t *ppos) 12030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_commands) 12070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_read_command(file, pp, buf, count); 12080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_events) 12090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_read_events(file, pp, buf, count); 12100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBADFD; 12120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic unsigned int smu_fpoll(struct file *file, poll_table *wait) 12150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int mask = 0; 12180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 12190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 12210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_commands) { 12240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt poll_wait(file, &pp->wait, wait); 12250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy && pp->cmd.status != 1) 12280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt mask |= POLLIN; 12290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } if (pp->mode == smu_file_events) { 12310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Not yet implemented */ 12320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return mask; 12340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_release(struct inode *inode, struct file *file) 12370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 12400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int busy; 12410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 12430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt file->private_data = NULL; 12460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Mark file as closing to avoid races with new request */ 12480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_closing; 12500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt busy = pp->busy; 12510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Wait for any pending request to complete */ 12530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (busy && pp->cmd.status == 1) { 12540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 12550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 12570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (;;) { 12580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_UNINTERRUPTIBLE); 12590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 1) 12600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 12610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 126294256dd680f837dc14dd7d1377c5326fb3362721Andrew Morton schedule(); 126394256dd680f837dc14dd7d1377c5326fb3362721Andrew Morton spin_lock_irqsave(&pp->lock, flags); 12640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 12660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 12670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 12710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&pp->list); 12720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 12730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt kfree(pp); 12740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1279fa027c2a0a0d6d1df6b29ee99048502c93da0dd4Arjan van de Venstatic const struct file_operations smu_device_fops = { 12800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .llseek = no_llseek, 12810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .read = smu_read, 12820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .write = smu_write, 12830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .poll = smu_fpoll, 12840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .open = smu_open, 12850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .release = smu_release, 12860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 12870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12886b67f62cf655c80147435544a2f8f6f57e07ec87Stephen Rothwellstatic struct miscdevice pmu_device = { 12890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt MISC_DYNAMIC_MINOR, "smu", &smu_device_fops 12900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 12910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_device_init(void) 12930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 12950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 12960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (misc_register(&pmu_device) < 0) 12970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_ERR "via-pmu: cannot register misc device.\n"); 12980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 13000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtdevice_initcall(smu_device_init); 1301