smu.c revision a28d3af2a26c89aaa6470ca36edb212e05143d67
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/config.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/dmapool.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bootmem.h> 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/highmem.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/jiffies.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/rtc.h> 330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/completion.h> 340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/miscdevice.h> 350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/delay.h> 360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/sysdev.h> 370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#include <linux/poll.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> 490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 50183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt#define VERSION "0.7" 510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#undef DEBUG_SMU 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_SMU 561beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0) 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DPRINTK(fmt, args...) do { } while (0) 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is the command buffer passed to the SMU hardware 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt#define SMU_MAX_DATA 254 650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct smu_cmd_buf { 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 cmd; 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 length; 690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 data[SMU_MAX_DATA]; 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct smu_device { 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spinlock_t lock; 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct device_node *of_node; 750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct of_device *of_dev; 760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int doorbell; /* doorbell gpio */ 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u32 __iomem *db_buf; /* doorbell buffer */ 780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int db_irq; 790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int msg; 800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int msg_irq; 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct smu_cmd_buf *cmd_buf; /* command buffer virtual */ 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u32 cmd_buf_abs; /* command buffer absolute */ 830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head cmd_list; 840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd_cur; /* pending command */ 850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head cmd_i2c_list; 860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */ 870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct timer_list i2c_timer; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * I don't think there will ever be more than one SMU, so 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for now, just hard code that 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct smu_device *smu; 95183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic DECLARE_MUTEX(smu_part_access); 960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 97730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidtstatic void smu_i2c_retry(unsigned long data); 98730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * SMU driver low level stuff 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_start_cmd(void) 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long faddr, fend; 1060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd; 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (list_empty(&smu->cmd_list)) 1090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 1100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fetch first command in queue */ 1120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link); 1130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_cur = cmd; 1140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&cmd->link); 1150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, 1170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_len); 118183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n", 1190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1], 120183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3], 121183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5], 122183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt ((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]); 1230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fill the SMU command buffer */ 1250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_buf->cmd = cmd->cmd; 1260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_buf->length = cmd->data_len; 1270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(smu->cmd_buf->data, cmd->data_buf, cmd->data_len); 1280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Flush command and data to RAM */ 1300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fend = faddr + smu->cmd_buf->length + 2; 1320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt flush_inval_dcache_range(faddr, fend); 1330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* This isn't exactly a DMA mapping here, I suspect 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the SMU is actually communicating with us via i2c to the 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * northbridge or the CPU to access RAM. 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt writel(smu->cmd_buf_abs, smu->db_buf); 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ring the SMU doorbell */ 1410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, smu->doorbell, 4); 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic irqreturn_t smu_db_intr(int irq, void *arg, struct pt_regs *regs) 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 1480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd; 1490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc) = NULL; 1500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc = NULL; 1510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 gpio; 1520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc = 0; 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* SMU completed the command, well, we hope, let's make sure 1550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * of it 1560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 1570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 160a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt if ((gpio & 7) != 7) { 161a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 1620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 163a44fe13eab664ac488ced0845d9885b019bd24c5Benjamin Herrenschmidt } 1640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd = smu->cmd_cur; 1660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_cur = NULL; 1670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd == NULL) 1680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt goto bail; 1690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc == 0) { 1710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long faddr; 1720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int reply_len; 1730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 ack; 1740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* CPU might have brought back the cache line, so we need 1760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * to flush again before peeking at the SMU response. We 1770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * flush the entire buffer for now as we haven't read the 1780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * reply lenght (it's only 2 cache lines anyway) 1790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 1800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt faddr = (unsigned long)smu->cmd_buf; 1810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt flush_inval_dcache_range(faddr, faddr + 256); 1820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 1830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now check ack */ 1840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ack = (~cmd->cmd) & 0xff; 1850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (ack != smu->cmd_buf->cmd) { 1860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: incorrect ack, want %x got %x\n", 1870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt ack, smu->cmd_buf->cmd); 1880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = -EIO; 1890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 1900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len = rc == 0 ? smu->cmd_buf->length : 0; 1910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: reply len: %d\n", reply_len); 1920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (reply_len > cmd->reply_len) { 1930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: reply buffer too small," 1940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "got %d bytes for a %d bytes buffer\n", 1950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len, cmd->reply_len); 1960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt reply_len = cmd->reply_len; 1970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 1980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len = reply_len; 1990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->reply_buf && reply_len) 2000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len); 2010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 2020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now complete the command. Write status last in order as we lost 2040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * ownership of the command structure as soon as it's no longer -1 2050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done = cmd->done; 2070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt misc = cmd->misc; 2080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt mb(); 2090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = rc; 2100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt bail: 2110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Start next command if any */ 2120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 2130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Call command completion handler if any */ 2160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (done) 2170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done(cmd, misc); 2180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic irqreturn_t smu_msg_intr(int irq, void *arg, struct pt_regs *regs) 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* I don't quite know what to do with this one, we seem to never 2270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * receive it, so I suspect we have to arm it someway in the SMU 2280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * to start getting events that way. 2290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_INFO "SMU: message interrupt !\n"); 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* It's an edge interrupt, nothing to do */ 2340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return IRQ_HANDLED; 2350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 2390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Queued command management. 2400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * 2410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_cmd(struct smu_cmd *cmd) 2440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 2450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 2480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 2490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->data_len > SMU_MAX_DATA || 2500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len > SMU_MAX_DATA) 2510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 2520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = 1; 2540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 2550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_list); 2560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 2570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 2580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 2590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_queue_cmd); 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_simple(struct smu_simple_cmd *scmd, u8 command, 2660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int data_len, 2670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_cmd *cmd, void *misc), 2680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc, ...) 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd *cmd = &scmd->cmd; 2710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_list list; 2720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int i; 2730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (data_len > sizeof(scmd->buffer)) 2750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 2760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(scmd, 0, sizeof(*scmd)); 2780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->cmd = command; 2790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_len = data_len; 2800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->data_buf = scmd->buffer; 2810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_len = sizeof(scmd->buffer); 2820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->reply_buf = scmd->buffer; 2830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->done = done; 2840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->misc = misc; 2850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_start(list, misc); 2870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (i = 0; i < data_len; ++i) 2880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->buffer[i] = (u8)va_arg(list, int); 2890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt va_end(list); 2900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_queue_cmd(cmd); 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_queue_simple); 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 2960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_poll(void) 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 gpio; 2990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 3010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 3020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell); 3040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if ((gpio & 7) == 7) 3050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_db_intr(smu->db_irq, smu, NULL); 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_poll); 3080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_done_complete(struct smu_cmd *cmd, void *misc) 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct completion *comp = misc; 3130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt complete(comp); 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_done_complete); 3170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtvoid smu_spinwait_cmd(struct smu_cmd *cmd) 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt while(cmd->status == 1) 3220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_poll(); 3230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 3240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_spinwait_cmd); 3250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* RTC low level commands */ 3280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic inline int bcd2hex (int n) 3290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 3300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return (((n & 0xf0) >> 4) * 10) + (n & 0xf); 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic inline int hex2bcd (int n) 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return ((n / 10) << 4) + (n % 10); 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf, 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct rtc_time *time) 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->cmd = 0x8e; 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->length = 8; 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[0] = 0x80; 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[1] = hex2bcd(time->tm_sec); 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[2] = hex2bcd(time->tm_min); 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[3] = hex2bcd(time->tm_hour); 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[4] = time->tm_wday; 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[5] = hex2bcd(time->tm_mday); 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1; 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_buf->data[7] = hex2bcd(time->tm_year - 100); 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_get_rtc_time(struct rtc_time *time, int spinwait) 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(time, 0, sizeof(struct rtc_time)); 3650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 1, NULL, NULL, 3660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SMU_CMD_RTC_GET_DATETIME); 3670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 3680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 3690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_sec = bcd2hex(cmd.buffer[0]); 3720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_min = bcd2hex(cmd.buffer[1]); 3730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_hour = bcd2hex(cmd.buffer[2]); 3740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_wday = bcd2hex(cmd.buffer[3]); 3750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_mday = bcd2hex(cmd.buffer[4]); 3760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_mon = bcd2hex(cmd.buffer[5]) - 1; 3770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_year = bcd2hex(cmd.buffer[6]) + 100; 3780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 3830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_set_rtc_time(struct rtc_time *time, int spinwait) 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int rc; 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 8, NULL, NULL, 3920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SMU_CMD_RTC_SET_DATETIME, 3930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_sec), 3940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_min), 3950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_hour), 3960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt time->tm_wday, 3970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_mday), 3980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_mon) + 1, 3990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hex2bcd(time->tm_year - 100)); 4000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 4010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 4020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid smu_shutdown(void) 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 9, NULL, NULL, 4160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 'S', 'H', 'U', 'T', 'D', 'O', 'W', 'N', 0)) 4170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 4180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid smu_restart(void) 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_simple_cmd cmd; 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, NULL, NULL, 4320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 'R', 'E', 'S', 'T', 'A', 'R', 'T', 0)) 4330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 4340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_spinwait_simple(&cmd); 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint smu_present(void) 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return smu != NULL; 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL(smu_present); 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 447183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtint __init smu_init (void) 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct device_node *np; 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u32 *data; 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds np = of_find_node_by_type(NULL, "smu"); 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (np == NULL) 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_INFO "SMU driver %s %s\n", VERSION, AUTHOR); 4570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu_cmdbuf_abs == 0) { 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Command buffer not allocated !\n"); 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu = alloc_bootmem(sizeof(struct smu_device)); 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu == NULL) 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(smu, 0, sizeof(*smu)); 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_init(&smu->lock); 4690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_list); 4700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt INIT_LIST_HEAD(&smu->cmd_i2c_list); 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->of_node = np; 4720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = NO_IRQ; 4730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = NO_IRQ; 4740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 32 bits value safely 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs); 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds np = of_find_node_by_name(NULL, "smu-doorbell"); 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (np == NULL) { 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto fail; 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds data = (u32 *)get_property(np, "reg", NULL); 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (data == NULL) { 4880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt of_node_put(np); 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n"); 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto fail; 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Current setup has one doorbell GPIO that does both doorbell 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and ack. GPIOs are at 0x50, best would be to find that out 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the device-tree though. 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->doorbell = *data; 4980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->doorbell < 0x50) 4990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->doorbell += 0x50; 5000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (np->n_intrs > 0) 5010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = np->intrs[0].line; 5020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt of_node_put(np); 5040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Now look for the smu-interrupt GPIO */ 5060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt do { 5070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt np = of_find_node_by_name(NULL, "smu-interrupt"); 5080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (np == NULL) 5090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 5100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt data = (u32 *)get_property(np, "reg", NULL); 5110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (data == NULL) { 5120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt of_node_put(np); 5130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 5140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg = *data; 5160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->msg < 0x50) 5170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg += 0x50; 5180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (np->n_intrs > 0) 5190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = np->intrs[0].line; 5200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt of_node_put(np); 5210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } while(0); 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Doorbell buffer is currently hard-coded, I didn't find a proper 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * device-tree entry giving the address. Best would probably to use 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * an offset for K2 base though, but let's do it that way for now. 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu->db_buf = ioremap(0x8000860c, 0x1000); 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (smu->db_buf == NULL) { 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n"); 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto fail; 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sys_ctrler = SYS_CTRLER_SMU; 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fail: 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds smu = NULL; 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENXIO; 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_late_init(void) 5440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 5450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 5460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 5470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 548730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt init_timer(&smu->i2c_timer); 549730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt smu->i2c_timer.function = smu_i2c_retry; 550730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt smu->i2c_timer.data = (unsigned long)smu; 551730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt 5520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 5530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Try to request the interrupts 5540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 5550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->db_irq != NO_IRQ) { 5570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (request_irq(smu->db_irq, smu_db_intr, 5580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SA_SHIRQ, "SMU doorbell", smu) < 0) { 5590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 5600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "request interrupt %d\n", 5610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq); 5620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->db_irq = NO_IRQ; 5630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->msg_irq != NO_IRQ) { 5670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (request_irq(smu->msg_irq, smu_msg_intr, 5680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt SA_SHIRQ, "SMU message", smu) < 0) { 5690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_WARNING "SMU: can't " 5700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt "request interrupt %d\n", 5710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq); 5720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->msg_irq = NO_IRQ; 5730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 5750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 5770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 578730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt/* This has to be before arch_initcall as the low i2c stuff relies on the 579730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt * above having been done before we reach arch_initcalls 580730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt */ 581730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidtcore_initcall(smu_late_init); 5820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 5840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * sysfs visibility 5850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 5860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_expose_childs(void *unused) 5880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 589a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt struct device_node *np; 590a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt 591a28d3af2a26c89aaa6470ca36edb212e05143d67Benjamin Herrenschmidt for (np = NULL; (np = of_get_next_child(smu->of_node, np)) != NULL;) 59275722d3992f57375c0cc029dcceb2334a45ceff1Benjamin Herrenschmidt if (device_is_compatible(np, "smu-sensors")) 593730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt of_platform_device_create(np, "smu-sensors", 594730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt &smu->of_dev->dev); 5950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 5960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic DECLARE_WORK(smu_expose_childs_work, smu_expose_childs, NULL); 5980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 5990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_platform_probe(struct of_device* dev, 6000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt const struct of_device_id *match) 6010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 6030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 6040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->of_dev = dev; 6050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 6070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Ok, we are matched, now expose all i2c busses. We have to defer 6080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * that unfortunately or it would deadlock inside the device model 6090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule_work(&smu_expose_childs_work); 6110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic struct of_device_id smu_platform_match[] = 6160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt { 6180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .type = "smu", 6190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt }, 6200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt {}, 6210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 6220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic struct of_platform_driver smu_of_platform_driver = 6240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .name = "smu", 6260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .match_table = smu_platform_match, 6270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .probe = smu_platform_probe, 6280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 6290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int __init smu_init_sysfs(void) 6310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc; 6330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* 6350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Due to sysfs bogosity, a sysdev is not a real device, so 6360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * we should in fact create both if we want sysdev semantics 6370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * for power management. 6380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * For now, we don't power manage machines with an SMU chip, 6390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * I'm a bit too far from figuring out how that works with those 6400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * new chipsets, but that will come back and bite us 6410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = of_register_driver(&smu_of_platform_driver); 6430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 6440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtdevice_initcall(smu_init_sysfs); 6470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstruct of_device *smu_get_ofdev(void) 6490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 6510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return NULL; 6520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu->of_dev; 6530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 6540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin HerrenschmidtEXPORT_SYMBOL_GPL(smu_get_ofdev); 6560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 6580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * i2c interface 6590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail) 6620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 6630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void (*done)(struct smu_i2c_cmd *cmd, void *misc) = cmd->done; 6640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt void *misc = cmd->misc; 6650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 6660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check for read case */ 6680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!fail && cmd->read) { 6690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->pdata[0] < 1) 6700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = 1; 6710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else 6720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memcpy(cmd->info.data, &cmd->pdata[1], 6730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.datalen); 6740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 6750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: completing, success: %d\n", !fail); 6770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Update status and mark no pending i2c command with lock 6790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * held so nobody comes in while we dequeue an eventual 6800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * pending next i2c command 6810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 6820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 6830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = NULL; 6840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wmb(); 6850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = fail ? -EIO : 0; 6860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Is there another i2c command waiting ? */ 6880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!list_empty(&smu->cmd_i2c_list)) { 6890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *newcmd; 6900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fetch it, new current, remove from list */ 6920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt newcmd = list_entry(smu->cmd_i2c_list.next, 6930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd, link); 6940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = newcmd; 6950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&cmd->link); 6960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 6970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Queue with low level smu */ 6980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 6990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 7000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 7010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 7030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Call command completion handler if any */ 7050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (done) 7060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt done(cmd, misc); 7070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_retry(unsigned long data) 7120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 713730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt struct smu_i2c_cmd *cmd = smu->cmd_i2c_cur; 7140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, requeuing...\n"); 7160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* requeue command simply by resetting reply_len */ 7180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 719730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 7200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_queue_cmd(&cmd->scmd); 7210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc) 7250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 7260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_i2c_cmd *cmd = misc; 7270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int fail = 0; 7280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c compl. stage=%d status=%x pdata[0]=%x rlen: %x\n", 7300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage, scmd->status, cmd->pdata[0], scmd->reply_len); 7310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check for possible status */ 7330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (scmd->status < 0) 7340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = 1; 7350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (cmd->read) { 7360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->stage == 0) 7370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else 7390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] >= 0x80; 7400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else { 7410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt fail = cmd->pdata[0] != 0; 7420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Handle failures by requeuing command, after 5ms interval 7450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 7460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (fail && --cmd->retries > 0) { 7470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c failure, starting timer...\n"); 748730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt BUG_ON(cmd != smu->cmd_i2c_cur); 749730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5)); 7500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 7510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* If failure or stage 1, command is complete */ 7540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (fail || cmd->stage != 0) { 7550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_i2c_complete_command(cmd, fail); 7560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return; 7570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 7580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: going to stage 1\n"); 7600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Ok, initial command complete, now poll status */ 7620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->reply_buf = cmd->pdata; 763730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt scmd->reply_len = sizeof(cmd->pdata); 7640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->data_buf = cmd->pdata; 7650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt scmd->data_len = 1; 7660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0; 7670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage = 1; 7680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->retries = 20; 7690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_queue_cmd(scmd); 7700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 7710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtint smu_queue_i2c(struct smu_i2c_cmd *cmd) 7740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 7750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 7760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu == NULL) 7780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 7790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Fill most fields of scmd */ 7810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.cmd = SMU_CMD_I2C_COMMAND; 7820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.done = smu_i2c_low_completion; 7830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.misc = cmd; 7840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.reply_buf = cmd->pdata; 785730745a5c45093982112ddc94cee6a9973455641Benjamin Herrenschmidt cmd->scmd.reply_len = sizeof(cmd->pdata); 7860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_buf = (u8 *)(char *)&cmd->info; 7870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.status = 1; 7880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->stage = 0; 7890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->pdata[0] = 0xff; 7900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->retries = 20; 7910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->status = 1; 7920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 7930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Check transfer type, sanitize some "info" fields 7940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * based on transfer type and do more checking 7950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 7960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.caddr = cmd->info.devaddr; 7970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->read = cmd->info.devaddr & 0x01; 7980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt switch(cmd->info.type) { 7990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_SIMPLE: 8000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(&cmd->info.sublen, 0, 4); 8010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 8020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_COMBINED: 8030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.devaddr &= 0xfe; 8040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt case SMU_I2C_TRANSFER_STDSUB: 8050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.sublen > 3) 8060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 8080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt default: 8090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Finish setting up command based on transfer direction 8130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->read) { 8150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_READ_MAX) 8160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(cmd->info.data, 0xff, cmd->info.datalen); 8180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_len = 9; 8190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else { 8200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (cmd->info.datalen > SMU_I2C_WRITE_MAX) 8210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 8220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->scmd.data_len = 9 + cmd->info.datalen; 8230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 8240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: i2c enqueuing command\n"); 8260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DPRINTK("SMU: %s, len=%d bus=%x addr=%x sub0=%x type=%x\n", 8270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->read ? "read" : "write", cmd->info.datalen, 8280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.bus, cmd->info.caddr, 8290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt cmd->info.subaddr[0], cmd->info.type); 8300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Enqueue command in i2c list, and if empty, enqueue also in 8330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * main command list 8340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 8350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu->lock, flags); 8360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_i2c_cur == NULL) { 8370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu->cmd_i2c_cur = cmd; 8380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->scmd.link, &smu->cmd_list); 8390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (smu->cmd_cur == NULL) 8400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_start_cmd(); 8410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else 8420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add_tail(&cmd->link, &smu->cmd_i2c_list); 8430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu->lock, flags); 8440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 8450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 8460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 8470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 848183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt/* 849183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * Handling of "partitions" 850183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 851183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 852183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len) 853183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 854183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt DECLARE_COMPLETION(comp); 855183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int chunk; 856183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_cmd cmd; 857183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int rc; 858183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt u8 params[8]; 859183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 860183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* We currently use a chunk size of 0xe. We could check the 861183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * SMU firmware version and use bigger sizes though 862183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 863183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt chunk = 0xe; 864183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 865183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt while (len) { 866183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int clen = min(len, chunk); 867183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 868183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.cmd = SMU_CMD_MISC_ee_COMMAND; 869183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.data_len = 7; 870183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.data_buf = params; 871183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_len = chunk; 872183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_buf = dest; 873183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.done = smu_done_complete; 874183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.misc = ∁ 875183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC; 876183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[1] = 0x4; 877183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt *((u32 *)¶ms[2]) = addr; 878183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt params[6] = clen; 879183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 880183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt rc = smu_queue_cmd(&cmd); 881183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (rc) 882183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return rc; 883183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt wait_for_completion(&comp); 884183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.status != 0) 885183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return rc; 886183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.reply_len != clen) { 887183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: short read in " 888183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "smu_read_datablock, got: %d, want: %d\n", 889183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt cmd.reply_len, clen); 890183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return -EIO; 891183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 892183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt len -= clen; 893183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt addr += clen; 894183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt dest += clen; 895183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 896183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return 0; 897183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 898183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 899183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstatic struct smu_sdbp_header *smu_create_sdb_partition(int id) 900183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 901183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt DECLARE_COMPLETION(comp); 902183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_simple_cmd cmd; 903183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt unsigned int addr, len, tlen; 904183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_sdbp_header *hdr; 905183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct property *prop; 906183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 907183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* First query the partition info */ 9081beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq); 909183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2, 910183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt smu_done_complete, &comp, 911183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt SMU_CMD_PARTITION_LATEST, id); 912183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt wait_for_completion(&comp); 9131beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("SMU: done, status: %d, reply_len: %d\n", 9141beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt cmd.cmd.status, cmd.cmd.reply_len); 915183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 916183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Partition doesn't exist (or other error) */ 917183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6) 918183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 919183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 920183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Fetch address and length from reply */ 921183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt addr = *((u16 *)cmd.buffer); 922183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt len = cmd.buffer[3] << 2; 923183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Calucluate total length to allocate, including the 17 bytes 924183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * for "sdb-partition-XX" that we append at the end of the buffer 925183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 926183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt tlen = sizeof(struct property) + len + 18; 927183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 928183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop = kcalloc(tlen, 1, GFP_KERNEL); 929183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (prop == NULL) 930183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 931183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt hdr = (struct smu_sdbp_header *)(prop + 1); 932183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->name = ((char *)prop) + tlen - 18; 933183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt sprintf(prop->name, "sdb-partition-%02x", id); 934183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->length = len; 935183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->value = (unsigned char *)hdr; 936183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt prop->next = NULL; 937183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 938183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Read the datablock */ 939183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (smu_read_datablock((u8 *)hdr, addr, len)) { 940183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: datablock read failed while reading " 941183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "partition %02x !\n", id); 942183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 943183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 944183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 945183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt /* Got it, check a few things and create the property */ 946183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (hdr->id != id) { 947183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Reading partition %02x and got " 948183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "%02x !\n", id, hdr->id); 949183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 950183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 951183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (prom_add_property(smu->of_node, prop)) { 952183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " 953183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt "property !\n", id); 954183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt goto failure; 955183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 956183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 957183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return hdr; 958183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt failure: 959183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt kfree(prop); 960183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return NULL; 961183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 962183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 963183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt/* Note: Only allowed to return error code in pointers (using ERR_PTR) 964183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt * when interruptible is 1 965183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt */ 966183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstruct smu_sdbp_header *__smu_get_sdb_partition(int id, unsigned int *size, 967183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int interruptible) 9684350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt{ 9694350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt char pname[32]; 970183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_sdbp_header *part; 9714350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt 9724350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt if (!smu) 9734350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt return NULL; 9744350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt 9754350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt sprintf(pname, "sdb-partition-%02x", id); 976183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 9771beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("smu_get_sdb_partition(%02x)\n", id); 9781beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt 979183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (interruptible) { 980183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt int rc; 981183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt rc = down_interruptible(&smu_part_access); 982183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (rc) 983183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return ERR_PTR(rc); 984183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } else 985183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt down(&smu_part_access); 986183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 987183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = (struct smu_sdbp_header *)get_property(smu->of_node, 9884350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt pname, size); 989183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part == NULL) { 9901beb6a7d6cbed3ac03500ce9b5b9bb632c512039Benjamin Herrenschmidt DPRINTK("trying to extract from SMU ...\n"); 991183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = smu_create_sdb_partition(id); 992183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part != NULL && size) 993183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt *size = part->len << 2; 994183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } 995183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt up(&smu_part_access); 996183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return part; 997183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt} 998183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt 999183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidtstruct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) 1000183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt{ 1001183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return __smu_get_sdb_partition(id, size, 0); 10024350147a816b9c5b40fa59e4fa23f17490630b79Benjamin Herrenschmidt} 10034350147a816b9c5b40fa59e4fa23f17490630b79Benjamin HerrenschmidtEXPORT_SYMBOL(smu_get_sdb_partition); 10040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt/* 10070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt * Userland driver interface 10080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt */ 10090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic LIST_HEAD(smu_clist); 10120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic DEFINE_SPINLOCK(smu_clist_lock); 10130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtenum smu_file_mode { 10150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_commands, 10160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_events, 10170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt smu_file_closing 10180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 10190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstruct smu_private 10210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct list_head list; 10230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt enum smu_file_mode mode; 10240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int busy; 10250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_cmd cmd; 10260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spinlock_t lock; 10270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wait_queue_head_t wait; 10280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt u8 buffer[SMU_MAX_DATA]; 10290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 10300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_open(struct inode *inode, struct file *file) 10330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp; 10350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 10360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp = kmalloc(sizeof(struct smu_private), GFP_KERNEL); 10380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 10390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENOMEM; 10400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt memset(pp, 0, sizeof(struct smu_private)); 10410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_init(&pp->lock); 10420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_commands; 10430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt init_waitqueue_head(&pp->wait); 10440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 10460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_add(&pp->list, &smu_clist); 10470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 10480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt file->private_data = pp; 10490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 10510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 10520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic void smu_user_cmd_done(struct smu_cmd *cmd, void *misc) 10550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = misc; 10570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt wake_up_all(&pp->wait); 10590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 10600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_write(struct file *file, const char __user *buf, 10630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size_t count, loff_t *ppos) 10640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 10650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 10660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 10670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_user_cmd_hdr hdr; 10680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int rc = 0; 10690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy) 10710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBUSY; 10720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (copy_from_user(&hdr, buf, sizeof(hdr))) 10730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 10740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) { 10750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_events; 10760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 1077183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt } else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) { 1078183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt struct smu_sdbp_header *part; 1079183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt part = __smu_get_sdb_partition(hdr.cmd, NULL, 1); 1080183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt if (part == NULL) 1081183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return -EINVAL; 1082183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt else if (IS_ERR(part)) 1083183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return PTR_ERR(part); 1084183d020258dfd08178a05c6793dae10409db8abbBenjamin Herrenschmidt return 0; 10850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } else if (hdr.cmdtype != SMU_CMDTYPE_SMU) 10860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 10870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (pp->mode != smu_file_commands) 10880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBADFD; 10890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt else if (hdr.data_len > SMU_MAX_DATA) 10900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EINVAL; 10910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 10920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 10930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy) { 10940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 10950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBUSY; 10960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 10970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 1; 10980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.status = 1; 10990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (copy_from_user(pp->buffer, buf + sizeof(hdr), hdr.data_len)) { 11020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 0; 11030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.cmd = hdr.cmd; 11070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.data_len = hdr.data_len; 11080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_len = SMU_MAX_DATA; 11090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.data_buf = pp->buffer; 11100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_buf = pp->buffer; 11110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.done = smu_user_cmd_done; 11120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.misc = pp; 11130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = smu_queue_cmd(&pp->cmd); 11140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc < 0) 11150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return count; 11170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read_command(struct file *file, struct smu_private *pp, 11210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt char __user *buf, size_t count) 11220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 11240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_user_reply_hdr hdr; 11250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 11260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt int size, rc = 0; 11270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!pp->busy) 11290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 11300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (count < sizeof(struct smu_user_reply_hdr)) 11310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EOVERFLOW; 11320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status == 1) { 11340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (file->f_flags & O_NONBLOCK) 11350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EAGAIN; 11360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 11370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (;;) { 11380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_INTERRUPTIBLE); 11390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = 0; 11400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 1) 11410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 11420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = -ERESTARTSYS; 11430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (signal_pending(current)) 11440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 11450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule(); 11470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 11480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 11500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 11510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 11520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 11530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (rc) 11540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 0) 11560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->cmd.reply_len = 0; 11570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size = sizeof(hdr) + pp->cmd.reply_len; 11580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (count < size) 11590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size = count; 11600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt rc = size; 11610365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hdr.status = pp->cmd.status; 11620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt hdr.reply_len = pp->cmd.reply_len; 11630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (copy_to_user(buf, &hdr, sizeof(hdr))) 11640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size -= sizeof(hdr); 11660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (size && copy_to_user(buf + sizeof(hdr), pp->buffer, size)) 11670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EFAULT; 11680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->busy = 0; 11690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11700365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return rc; 11710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read_events(struct file *file, struct smu_private *pp, 11750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt char __user *buf, size_t count) 11760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Not implemented */ 11780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt msleep_interruptible(1000); 11790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 11800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11830365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic ssize_t smu_read(struct file *file, char __user *buf, 11840365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt size_t count, loff_t *ppos) 11850365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11860365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 11870365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11880365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_commands) 11890365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_read_command(file, pp, buf, count); 11900365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_events) 11910365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return smu_read_events(file, pp, buf, count); 11920365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11930365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -EBADFD; 11940365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 11950365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 11960365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic unsigned int smu_fpoll(struct file *file, poll_table *wait) 11970365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 11980365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 11990365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int mask = 0; 12000365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 12010365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12020365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 12030365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12040365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12050365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->mode == smu_file_commands) { 12060365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt poll_wait(file, &pp->wait, wait); 12070365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12080365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12090365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->busy && pp->cmd.status != 1) 12100365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt mask |= POLLIN; 12110365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12120365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } if (pp->mode == smu_file_events) { 12130365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Not yet implemented */ 12140365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12150365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return mask; 12160365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12170365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12180365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_release(struct inode *inode, struct file *file) 12190365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12200365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt struct smu_private *pp = file->private_data; 12210365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned long flags; 12220365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt unsigned int busy; 12230365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12240365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp == 0) 12250365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12260365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12270365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt file->private_data = NULL; 12280365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12290365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Mark file as closing to avoid races with new request */ 12300365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12310365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt pp->mode = smu_file_closing; 12320365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt busy = pp->busy; 12330365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12340365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt /* Wait for any pending request to complete */ 12350365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (busy && pp->cmd.status == 1) { 12360365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt DECLARE_WAITQUEUE(wait, current); 12370365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12380365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt add_wait_queue(&pp->wait, &wait); 12390365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt for (;;) { 12400365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_UNINTERRUPTIBLE); 12410365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (pp->cmd.status != 1) 12420365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt break; 12430365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&pp->lock, flags); 12440365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt schedule(); 12450365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12460365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12470365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt set_current_state(TASK_RUNNING); 12480365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt remove_wait_queue(&pp->wait, &wait); 12490365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt } 12500365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&pp->lock, flags); 12510365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12520365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_lock_irqsave(&smu_clist_lock, flags); 12530365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt list_del(&pp->list); 12540365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt spin_unlock_irqrestore(&smu_clist_lock, flags); 12550365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt kfree(pp); 12560365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12570365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12580365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12590365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12600365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12616b67f62cf655c80147435544a2f8f6f57e07ec87Stephen Rothwellstatic struct file_operations smu_device_fops = { 12620365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .llseek = no_llseek, 12630365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .read = smu_read, 12640365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .write = smu_write, 12650365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .poll = smu_fpoll, 12660365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .open = smu_open, 12670365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt .release = smu_release, 12680365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 12690365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12706b67f62cf655c80147435544a2f8f6f57e07ec87Stephen Rothwellstatic struct miscdevice pmu_device = { 12710365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt MISC_DYNAMIC_MINOR, "smu", &smu_device_fops 12720365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt}; 12730365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt 12740365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtstatic int smu_device_init(void) 12750365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt{ 12760365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (!smu) 12770365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return -ENODEV; 12780365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt if (misc_register(&pmu_device) < 0) 12790365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt printk(KERN_ERR "via-pmu: cannot register misc device.\n"); 12800365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt return 0; 12810365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidt} 12820365ba7fb1fa94a41289d6a3d36b4d95960e56ccBenjamin Herrenschmidtdevice_initcall(smu_device_init); 1283