From d06e684f30b52dd35e6cd42c83317561bcd35a0c Mon Sep 17 00:00:00 2001 From: Shijie Zhang Date: Mon, 19 Mar 2012 21:18:38 +0800 Subject: [PATCH] intel_mid_pmic: update pmic driver BZ: 27883 This driver separates the interface for accessing PMIC device register function into a independent PMIC driver, making it more clear in logic. Change-Id: Ib19f6d14911f6f7e8dbcd79d1d8dcc44cadc223e Signed-off-by: Shijie Zhang Reviewed-on: http://android.intel.com:8080/39500 Reviewed-by: Du, Alek Tested-by: Wang, Zhifeng Reviewed-by: buildbot Tested-by: buildbot --- arch/x86/include/asm/intel_scu_ipc.h | 41 +-- arch/x86/include/asm/intel_scu_pmic.h | 11 + arch/x86/platform/intel-mid/intel-mid.c | 22 ++ drivers/platform/x86/Makefile | 2 +- drivers/platform/x86/intel_scu_ipc.c | 483 +------------------------------- drivers/platform/x86/intel_scu_pmic.c | 428 ++++++++++++++++++++++++++++ 6 files changed, 468 insertions(+), 519 deletions(-) create mode 100644 arch/x86/include/asm/intel_scu_pmic.h create mode 100644 drivers/platform/x86/intel_scu_pmic.c diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h index a8d0776..de1bf0a 100644 --- a/arch/x86/include/asm/intel_scu_ipc.h +++ b/arch/x86/include/asm/intel_scu_ipc.h @@ -3,6 +3,7 @@ #include #include +#include /* IPC defines the following message types */ #define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */ @@ -15,18 +16,12 @@ #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ #define IPCMSG_VRTC 0xFA /* Set vRTC device */ #define IPCMSG_FW_UPDATE 0xFE /* Firmware update */ -#define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */ #define IPCMSG_OSC_CLK 0xE6 /* Turn on/off osc clock */ #define IPC_CMD_UMIP_RD 0 #define IPC_CMD_UMIP_WR 1 #define IPC_CMD_SMIP_RD 2 -/* Command id associated with message IPCMSG_PCNTRL */ -#define IPC_CMD_PCNTRL_W 0 /* Register write */ -#define IPC_CMD_PCNTRL_R 1 /* Register read */ -#define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ - #define IPC_ERR_NONE 0 #define IPC_ERR_CMD_NOT_SUPPORTED 1 #define IPC_ERR_CMD_NOT_SERVICED 2 @@ -35,44 +30,10 @@ #define IPC_ERR_CMD_FAILED 5 #define IPC_ERR_EMSECURITY 6 -#define MSIC_DEBUG_FILE "msic" -#define MSIC_ALL_DEBUG_FILE "msic_all" -#define MAX_MSIC_REG 0x3FF -#define MIN_MSIC_REG 0x0 - - - /* Command id associated with message IPCMSG_VRTC */ #define IPC_CMD_VRTC_SETTIME 1 /* Set time */ #define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */ #define IPC_CMD_VRTC_SYNC_RTC 3 /* Sync MSIC/PMIC RTC to VRTC */ -/* Read single register */ -int intel_scu_ipc_ioread8(u16 addr, u8 *data); - -/* Read two sequential registers */ -int intel_scu_ipc_ioread16(u16 addr, u16 *data); - -/* Read four sequential registers */ -int intel_scu_ipc_ioread32(u16 addr, u32 *data); - -/* Read a vector */ -int intel_scu_ipc_readv(u16 *addr, u8 *data, int len); - -/* Write single register */ -int intel_scu_ipc_iowrite8(u16 addr, u8 data); - -/* Write two sequential registers */ -int intel_scu_ipc_iowrite16(u16 addr, u16 data); - -/* Write four sequential registers */ -int intel_scu_ipc_iowrite32(u16 addr, u32 data); - -/* Write a vector */ -int intel_scu_ipc_writev(u16 *addr, u8 *data, int len); - -/* Update single register based on the mask */ -int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); - /* Issue commands to the SCU with or without data */ int intel_scu_ipc_simple_command(int cmd, int sub); int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, diff --git a/arch/x86/include/asm/intel_scu_pmic.h b/arch/x86/include/asm/intel_scu_pmic.h new file mode 100644 index 0000000..5db98df --- /dev/null +++ b/arch/x86/include/asm/intel_scu_pmic.h @@ -0,0 +1,11 @@ +#ifndef __INTEL_SCU_PMIC_H__ +#define __INTEL_SCU_PMIC_H__ + +int intel_scu_ipc_ioread8(u16 addr, u8 *data); +int intel_scu_ipc_ioread32(u16 addr, u32 *data); +int intel_scu_ipc_readv(u16 *addr, u8 *data, int len); +int intel_scu_ipc_iowrite8(u16 addr, u8 data); +int intel_scu_ipc_writev(u16 *addr, u8 *data, int len); +int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); + +#endif /*__INTEL_SCU_PMIC_H__ */ diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c index ef96d94..4d36817 100644 --- a/arch/x86/platform/intel-mid/intel-mid.c +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -549,6 +549,28 @@ static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, ipc_device_add_to_list(ipcdev); } +#ifdef CONFIG_INTEL_SCU_IPC +static int __init intel_scu_pmic_init(void) +{ + int ret; + struct ipc_board_info board_info; + + memset(&board_info, 0, sizeof(board_info)); + strncpy(board_info.name, "intel_scu_pmic", 16); + board_info.bus_id = IPC_SCU; + + ret = ipc_new_device(&board_info); + if (ret) { + pr_err("failed to create ipc device: intel_scu_pmic\n"); + return -EINVAL; + } + + return 0; +} +/* Ensure that it's created as the first ipc device in the ipc_device_list */ +postcore_initcall(intel_scu_pmic_init); +#endif + static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, struct devs_id *dev) { diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 888274a..23f37dc 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -31,7 +31,7 @@ obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o -obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o +obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o intel_scu_pmic.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 2749a08..3f2e9b3 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -55,8 +54,6 @@ #define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ #define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ -#define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ -#define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ #define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ #define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ @@ -66,7 +63,6 @@ #define IPC_MIP_MAX_ADDR 0x1000 #define IPC_IOC 0x100 -#define DATA_STRING_LEN 20 static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); static void ipc_remove(struct pci_dev *pdev); @@ -216,240 +212,6 @@ static inline int ipc_wait_interrupt(void) return ret; } -/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ -static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) -{ - int i, nc, bytes, d; - u32 offset = 0; - int err; - u8 cbuf[IPC_WWBUF_SIZE] = { }; - u32 *wbuf = (u32 *)&cbuf; - - mutex_lock(&ipclock); - - memset(cbuf, 0, sizeof(cbuf)); - - if (ipcdev.pdev == NULL) { - mutex_unlock(&ipclock); - return -ENODEV; - } - - if (platform == INTEL_MID_CPU_CHIP_LINCROFT) { - bytes = 0; - d = 0; - for (i = 0; i < count; i++) { - cbuf[bytes++] = addr[i]; - cbuf[bytes++] = addr[i] >> 8; - if (id != IPC_CMD_PCNTRL_R) - cbuf[bytes++] = data[d++]; - if (id == IPC_CMD_PCNTRL_M) - cbuf[bytes++] = data[d++]; - } - for (i = 0; i < bytes; i += 4) - ipc_data_writel(wbuf[i/4], i); - ipc_command(bytes << 16 | id << 12 | 0 << 8 | op); - } else { /* Penwell or Cloverview */ - for (nc = 0; nc < count; nc++, offset += 2) { - cbuf[offset] = addr[nc]; - cbuf[offset + 1] = addr[nc] >> 8; - } - - if (id == IPC_CMD_PCNTRL_R) { - for (nc = 0, offset = 0; nc < count; nc++, offset += 4) - ipc_data_writel(wbuf[nc], offset); - ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); - } else if (id == IPC_CMD_PCNTRL_W) { - for (nc = 0; nc < count; nc++, offset += 1) - cbuf[offset] = data[nc]; - for (nc = 0, offset = 0; nc < count; nc++, offset += 4) - ipc_data_writel(wbuf[nc], offset); - ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); - } else if (id == IPC_CMD_PCNTRL_M) { - cbuf[offset] = data[0]; - cbuf[offset + 1] = data[1]; - ipc_data_writel(wbuf[0], 0); /* Write wbuff */ - ipc_command(4 << 16 | id << 12 | 0 << 8 | op); - } - } - - err = ipc_wait_interrupt(); - if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ - /* Workaround: values are read as 0 without memcpy_fromio */ - memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); - if (platform == INTEL_MID_CPU_CHIP_LINCROFT) { - for (nc = 0, offset = 2; nc < count; nc++, offset += 3) - data[nc] = ipc_data_readb(offset); - } else { - for (nc = 0; nc < count; nc++) - data[nc] = ipc_data_readb(nc); - } - } - mutex_unlock(&ipclock); - return err; -} - -/** - * intel_scu_ipc_ioread8 - read a word via the SCU - * @addr: register on SCU - * @data: return pointer for read byte - * - * Read a single register. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * This function may sleep. - */ -int intel_scu_ipc_ioread8(u16 addr, u8 *data) -{ - return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); -} -EXPORT_SYMBOL(intel_scu_ipc_ioread8); - -/** - * intel_scu_ipc_ioread16 - read a word via the SCU - * @addr: register on SCU - * @data: return pointer for read word - * - * Read a register pair. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * This function may sleep. - */ -int intel_scu_ipc_ioread16(u16 addr, u16 *data) -{ - u16 x[2] = {addr, addr + 1 }; - return pwr_reg_rdwr(x, (u8 *)data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); -} -EXPORT_SYMBOL(intel_scu_ipc_ioread16); - -/** - * intel_scu_ipc_ioread32 - read a dword via the SCU - * @addr: register on SCU - * @data: return pointer for read dword - * - * Read four registers. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * This function may sleep. - */ -int intel_scu_ipc_ioread32(u16 addr, u32 *data) -{ - u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; - return pwr_reg_rdwr(x, (u8 *)data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); -} -EXPORT_SYMBOL(intel_scu_ipc_ioread32); - -/** - * intel_scu_ipc_iowrite8 - write a byte via the SCU - * @addr: register on SCU - * @data: byte to write - * - * Write a single register. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * This function may sleep. - */ -int intel_scu_ipc_iowrite8(u16 addr, u8 data) -{ - return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); -} -EXPORT_SYMBOL(intel_scu_ipc_iowrite8); - -/** - * intel_scu_ipc_iowrite16 - write a word via the SCU - * @addr: register on SCU - * @data: word to write - * - * Write two registers. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * This function may sleep. - */ -int intel_scu_ipc_iowrite16(u16 addr, u16 data) -{ - u16 x[2] = {addr, addr + 1 }; - return pwr_reg_rdwr(x, (u8 *)&data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); -} -EXPORT_SYMBOL(intel_scu_ipc_iowrite16); - -/** - * intel_scu_ipc_iowrite32 - write a dword via the SCU - * @addr: register on SCU - * @data: dword to write - * - * Write four registers. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * This function may sleep. - */ -int intel_scu_ipc_iowrite32(u16 addr, u32 data) -{ - u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; - return pwr_reg_rdwr(x, (u8 *)&data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); -} -EXPORT_SYMBOL(intel_scu_ipc_iowrite32); - -/** - * intel_scu_ipc_readvv - read a set of registers - * @addr: register list - * @data: bytes to return - * @len: length of array - * - * Read registers. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * The largest array length permitted by the hardware is 5 items. - * - * This function may sleep. - */ -int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) -{ - return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); -} -EXPORT_SYMBOL(intel_scu_ipc_readv); - -/** - * intel_scu_ipc_writev - write a set of registers - * @addr: register list - * @data: bytes to write - * @len: length of array - * - * Write registers. Returns 0 on success or an error code. All - * locking between SCU accesses is handled for the caller. - * - * The largest array length permitted by the hardware is 5 items. - * - * This function may sleep. - * - */ -int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) -{ - return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); -} -EXPORT_SYMBOL(intel_scu_ipc_writev); - - -/** - * intel_scu_ipc_update_register - r/m/w a register - * @addr: register address - * @bits: bits to update - * @mask: mask of bits to update - * - * Read-modify-write power control unit register. The first data argument - * must be register value and second is mask value - * mask is a bitmap that indicates which bits to update. - * 0 = masked. Don't modify this bit, 1 = modify this bit. - * returns 0 on success or an error code. - * - * This function may sleep. Locking between SCU accesses is handled - * for the caller. - */ -int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) -{ - u8 data[2] = { bits, mask }; - return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); -} -EXPORT_SYMBOL(intel_scu_ipc_update_register); - /** * intel_scu_ipc_simple_command - send a simple command * @cmd: command @@ -1484,15 +1246,12 @@ EXPORT_SYMBOL(intel_scu_ipc_write_umip); #define IPCMSG_FW_REVISION 0xF4 -static u16 msic_sysfs_reg_addr; -static char err_buf[50]; +#define ERR_BUF_SIZE 50 +static char err_buf[ERR_BUF_SIZE]; -static void cur_err(const char *err_str) +static void cur_err(const char *err_info) { - if (err_str) { - memset(err_buf, 0, sizeof(err_buf)); - strncpy(err_buf, err_str, sizeof(err_buf)-1); - } + strncpy(err_buf, err_info, ERR_BUF_SIZE - 1); } static ssize_t write_dnx(struct file *file, struct kobject *kobj, @@ -1746,77 +1505,6 @@ end: return sprintf(buf, "%s", "Error in fwud prepare"); } - -static ssize_t intel_scu_ipc_show_addr(struct device *dev, - struct device_attribute *attr, char *buf) -{ - if (msic_sysfs_reg_addr < MIN_MSIC_REG || - msic_sysfs_reg_addr > MAX_MSIC_REG) - return sprintf(buf, "%s", "invalid reg addr"); - else - return sprintf(buf, "0x%x", msic_sysfs_reg_addr); -} - - -static ssize_t intel_scu_ipc_store_addr(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - u32 reg_addr; - if (sscanf(buf, "%x", ®_addr) == 1) { - if (reg_addr < MIN_MSIC_REG || reg_addr > MAX_MSIC_REG) { - cur_err("MSIC reg addr out of range"); - return -EINVAL; - } - msic_sysfs_reg_addr = (u16)reg_addr; - dev_info(&ipcdev.pdev->dev, - "Set MSIC reg addr = 0x%x\n", msic_sysfs_reg_addr); - } - - return size; -} - -static ssize_t intel_scu_ipc_show_value(struct device *dev, - struct device_attribute *attr, char *buf) -{ - uint8_t data; - int ret; - - ret = intel_scu_ipc_ioread8(msic_sysfs_reg_addr, &data); - if (ret) { - cur_err("Error read msic register"); - return -EFAULT; - } - - return sprintf(buf, "msic[%3.3x]=0x%x\n", msic_sysfs_reg_addr, data); -} - -static ssize_t intel_scu_ipc_store_value(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - u32 val; - u8 data; - int ret; - - if (sscanf(buf, "%x", &val) == 1) { - if (val > 0xFF) { - cur_err("Write data out of range"); - return -EINVAL; - } - data = (u8)val; - dev_info(&ipcdev.pdev->dev, - "Write 0x%x to MSIC reg addr = 0x%x\n", - data, msic_sysfs_reg_addr); - - ret = intel_scu_ipc_iowrite8(msic_sysfs_reg_addr, data); - if (ret) { - cur_err("Error write msic register"); - return -EFAULT; - } - } - - return size; -} - static ssize_t intel_scu_ipc_show_fw_version(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1864,18 +1552,12 @@ static struct bin_attribute bin_attr_dnx_hdr = { .read = read_dnx_hdr, }; -static DEVICE_ATTR(addr, S_IRUGO | S_IWUSR, - intel_scu_ipc_show_addr, intel_scu_ipc_store_addr); -static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, - intel_scu_ipc_show_value, intel_scu_ipc_store_value); static DEVICE_ATTR(fw_version, S_IRUGO, intel_scu_ipc_show_fw_version, NULL); static DEVICE_ATTR(last_error, S_IRUGO, intel_scu_ipc_show_last_error, NULL); static DEVICE_ATTR(medfw_prepare, S_IRUGO, intel_scu_ipc_show_medfw_prepare, NULL); static struct attribute *intel_scu_ipc_attrs[] = { - &dev_attr_addr.attr, - &dev_attr_value.attr, &dev_attr_fw_version.attr, &dev_attr_last_error.attr, &dev_attr_medfw_prepare.attr, @@ -1933,158 +1615,6 @@ static void intel_scu_sysfs_remove(struct pci_dev *dev) sysfs_remove_group(&dev->dev.kobj, &intel_scu_ipc_attr_group); } -static struct dentry *msic_debug_file; -static struct dentry *msic_all_debug_file; -static u16 msic_debug_reg_addr; -static char *msic_all_buf; -static int kbuf_offset; - -/* MSIC read register with DEBUGFS */ -/* Usage : */ -/* echo "-0x188" > msic && cat msic */ -/* => read value of register 0x188 */ -static ssize_t msic_debug_read(struct file *filp, char __user *buffer, - size_t length, loff_t *off) -{ - uint8_t data; - char buf[DATA_STRING_LEN]; - int len; - int ret; - - if (*off) - return 0; - - ret = intel_scu_ipc_ioread8(msic_debug_reg_addr, &data); - if (ret) { - dev_err(&ipcdev.pdev->dev, - "Error read msic register[%3.3x],ret=%d\n", - msic_debug_reg_addr, ret); - return -EFAULT; - } - len = snprintf(buf, sizeof(buf), "msic[%3.3x]=0x%x\n", \ - msic_debug_reg_addr, data); - if (copy_to_user(buffer, buf, len)) - return -EFAULT; - *off = len; - return len; -} - -static ssize_t msic_debug_write(struct file *filp, - const char __user *buffer, size_t len, loff_t *off) -{ - u32 reg_addr; - char buf[DATA_STRING_LEN]; - int ret; - - ret = copy_from_user(buf, buffer, min(sizeof(buf), len)); - if (ret) - return -EFAULT; - - if (buf[0] == '-') { - if (sscanf(buf+1, "%x", ®_addr) == 1) { - if (reg_addr < MIN_MSIC_REG - || reg_addr > MAX_MSIC_REG) { - dev_err(&ipcdev.pdev->dev, - "msic reg addr out of range\n"); - return -EINVAL; - } - msic_debug_reg_addr = (u16)reg_addr; - dev_err(&ipcdev.pdev->dev, - "Set MSIC reg addr=%x\n", msic_debug_reg_addr); - } - } - return len; -} -/* Basic function for dumping all MSIC registers */ -/* Called by msic_all_debug_read */ -static void msic_all_debug_dump(void) -{ - int i, j; - int ret; - uint8_t reg_value[4]; - - kbuf_offset = 0; - for (i = MIN_MSIC_REG ; i <= MAX_MSIC_REG ; i += 4) { - ret = intel_scu_ipc_ioread32(i, (u32 *) reg_value); - if (ret) { - dev_err(&ipcdev.pdev->dev, - "Error read msic reg[%x .. %x],ret=%d\n", i, i+3, ret); - } else { - for (j = 0 ; j < 4 ; j++) - kbuf_offset += sprintf( - msic_all_buf+kbuf_offset, - "[%3.3x]=0x%2.2x\n", - i+j, - reg_value[j]); - } - } -} - -/* MSIC dump all registers with DEBUGFS */ -/* Usage : */ -/* cat msic_all */ -/* => dump all registers */ -static ssize_t msic_all_debug_read(struct file *filp, - char *buffer, size_t length, loff_t *offset) -{ - int buf_count; - - if (*offset == 0) - /* Dump MSIC registers */ - msic_all_debug_dump(); - - if (*offset >= kbuf_offset) { - buf_count = 0; - goto end_of_read; - } else - buf_count = min(length, - (unsigned int)(kbuf_offset-(int)*offset)); - - if (copy_to_user(buffer, msic_all_buf + *offset, buf_count)) { - buf_count = -EFAULT; - goto end_of_read; - } - *offset += buf_count; -end_of_read: - return buf_count; -} - -static const struct file_operations msic_debug_ops = { - .write = msic_debug_write, - .read = msic_debug_read -}; - -static const struct file_operations msic_all_debug_ops = { - .read = msic_all_debug_read -}; - -static void create_msic_debug_entries(void) -{ - msic_debug_file = debugfs_create_file(MSIC_DEBUG_FILE, - 0644, NULL, NULL, &msic_debug_ops); - /* if DEBUGFS is not activated NULL pointer is returned */ - if (msic_debug_file) { - msic_all_debug_file = debugfs_create_file(MSIC_ALL_DEBUG_FILE, - 0444, NULL, NULL, &msic_all_debug_ops); - msic_all_buf = kmalloc((MAX_MSIC_REG - MIN_MSIC_REG + 1)*40, - GFP_KERNEL); - if (!msic_all_buf) { - dev_err(&ipcdev.pdev->dev, "Error allocate memory\n"); - return; - } - msic_debug_reg_addr = MIN_MSIC_REG; - } else { - /* DEBUGFS not activated, nothing to be done */ - } -} - -static void remove_msic_debug_entries(void) -{ - kfree(msic_all_buf); - debugfs_remove(msic_debug_file); - debugfs_remove(msic_all_debug_file); -} - #define OSHOB_SIZE 60 #define OSNIB_SIZE 32 #define IPCMSG_GET_HOBADDR 0xE5 @@ -2295,8 +1825,6 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) intel_scu_devices_create(*bus_id); - create_msic_debug_entries(); - intel_scu_sysfs_create(dev); pci_set_drvdata(dev, bus_id); @@ -2305,7 +1833,7 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) } /** - * ipc_remove - remove a bound IPC device + * ipc_remove - remove a bound IPC device * @pdev: PCI device * * In practice the SCU is not removable but this function is also @@ -2318,7 +1846,6 @@ static void ipc_remove(struct pci_dev *pdev) { int *bus_id = pci_get_drvdata(pdev); intel_scu_sysfs_remove(pdev); - remove_msic_debug_entries(); free_irq(pdev->irq, &ipcdev); pci_release_regions(pdev); pci_dev_put(ipcdev.pdev); diff --git a/drivers/platform/x86/intel_scu_pmic.c b/drivers/platform/x86/intel_scu_pmic.c new file mode 100644 index 0000000..d6b1b6e --- /dev/null +++ b/drivers/platform/x86/intel_scu_pmic.c @@ -0,0 +1,428 @@ +/* + * pmic.c - Intel MSIC Driver + * + * Copyright (C) 2012 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Author: Bin Yang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPC_WWBUF_SIZE 20 +#define IPC_RWBUF_SIZE 20 + +#define IPCMSG_PCNTRL 0xFF + +#define IPC_CMD_PCNTRL_W 0 +#define IPC_CMD_PCNTRL_R 1 +#define IPC_CMD_PCNTRL_M 2 + +static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 cmd, u32 sub) +{ + int i, j, err, inlen = 0, outlen; + + u8 wbuf[IPC_WWBUF_SIZE] = {}; + u8 rbuf[IPC_RWBUF_SIZE] = {}; + + memset(wbuf, 0, sizeof(wbuf)); + + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_LINCROFT) { + for (i = 0, j = 0; i < count; i++) { + wbuf[inlen++] = addr[i] & 0xff; + wbuf[inlen++] = (addr[i] >> 8) & 0xff; + + if (sub != IPC_CMD_PCNTRL_R) { + wbuf[inlen++] = data[j++]; + outlen = 0; + } + + if (sub == IPC_CMD_PCNTRL_M) + wbuf[inlen++] = data[j++]; + } + } else { + for (i = 0; i < count; i++) { + wbuf[inlen++] = addr[i] & 0xff; + wbuf[inlen++] = (addr[i] >> 8) & 0xff; + } + + if (sub == IPC_CMD_PCNTRL_R) { + outlen = count > 0 ? ((count - 1) / 4) + 1 : 0; + } else if (sub == IPC_CMD_PCNTRL_W) { + for (i = 0; i < count; i++) + wbuf[inlen++] = data[i] & 0xff; + outlen = 0; + } else if (sub == IPC_CMD_PCNTRL_M) { + wbuf[inlen++] = data[0] & 0xff; + wbuf[inlen++] = data[1] & 0xff; + outlen = 0; + } else + pr_err("IPC command not supported\n"); + } + + err = intel_scu_ipc_command(cmd, sub, (u32 *)wbuf, inlen, + (u32 *)rbuf, outlen); + + if (sub == IPC_CMD_PCNTRL_R) { + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_LINCROFT) { + for (i = 0, j = 2; i < count; i++, j += 3) + data[i] = rbuf[j]; + } else { + for (i = 0; i < count; i++) + data[i] = rbuf[i]; + } + } + + return err; +} + +int intel_scu_ipc_ioread8(u16 addr, u8 *data) +{ + return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); +} +EXPORT_SYMBOL(intel_scu_ipc_ioread8); + +int intel_scu_ipc_iowrite8(u16 addr, u8 data) +{ + return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_iowrite8); + +int intel_scu_ipc_iowrite32(u16 addr, u32 data) +{ + u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; + return pwr_reg_rdwr(x, (u8 *)&data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_iowrite32); + +int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) +{ + return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); +} +EXPORT_SYMBOL(intel_scu_ipc_readv); + +int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) +{ + return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_writev); + +int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) +{ + u8 data[2] = { bits, mask }; + return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); +} +EXPORT_SYMBOL(intel_scu_ipc_update_register); + +/* pmic sysfs for debug */ + +#define MAX_PMIC_REG_NR 4 +#define PMIC_OPS_LEN 10 + +enum { + PMIC_DBG_ADDR, + PMIC_DBG_BITS, + PMIC_DBG_DATA, + PMIC_DBG_MASK, +}; + +static char *pmic_msg_format[] = { + "addr[%d]: %#x\n", + "bits[%d]: %#x\n", + "data[%d]: %#x\n", + "mask[%d]: %#x\n", +}; + +static u16 pmic_reg_addr[MAX_PMIC_REG_NR]; +static u8 pmic_reg_bits[MAX_PMIC_REG_NR]; +static u8 pmic_reg_data[MAX_PMIC_REG_NR]; +static u8 pmic_reg_mask[MAX_PMIC_REG_NR]; +static int valid_addr_nr; +static int valid_bits_nr; +static int valid_data_nr; +static int valid_mask_nr; +static char pmic_ops[PMIC_OPS_LEN]; + +static int pmic_dbg_error; + +static ssize_t pmic_generic_show(char *buf, int valid, u8 *array, int type) +{ + int i; + ssize_t ret = 0; + + switch (type) { + case PMIC_DBG_ADDR: + for (i = 0; i < valid; i++) + ret += sprintf(buf + ret, pmic_msg_format[type], + i, pmic_reg_addr[i]); + break; + case PMIC_DBG_BITS: + case PMIC_DBG_DATA: + case PMIC_DBG_MASK: + for (i = 0; i < valid; i++) + ret += sprintf(buf + ret, pmic_msg_format[type], + i, array[i]); + break; + default: + break; + } + + return ret; +} + +static void pmic_generic_store(const char *buf, int *valid, u8 *array, int type) +{ + u32 tmp[MAX_PMIC_REG_NR]; + int i, ret; + + ret = sscanf(buf, "%x %x %x %x", &tmp[0], &tmp[1], &tmp[2], &tmp[3]); + if (ret < 0 || ret > MAX_PMIC_REG_NR) { + *valid = 0; + pmic_dbg_error = -EINVAL; + return; + } + + *valid = ret; + + switch (type) { + case PMIC_DBG_ADDR: + memset(pmic_reg_addr, 0, sizeof(pmic_reg_addr)); + for (i = 0; i < ret; i++) + pmic_reg_addr[i] = (u16)tmp[i]; + break; + case PMIC_DBG_BITS: + case PMIC_DBG_DATA: + case PMIC_DBG_MASK: + memset(array, 0, sizeof(*array) * MAX_PMIC_REG_NR); + for (i = 0; i < ret; i++) + array[i] = (u8)tmp[i]; + break; + default: + break; + } +} + +static ssize_t pmic_addr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return pmic_generic_show(buf, valid_addr_nr, NULL, PMIC_DBG_ADDR); +} + +static ssize_t pmic_addr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + pmic_generic_store(buf, &valid_addr_nr, NULL, PMIC_DBG_ADDR); + return size; +} + +static ssize_t pmic_bits_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return pmic_generic_show(buf, valid_bits_nr, pmic_reg_bits, + PMIC_DBG_BITS); +} + +static ssize_t pmic_bits_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + pmic_generic_store(buf, &valid_bits_nr, pmic_reg_bits, PMIC_DBG_BITS); + return size; +} + +static ssize_t pmic_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return pmic_generic_show(buf, valid_data_nr, pmic_reg_data, + PMIC_DBG_DATA); +} + +static ssize_t pmic_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + pmic_generic_store(buf, &valid_data_nr, pmic_reg_data, PMIC_DBG_DATA); + return size; +} + +static ssize_t pmic_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return pmic_generic_show(buf, valid_mask_nr, pmic_reg_mask, + PMIC_DBG_MASK); +} + +static ssize_t pmic_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + pmic_generic_store(buf, &valid_mask_nr, pmic_reg_mask, PMIC_DBG_MASK); + return size; +} + +static ssize_t pmic_ops_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int i, ret; + + memset(pmic_ops, 0, sizeof(pmic_ops)); + + ret = sscanf(buf, "%9s", pmic_ops); + if (ret < 0) { + pmic_dbg_error = -EINVAL; + goto end; + } + + if (valid_addr_nr <= 0) { + pmic_dbg_error = -EINVAL; + goto end; + } + + if (!strncmp("read", pmic_ops, PMIC_OPS_LEN)) { + valid_data_nr = valid_addr_nr; + for (i = 0; i < valid_addr_nr; i++) { + ret = intel_scu_ipc_ioread8(pmic_reg_addr[i], + &pmic_reg_data[i]); + if (ret) { + pmic_dbg_error = ret; + goto end; + } + } + } else if (!strncmp("write", pmic_ops, PMIC_OPS_LEN)) { + if (valid_addr_nr == valid_data_nr) { + for (i = 0; i < valid_addr_nr; i++) { + ret = intel_scu_ipc_iowrite8(pmic_reg_addr[i], + pmic_reg_data[i]); + if (ret) { + pmic_dbg_error = ret; + goto end; + } + } + } else { + pmic_dbg_error = -EINVAL; + goto end; + } + } else if (!strncmp("update", pmic_ops, PMIC_OPS_LEN)) { + if (valid_addr_nr == valid_mask_nr && + valid_mask_nr == valid_bits_nr) { + for (i = 0; i < valid_addr_nr; i++) { + ret = intel_scu_ipc_update_register( + pmic_reg_addr[i], + pmic_reg_bits[i], + pmic_reg_mask[i]); + if (ret) { + pmic_dbg_error = ret; + goto end; + } + } + } else { + pmic_dbg_error = -EINVAL; + goto end; + } + } else { + pmic_dbg_error = -EINVAL; + goto end; + } + + pmic_dbg_error = 0; + +end: + return size; +} + +static ssize_t pmic_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", pmic_dbg_error); +} + +static DEVICE_ATTR(addr, S_IRUGO|S_IWUSR, pmic_addr_show, pmic_addr_store); +static DEVICE_ATTR(bits, S_IRUGO|S_IWUSR, pmic_bits_show, pmic_bits_store); +static DEVICE_ATTR(data, S_IRUGO|S_IWUSR, pmic_data_show, pmic_data_store); +static DEVICE_ATTR(mask, S_IRUGO|S_IWUSR, pmic_mask_show, pmic_mask_store); +static DEVICE_ATTR(ops, S_IWUSR, NULL, pmic_ops_store); +static DEVICE_ATTR(error, S_IRUGO, pmic_show_error, NULL); + +static struct attribute *pmic_attrs[] = { + &dev_attr_addr.attr, + &dev_attr_bits.attr, + &dev_attr_data.attr, + &dev_attr_mask.attr, + &dev_attr_ops.attr, + &dev_attr_error.attr, + NULL, +}; + +static struct attribute_group pmic_attr_group = { + .name = "pmic_debug", + .attrs = pmic_attrs, +}; + +static int pmic_sysfs_create(struct ipc_device *ipcdev) +{ + return sysfs_create_group(&ipcdev->dev.kobj, &pmic_attr_group); +} + +static void pmic_sysfs_remove(struct ipc_device *ipcdev) +{ + sysfs_remove_group(&ipcdev->dev.kobj, &pmic_attr_group); +} + +static int __devinit pmic_probe(struct ipc_device *ipcdev) +{ + return pmic_sysfs_create(ipcdev); +} + +static int __devexit pmic_remove(struct ipc_device *ipcdev) +{ + pmic_sysfs_remove(ipcdev); + return 0; +} + +static struct ipc_driver pmic_driver = { + .driver = { + .name = "intel_scu_pmic", + .owner = THIS_MODULE, + }, + .probe = pmic_probe, + .remove = __devexit_p(pmic_remove), +}; + +static int __init pmic_module_init(void) +{ + return ipc_driver_register(&pmic_driver); +} + +static void __exit pmic_module_exit(void) +{ + ipc_driver_unregister(&pmic_driver); +} + +module_init(pmic_module_init); +module_exit(pmic_module_exit); + +MODULE_AUTHOR("Bin Yang"); +MODULE_DESCRIPTION("Intel PMIC Driver"); +MODULE_LICENSE("GPL v2"); -- 2.7.4