Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754668AbZGUGu3 (ORCPT ); Tue, 21 Jul 2009 02:50:29 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754541AbZGUGu2 (ORCPT ); Tue, 21 Jul 2009 02:50:28 -0400 Received: from centrinvest.ru ([94.25.115.130]:50229 "EHLO centrinvest.ru" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751158AbZGUGu0 (ORCPT ); Tue, 21 Jul 2009 02:50:26 -0400 From: "Andrey Panin" Date: Tue, 21 Jul 2009 10:50:21 +0400 To: "Gurudatt, Sreenidhi B" Cc: "x86@kernel.org" , "linux-kernel@vger.kernel.org" , Alan Cox Subject: Re: x86: IPC driver patch for Intel Moorestown platform Message-ID: <20090721065021.GC11781@centrinvest.ru> Mail-Followup-To: "Gurudatt, Sreenidhi B" , "x86@kernel.org" , "linux-kernel@vger.kernel.org" , Alan Cox References: <98769532B4BB14429434178695419EAE5F3F9541@bgsmsx501.gar.corp.intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <98769532B4BB14429434178695419EAE5F3F9541@bgsmsx501.gar.corp.intel.com> X-Uname: Linux 2.6.26-1-amd64 x86_64 User-Agent: Mutt/1.5.20 (2009-06-14) X-Anti-Virus: kav4lms: continue Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 57987 Lines: 1641 On 202, 07 21, 2009 at 06:58:47AM +0530, Gurudatt, Sreenidhi B wrote: > From c77d58ad07fbedb5de478bff21eb48c62b399cf1 Mon Sep 17 00:00:00 2001 > From: Sreenidhi Gurudatt > Date: Mon, 20 Jul 2009 15:41:10 +0530 > Subject: [PATCH] x86: IPC driver patch for Intel Moorestown platform. > > The Inter-Processor-Communication driver provides interfaces to host > drivers in kernel to access various Langwell ICH blocks such as PMIC, > GPIO and Battery for Intel Moorestown platform. > The IPC driver for Intel Moorestown platform communicates via SCU > firmware to access these registers. > > Signed-off-by: Sreenidhi Gurudatt > > modified: arch/x86/Kconfig > new file: arch/x86/include/asm/mrst_ipcdefs.h > modified: arch/x86/kernel/Makefile > new file: arch/x86/kernel/mrst_ipc.c > new file: arch/x86/kernel/mrst_ipc.h > --- > arch/x86/Kconfig | 10 +- > arch/x86/include/asm/mrst_ipcdefs.h | 205 +++++++ > arch/x86/kernel/Makefile | 1 + > arch/x86/kernel/mrst_ipc.c | 1058 +++++++++++++++++++++++++++++++++++ > arch/x86/kernel/mrst_ipc.h | 238 ++++++++ > 5 files changed, 1510 insertions(+), 2 deletions(-) > create mode 100644 arch/x86/include/asm/mrst_ipcdefs.h > create mode 100644 arch/x86/kernel/mrst_ipc.c > create mode 100644 arch/x86/kernel/mrst_ipc.h > > diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig > index 738bdc6..2c607b8 100644 > --- a/arch/x86/Kconfig > +++ b/arch/x86/Kconfig > @@ -570,8 +570,14 @@ config HPET_EMULATE_RTC > def_bool y > depends on HPET_TIMER && (RTC=y || RTC=m || RTC_DRV_CMOS=m || RTC_DRV_CMOS=y) > > -# Mark as embedded because too many people got it wrong. > -# The code disables itself when not needed. > +config LANGWELL_IPC > + def_bool n > + prompt "Langwell IPC Support" if (X86_32) > + help > + Langwell Inter Processor Communication block is used in Intel > + Moorestown MID platform to bridge the communications between IA CPU > + and System Controller Unit via SCU firmware. > + > config DMI > default y > bool "Enable DMI scanning" if EMBEDDED > diff --git a/arch/x86/include/asm/mrst_ipcdefs.h b/arch/x86/include/asm/mrst_ipcdefs.h > new file mode 100644 > index 0000000..cab8899 > --- /dev/null > +++ b/arch/x86/include/asm/mrst_ipcdefs.h > @@ -0,0 +1,205 @@ > +/* > + * mrst_ipc_defs.h: Driver for Langwell MRST IPC1 > + * Copyright (C) 2009 Intel Corp > + * > + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + * 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: Sreenidhi Gurudatt > + * Contact information: Sreenidhi Gurudatt > + */ > + > +#ifndef __MRST_IPCDEFS_H__ > +#define __MRST_IPCDEFS_H__ > + > +#include > +#include > + > +#define MAX_PMICREGS 5 > +#define MAX_PMIC_MOD_REGS 4 > + > +/* > + * List of commands sent by calling host > + * drivers to MRST_IPCDriver > +*/ > + > +/* CCA battery driver specific commands. > + * Thise commands are shared across MRST_IPCdriver > + * and calling host driver > + */ > + > +#define IPC_WATCHDOG 0xA0 > +#define IPC_PROGRAM_BUS_MASTER 0xA1 > +#define DEVICE_FW_UPGRADE 0xA2 > + > +#define IPC_BATT_CCA_READ 0xB0 > +#define IPC_BATT_CCA_WRITE 0xB1 > +#define IPC_BATT_GET_PROP 0xB2 > + > +/* VRTC IPC CMD ID and sub id */ > +#define IPC_VRTC_CMD 0xFA > +#define IPC_VRTC_SET_TIME 0x01 > +#define IPC_VRTC_SET_ALARM 0x02 > + > +/** > + * struct mrst_ipc_cmd_val - user-date for command. > + * @u32 mrst_ipc_cmd_data - User specified command data.; > + */ > +struct mrst_ipc_cmd_val { > + u32 mrst_ipc_cmd_data; > +}; > + > +/** > + * struct mrst_ipc_cmd_type > + * @u8 cmd - Command type > + * @u32 data - Command data > + * @u8 value - Command value > + * @u8 ioc - IOC/MSI field.; > + */ > +struct mrst_ipc_cmd_type { > + u8 cmd; > + u32 data; > + u8 value; > + u8 ioc; > +}; > + > +/** > + * struct mrst_ipc_batt_cca_data > + * @int cca_val - IPC_BATT_CCA_READ and IPC_BATT_CCA_WRITE > + */ > +struct mrst_ipc_batt_cca_data { > + int cca_val; > +}; > + > +/** > + * struct mrst_ipc_batt_prop_data - Structures defined for > + * battery PMIC driver This structure is used by IPC_BATT_GET_PROP > + * @u32 batt_value1 - Battery value. > + * @u8 batt_value2[5] - battery value for PMIC specific regs.; > + */ > +struct mrst_ipc_batt_prop_data { > + u32 batt_value1; > + u8 batt_value2[5]; > + u32 ipc_cmd_len; > + u8 ioc; > +}; > + > +/** > + * struct mrst_ipc_reg_data - PMIC register structure. > + * @u8 ioc - MSI or IOC bit. > + * @u32 address - PMIC register address. > + * @u32 data - PMIC register data. > +*/ > +struct mrst_ipc_reg_data { > + u8 ioc; > + u32 address; > + u32 data; > +}; > + > +/** > + * struct mrst_ipc_cmd - PMIC IPC command structure. > + * @u8 cmd - Commmand - bit. > + * @u32 data - Command data. > +*/ > +struct mrst_ipc_cmd { > + u8 cmd; > + u32 data; > +}; > + > +/** > + * struct pmicmodreg - PMIC IPC command structure. > + * @u16 register_address - register address. > + * @u8 value - register value. > + * @u8 bit_map - register bit_map. > + */ > +struct pmicmodreg { > + u16 register_address; > + u8 value; > + u8 bit_map; > +}; > + > +/** > + * struct pmicreg - PMIC IPC command structure. > + * @u16 register_address - register address. > + * @u8 value - register value. > + */ > +struct pmicreg { > + u16 register_address; > + u8 value; > +}; > + > +/** > + * struct mrst_pmic_reg_data - Moorestown specific PMIC IPC register structure. > + * @bool ioc; > + * @struct pmicreg pmic_reg_data[MAX_PMICREGS]; > + * @u8 num_entries - Number of register entries. > + */ > +struct mrst_pmic_reg_data { > + bool ioc; > + struct pmicreg pmic_reg_data[MAX_PMICREGS]; > + u8 num_entries; > +}; > + > +/** > + * struct mrst_pmic_mod_reg_data - Moorestown specific PMIC IPC register structure > + * @bool ioc - MSI Bit enabled/disabled. > + * @struct pmicmodreg pmic_mod_reg_data[MAX_PMIC_MOD_REGS] > + * @u8 num_entries - Number of entries > + */ > +struct mrst_pmic_mod_reg_data { > + bool ioc; > + struct pmicmodreg pmic_mod_reg_data[MAX_PMIC_MOD_REGS]; > + u8 num_entries; > +}; > + > +/** > + * struct watchdog_reg_data - Moorestown specific PMIC IPC register structure > + * @int payload1 - payload - u32. > + * @int payload2 - payload - u32. > + * @bool ioc - MSI or IOC bit. > +*/ > +struct watchdog_reg_data { > + int payload1; > + int payload2; > + bool ioc; > +}; > + > +/** > + * struct mrst_ipc_io_bus_master_regs - Moorestown specific PMIC IPC register structure > + * @u32 ctrl_reg_addr - control register address -u32. > + * @u32 ctrl_reg_data - control register data -u32. > +*/ > +struct mrst_ipc_io_bus_master_regs { > + u32 ctrl_reg_addr; > + u32 ctrl_reg_data; > +}; > + > +u8 mrst_pmic_ioreadb(u16 addr, bool ioc_notify, int *err); > +int mrst_pmic_iowriteb(u16 addr, bool ioc_notify, u8 data); > +int mrst_pmic_iowrite(struct mrst_pmic_reg_data *p_write_reg_data); > +int mrst_pmic_ioread(struct mrst_pmic_reg_data *p_read_reg_data); > +int mrst_pmic_ioread_modify(struct mrst_pmic_mod_reg_data > + *p_read_mod_reg_data); > +int mrst_ipc_read32(struct mrst_ipc_reg_data *p_reg_data); > +int mrst_ipc_write32(struct mrst_ipc_reg_data *p_reg_data); > +u32 mrst_ipc_batt_read(u8 ioc, int *err); > +int mrst_ipc_batt_write(u8 ioc, u32 value); > +int mrst_ipc_get_batt_properties(struct mrst_ipc_batt_prop_data *prop_data); > +int mrst_ipc_set_watchdog(struct watchdog_reg_data *p_watchdog_data); > +int mrst_ipc_program_io_bus_master(struct mrst_ipc_io_bus_master_regs > + *p_reg_data); > +int lnw_ipc_single_cmd(u8 cmd_id, u8 sub_id, int size, int msi); > + > +#endif > diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile > index 430d5b2..da56864 100644 > --- a/arch/x86/kernel/Makefile > +++ b/arch/x86/kernel/Makefile > @@ -85,6 +85,7 @@ obj-$(CONFIG_VM86) += vm86_32.o > obj-$(CONFIG_EARLY_PRINTK) += early_printk.o > > obj-$(CONFIG_HPET_TIMER) += hpet.o > +obj-$(CONFIG_LANGWELL_IPC) += mrst_ipc.o > > obj-$(CONFIG_K8_NB) += k8.o > obj-$(CONFIG_MGEODE_LX) += geode_32.o mfgpt_32.o > diff --git a/arch/x86/kernel/mrst_ipc.c b/arch/x86/kernel/mrst_ipc.c > new file mode 100644 > index 0000000..ed40632 > --- /dev/null > +++ b/arch/x86/kernel/mrst_ipc.c > @@ -0,0 +1,1058 @@ > +/* > + * mrst_ipc.c: Driver for Langwell IPC1 > + * Copyright (C) 2009 Intel Corp > + * > + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + * 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: Sreenidhi Gurudatt > + * Contact information: Sreenidhi Gurudatt > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "mrst_ipc.h" > + > +/*virtual memory address for IPC base returned by IOREMAP().*/ > +static void __iomem *p_mrst_ipc_base; > +static void __iomem *p_mrst_i2c_ser_bus; > +static struct pci_dev *mrst_ipc_pci_dev; > +static wait_queue_head_t mrst_cmd_wait; > +static int scu_cmd_completed; > +static void __iomem *lnw_ipc_virt_address; > +static unsigned short cmdid_pool = 0xffff; > +static DEFINE_MUTEX(mrst_ipc_mutex); > + > +static inline int lnw_ipc_set_mapping(struct pci_dev *dev) This inline isn't needed, gcc will handle it. > +{ > + unsigned long cadr; > + > + cadr = pci_resource_start(dev, 0); > + if (!cadr) { > + dev_info(&dev->dev, "No PCI resource for IPC\n"); > + return -ENODEV; > + } > + lnw_ipc_virt_address = ioremap_nocache(cadr, 0x1000); > + if (lnw_ipc_virt_address != NULL) { > + dev_info(&dev->dev, "lnw ipc base found 0x%lup: 0x%p\n", > + cadr, lnw_ipc_virt_address); > + return 0; > + } > + dev_err(&dev->dev, "Failed map LNW IPC1 phy address at %lu\n", cadr); > + return -ENODEV; > +} > + > +static inline void lnw_ipc_clear_mapping(void) > +{ > + iounmap(lnw_ipc_virt_address); > + lnw_ipc_virt_address = NULL; > +} Unused function. > +static u32 lnw_ipc_readl(u32 a) > +{ > + return readl(lnw_ipc_virt_address + a); > +} > + > +static inline void lnw_ipc_writel(u32 d, u32 a) > +{ > + writel(d, lnw_ipc_virt_address + a); > +} > + > +/** > + * int lnw_ipc_single_cmd() - Function to execute "one" IPC command > + * @u8 cmd_id : Command ID to send. > + * @u8 sub_id : Type of command. Subset of command ID. > + * @int msi Message Signal Interrupt flag : true/false > + * > + * This function provides and interface to send an IPC command to > + * SCU Firmware and recieve a response. > + */ > +int lnw_ipc_single_cmd(u8 cmd_id, u8 sub_id, int size, int msi) > +{ > + u32 cmdreg, stsreg, retry; > + > + if (size >= 16) { > + WARN_ON(1); > + printk(KERN_ERR > + "lnw_ipc_single_cmd: message size too big %d\n", size); > + goto err_ipccmd; Simply return -1 here. > + } > + > + WARN_ON(msi != 0 && msi != 1); > + > + cmdreg = cmd_id | (sub_id << 12) | (size << 16) | (msi << 8); > + > + lnw_ipc_writel(cmdreg, LNW_IPC_CMD); > + > + /* check status make sure the command is received by SCU */ > + retry = 1000; > + stsreg = lnw_ipc_readl(LNW_IPC_STS); > + if (stsreg & LNW_IPC_STS_ERR) { > + lnw_ipc_dbg("MRST IPC command ID %d error\n", cmd_id); > + goto err_ipccmd; And here too. > + } > + while ((stsreg & LNW_IPC_STS_BUSY) && retry) { > + lnw_ipc_dbg("MRST IPC command ID %d busy\n", cmd_id); > + stsreg = lnw_ipc_readl(LNW_IPC_STS); > + udelay(10); > + retry--; > + } > + > + if (!retry) > + printk(KERN_ERR "lnw_ipc_single_cmd: cmd %d failed/timeout", > + cmd_id); > + else > + lnw_ipc_dbg("MRST IPC command ID %d completed\n", cmd_id); > + > + return 0; > + > +err_ipccmd: > + return -1; You are not doing any cleanup here so both label and return are useless. > +} > +EXPORT_SYMBOL(lnw_ipc_single_cmd); > + > +/* > + * Interrupt handler for the command completion interrupt from SCU firmware. > + * This IRQ line is not shared. > + * The command processing is sequential, SCU Firmware does not process a > + * new command untill it recieves an EOI from IPC driver. > + */ > +static irqreturn_t mrst_ipc_irq(int irq, void *dev_id) > +{ > + union mrst_ipc_sts ipc_sts_reg; > + > + ipc_sts_reg.ipc_sts_data = readl(p_mrst_ipc_base + IPC_STS); > + > + if (!ipc_sts_reg.ipc_sts_parts.busy) { > + scu_cmd_completed = true; > + wake_up_interruptible(&mrst_cmd_wait); > + } else > + /*This IRQ is private.*/ > + dev_err(&mrst_ipc_pci_dev->dev, > + "Spurious IPC Interrupt recieved\n"); > + > + return IRQ_HANDLED; > +} > + > +static const struct mrst_ipc_driver ipc_mrst_driver = { > + .name = "MRST IPC Controller", > + /* > + * generic hardware linkage > + */ > + .irq = mrst_ipc_irq, > + .flags = 0, > +}; > + > +static int ipc_mrst_pci_probe(struct pci_dev *dev, > + const struct pci_device_id *id) > +{ > + int err, retval = 0, i; > + > + mrst_ipc_pci_dev = dev; > + lnw_ipc_dbg("Attempt to enable IPC irq 0x%x, pin %d\n", > + dev->irq, dev->pin); > + err = pci_enable_device(dev); > + if (err) { > + dev_err(&dev->dev, "Failed to enable MSRT IPC(%d)\n", err); > + goto fail; > + } > + retval = pci_request_regions(dev, "ipc_mrst"); > + if (retval) { > + dev_err(&dev->dev, "Failed to allocate resource\ > + for MRST IPC(%d)\n", retval); > + return -ENOMEM; > + } > + init_mrst_ipc_driver(); > + > + /* 0 means cmd ID is in use */ > + cmdid_pool = 0xffff; > + /* initialize mapping */ > + retval = lnw_ipc_set_mapping(dev); > + if (retval) > + goto fail; > + > + /* clear buffer */ > + for (i = 0; i < LNW_IPC_RWBUF_SIZE; i = i + 4) { > + lnw_ipc_writel(0, LNW_IPC_WBUF + i); > + lnw_ipc_writel(0, LNW_IPC_RBUF + i); > + } > + retval = request_irq(dev->irq, mrst_ipc_irq, 0, "ipc_mrst", > + (void *)&ipc_mrst_driver); > + if (retval == 0) > + return 0; Resource leak here, ioremapped region allocated in lnw_ipc_set_mapping() is not freed. > + dev_err(&dev->dev, "ipc_mrst_pci_probe: cannot register ISR %p irq %d\ > + ret %d\n", mrst_ipc_irq, dev->irq, retval); > +fail: > + pci_release_regions(dev); > + return retval; > + > +} > + > +static void ipc_mrst_pci_remove(struct pci_dev *pdev) > +{ > + pci_release_regions(pdev); > +} > + > +/* PCI driver selection metadata; PCI hotplugging uses this */ > +static const struct pci_device_id pci_ids[] = { > + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)} > +}; > + > +MODULE_DEVICE_TABLE(pci, pci_ids); > + > +/* pci driver glue; this is a "new style" PCI driver module */ This comment is useless. > +static struct pci_driver ipc_mrst_pci_driver = { > + .name = "ipc_mrst", > + .id_table = pci_ids, > + .probe = ipc_mrst_pci_probe, > + .remove = ipc_mrst_pci_remove, > +}; > + > +static int __init ipc_mrst_init(void) > +{ > + int retval; > + retval = pci_register_driver(&ipc_mrst_pci_driver); > + if (retval < 0) { > + printk(KERN_ERR "ipc_mrst_init: Failed to register %s\n", > + ipc_mrst_pci_driver.name); > + pci_unregister_driver(&ipc_mrst_pci_driver); > + } else { > + printk(KERN_INFO "****Loaded %s driver version %s****\n", > + ipc_mrst_pci_driver.name, MRST_IPC_DRIVER_VERSION); This line is cofusing, please align it properly. > + } > + return retval; > +} > + > +static void __exit ipc_mrst_exit(void) > +{ > + free_irq(mrst_ipc_pci_dev->irq, mrst_ipc_irq); > + iounmap(p_mrst_ipc_base); > + iounmap(p_mrst_i2c_ser_bus); > + pci_unregister_driver(&ipc_mrst_pci_driver); > + de_init_mrst_ipc_driver(); > +} > + > +/** > + * u8 mrst_ipc_batt_read() - This function reads the data from Coulumb counter > + * registers in SCU firmware for Moorestown platform. > + * @u8 ioc: ioc bit to enable/disable command completion interrupt from SCU. > + * @int *err: negative if an error occurred > + * > + * This is used by Intel Moorestown Battery driver to read Coulumb counters. > + */ > +u32 mrst_ipc_batt_read(u8 ioc, int *err) > +{ > + u32 data = 0; > + > + mutex_lock(&mrst_ipc_mutex); > + *err = do_mrst_ipc_battery(CCA_REG_READ, ioc, NULL); > + if (*err == 0) > + data = readl(p_mrst_ipc_base + IPC_RBUF); > + mutex_unlock(&mrst_ipc_mutex); > + > + return data; > +} > +EXPORT_SYMBOL(mrst_ipc_batt_read); > + > +/** > + * int mrst_ipc_batt_write() - This function reads the data from Coulumb counter > + * registers in SCU firmware for Moorestown platform. > + * @u8 ioc: ioc bit to enable/disable command completion interrupt from SCU. > + * @u32 value: Data value to be written. > + * > + * This is used by Intel Moorestown Battery driver to write to Coulumb counters. > + */ > +int mrst_ipc_batt_write(u8 ioc, u32 value) > +{ > + int ret; > + mutex_lock(&mrst_ipc_mutex); > + ret = do_mrst_ipc_battery(CCA_REG_WRITE, ioc, &value); > + mutex_unlock(&mrst_ipc_mutex); > + return ret; > +} > +EXPORT_SYMBOL(mrst_ipc_batt_write); > + > +/** > + * int mrst_ipc_get_batt_properties() - This function reads the data > + * from Coulumb counter registers in SCU firmware for Moorestown platform. > + * @struct mrst_ipc_batt_prop_data *mrst_batt_prop: > + * > + * This is used by Intel Moorestown Battery driver to get the battery > + * properties. > + */ > +int mrst_ipc_get_batt_properties(struct mrst_ipc_batt_prop_data *mrst_batt_prop) > +{ > + int ret; > + u32 rbuf_offset = 2; > + u32 ipc_wbuf; > + u8 cbuf[MAX_NUM_ENTRIES] = { '\0' }; > + u32 i; > + > + if (mrst_batt_prop == NULL) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + if (mrst_batt_prop->ipc_cmd_len < 4 || > + mrst_batt_prop->ipc_cmd_len > 9) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + ret = do_mrst_ipc_battery(CCA_REG_GET_PROP, mrst_batt_prop->ioc, NULL); > + if (ret) > + return ret; /*return error*/ What is this ^^^^^^^^^^^^^^^^^^ ? > + > + /* On wake-up fill the user buffer with IPC_RBUF data.*/ > + rbuf_offset = 0; > + > + if (mrst_batt_prop->ipc_cmd_len >= 4) { > + ipc_wbuf = readl(p_mrst_ipc_base + IPC_RBUF); > + rbuf_offset += 4; > + for (i = 0; i < (mrst_batt_prop->ipc_cmd_len - 4); i++) { > + cbuf[i] = readb(p_mrst_ipc_base + IPC_RBUF + > + rbuf_offset); > + mrst_batt_prop->batt_value2[i] = cbuf[i]; > + rbuf_offset++; > + } > + } > + mutex_unlock(&mrst_ipc_mutex); > + return ret; > +} > +EXPORT_SYMBOL(mrst_ipc_get_batt_properties); > + > +int init_mrst_ipc_driver(void) > +{ > + mutex_lock(&mrst_ipc_mutex); What are you trying to protect with this mutex here ? > + init_waitqueue_head(&mrst_cmd_wait); > + > + /* Map the memory of ipc1 PMIC reg base */ > + p_mrst_ipc_base = ioremap_nocache(IPC_BASE_ADDRESS, IPC_MAX_ADDRESS); > + if (p_mrst_ipc_base == NULL) { > + dev_err(&mrst_ipc_pci_dev->dev, "ERR:IPC Address Map Failed\n"); > + mutex_unlock(&mrst_ipc_mutex); > + return -ENOMEM; > + } > + > + p_mrst_i2c_ser_bus = ioremap_nocache(I2C_SER_BUS, I2C_MAX_ADDRESS); > + if (p_mrst_i2c_ser_bus == NULL) { > + iounmap(p_mrst_ipc_base); > + dev_err(&mrst_ipc_pci_dev->dev, "ERR:IPC Address Map Failed\n"); > + mutex_unlock(&mrst_ipc_mutex); > + return -ENOMEM; > + } > + > + mutex_unlock(&mrst_ipc_mutex); > + > + return 0; > +} > + > +static int de_init_mrst_ipc_driver(void) > +{ > + mutex_lock(&mrst_ipc_mutex); > + > + lnw_ipc_dbg( > + "ipc_driver: in <%s> -> <%s> file at line no = <%d>\n", > + __func__, __FILE__, __LINE__); > + iounmap(p_mrst_ipc_base); > + iounmap(p_mrst_i2c_ser_bus); > + mutex_unlock(&mrst_ipc_mutex); > + > + return 0; > + } Some evil force totally messed up formatting of this function. There are many other coding style violations in this driver. Please use checkpath.pl > + > +/** > + * u8 mrst_pmic_ioreadb() - This function reads the data from PMIC > + * registers and fills the user specified buffer with data. > + * @u16 addr: 16 Bit PMIC register offset address. > + * @bool ioc_notify: boolean_value to speicify Interrupt On Completion bit. > + * @int *err: negative if an error occurred > + * > + * This function reads 1byte of data data from specified PMIC register offset. > + * It returns 8 bits of PMIC register data > + */ > +u8 mrst_pmic_ioreadb(u16 addr, bool ioc_notify, int *err) > +{ > + struct mrst_pmic_reg_data r_data; > + int ret_val; > + > + r_data.ioc = ioc_notify; > + r_data.num_entries = 1; > + r_data.pmic_reg_data[0].register_address = addr; > + > + ret_val = mrst_pmic_ioread(&r_data); > + *err = ret_val; > + if (ret_val) { > + printk(KERN_ERR "mrst_pmic_ioreadb: ioreadb failed! \n"); > + return 0xFF; > + } > + return r_data.pmic_reg_data[0].value; > +} > +EXPORT_SYMBOL(mrst_pmic_ioreadb); > + > +/** > + * int mrst_pmic_iowriteb() - This function reads the data from PMIC > + * registers and fills the user specified buffer with data. > + * @u16 addr: 16 Bit PMIC register offset address. > + * @bool ioc_notify: boolean_value to speicify Interrupt On Completion bit. > + * @u8 data: 8 Bit PMIC register data.. > + * > + * This function reads 1byte of data data from specified PMIC register offset. > + * It returns 0 on success and returns -1 or an appropriate error-code. > + */ > +int mrst_pmic_iowriteb(u16 addr, bool ioc_notify, u8 data) > +{ > + struct mrst_pmic_reg_data w_data; > + int ret_val; > + > + w_data.ioc = ioc_notify; > + w_data.num_entries = 1; > + w_data.pmic_reg_data[0].register_address = addr; > + w_data.pmic_reg_data[0].value = data; > + > + ret_val = mrst_pmic_iowrite(&w_data); > + if (ret_val) { > + printk(KERN_ERR "MRST IPC writeb failed! \n"); > + return ret_val; > + } > + return 0; > +} > +EXPORT_SYMBOL(mrst_pmic_iowriteb); > + > +/** > + * int mrst_pmic_ioread() - This function reads the data from PMIC > + * registers and fills the user specified buffer with data. > + * @struct mrst_pmic_reg_data *p_read_reg_data: pointer to user-defined > + * structure containing PMIC register address and data fields. > + * > + * This function reads requested data from PMIC registers and fills the user > + * specified buffer with data. It returns 0 on success and returns -1 or > + * an appropriate error-code if the function failed to read IPC data. > + */ > +int mrst_pmic_ioread(struct mrst_pmic_reg_data *p_read_reg_data) > +{ > + union mrst_ipc_fw_cmd ipc_cmd; > + u32 *ipc_wbuf; > + u8 cbuf[IPC_BUF_LEN] = { '\0' }; > + u32 cnt = 0; > + u32 i = 0; > + u32 rbuf_offset = 2; > + ipc_wbuf = (u32 *)&cbuf; > + > + > + if (p_read_reg_data == NULL) { > + printk(KERN_ERR "mrst_pmic_ioread: No reg data\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + if (p_read_reg_data->num_entries > MAX_NUM_ENTRIES) { > + printk(KERN_ERR "mrst_pmic_ioread: num_entries too high\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + > + mrst_set_ipc_cmd_fields(&ipc_cmd, p_read_reg_data->ioc, > + 3*p_read_reg_data->num_entries, PMIC_REG_READ); > + > + for (i = 0; i < p_read_reg_data->num_entries; i++) { > + cbuf[cnt] = p_read_reg_data->pmic_reg_data[i].register_address; > + cbuf[cnt + 1] = > + p_read_reg_data->pmic_reg_data[i].register_address >> 8; > + cbuf[cnt + 2] = p_read_reg_data->pmic_reg_data[i].value; > + cnt = cnt + 3; > + } > + > + /*MRST SCU Busy-Bit check*/ > + if (is_mrst_scu_busy()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + rbuf_offset = 0; > + for (i = 0; i < p_read_reg_data->num_entries; i++) { > + writel(ipc_wbuf[i], p_mrst_ipc_base + IPC_WBUF + rbuf_offset); > + rbuf_offset += 4; > + if (i >= 3) > + break; > + } > + > + mrst_ipc_send_cmd(ipc_cmd.cmd_data); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ipc_cmd.cmd_parts.ioc)) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + /* IPC driver expects interrupt when IOC is set to 1.*/ > + if (ipc_cmd.cmd_parts.ioc == 1 && scu_cmd_completed == false) { > + printk(KERN_ERR "mrst_pmic: No interrupt on timeout\n"); > + mutex_unlock(&mrst_ipc_mutex); > + return -ETIMEDOUT; > + } > + /* Read RBUF if there is no error*/ > + if (is_mrst_scu_error()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + > + rbuf_offset = 2; > + for (i = 0; i < p_read_reg_data->num_entries; i++) { > + p_read_reg_data->pmic_reg_data[i].value = > + readb(p_mrst_ipc_base + IPC_RBUF + rbuf_offset); > + rbuf_offset += 3; > + } > + > + mutex_unlock(&mrst_ipc_mutex); > + > + return 0; > +} > +EXPORT_SYMBOL(mrst_pmic_ioread); > + > +/** > + * int mrst_pmic_iowrite() - PMIC register write API. > + * @struct mrst_pmic_reg_data *p_write_reg_data: pointer to PMIC write > + * data structure. > + * > + * This function writes the user-specified data to the PMIC registers. The > + * function returns 0 on success and returns -1 or an appropriate error-code > + * if the function failed to process the write request. > + */ > +int mrst_pmic_iowrite(struct mrst_pmic_reg_data *p_write_reg_data) > +{ > + union mrst_ipc_fw_cmd ipc_cmd; > + u32 *ipc_wbuf; > + u8 cbuf[IPC_BUF_LEN] = { '\0' }; > + u32 cnt = 0; > + u32 i = 0; > + u32 rbuf_offset = 2; > + > + ipc_wbuf = (u32 *)&cbuf; > + > + if (p_write_reg_data == NULL) { > + printk(KERN_ERR "mrst_pmic_write: write_reg_data is NULL\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + if (p_write_reg_data->num_entries > MAX_NUM_ENTRIES) { > + printk(KERN_ERR "mrst_pmic_write: num entries too high\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + > + mrst_set_ipc_cmd_fields(&ipc_cmd, p_write_reg_data->ioc, > + 3*p_write_reg_data->num_entries, PMIC_REG_WRITE); > + > + for (i = 0; i < p_write_reg_data->num_entries; i++) { > + cbuf[cnt] = p_write_reg_data->pmic_reg_data[i].register_address; > + cbuf[cnt + 1] = > + p_write_reg_data->pmic_reg_data[i].register_address >> 8; > + cbuf[cnt + 2] = p_write_reg_data->pmic_reg_data[i].value; > + cnt = cnt + 3; > + } > + /*MRST SCU Busy-Bit check*/ > + if (is_mrst_scu_busy()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + rbuf_offset = 0; > + for (i = 0; i < p_write_reg_data->num_entries; i++) { > + writel(ipc_wbuf[i], p_mrst_ipc_base + IPC_WBUF > + + rbuf_offset); > + rbuf_offset += 4; > + if (i >= 3) > + break; > + } > + mrst_ipc_send_cmd(ipc_cmd.cmd_data); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ipc_cmd.cmd_parts.ioc)) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + /*Check for error in command processing*/ > + if (is_mrst_scu_error()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + mutex_unlock(&mrst_ipc_mutex); > + return 0; > +} > +EXPORT_SYMBOL(mrst_pmic_iowrite); > + > +/** > + * int mrst_pmic_ioread_modify() - Performs PMIC register read modified > + * writes. > + * @struct mrst_pmic_mod_reg_data *p_read_mod_reg_data: pointer to user-defined > + * structure containing PMIC register address and data fields. > + * > + * This function reads the requested data from PMIC registers after a bit-map > + * modification as defined by the calling host driver. > + */ > +int mrst_pmic_ioread_modify(struct mrst_pmic_mod_reg_data > + *p_read_mod_reg_data) > +{ > + union mrst_ipc_fw_cmd ipc_cmd; > + u32 *ipc_wbuf; > + u8 cbuf[IPC_BUF_LEN] = { '\0' }; > + u32 cnt = 0; > + u32 i = 0; > + u32 rbuf_offset = 2; > + ipc_wbuf = (u32 *)&cbuf; > + > + > + if (p_read_mod_reg_data == NULL) { > + printk(KERN_ERR "mrst_pmic_ioread_modify: reg_data is NULL\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + if (p_read_mod_reg_data->num_entries > MAX_NUM_RMW_ENTRIES) { > + printk(KERN_ERR "mrst_pic_ioread_modify: num_entries too high\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + > + mrst_set_ipc_cmd_fields(&ipc_cmd, p_read_mod_reg_data->ioc, > + 4*p_read_mod_reg_data->num_entries, > + PMIC_REG_READ_MODIFY); > + > + for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { > + cbuf[cnt] = > + p_read_mod_reg_data->pmic_mod_reg_data[i].register_address; > + cbuf[cnt + 1] = p_read_mod_reg_data-> > + pmic_mod_reg_data[i].register_address >> 8; > + cbuf[cnt + 2] = p_read_mod_reg_data-> > + pmic_mod_reg_data[i].value; > + cbuf[cnt + 3] = p_read_mod_reg_data-> > + pmic_mod_reg_data[i].bit_map; > + cnt = cnt + 4; > + } > + > + /*MRST SCU Busy-Bit check*/ > + if (is_mrst_scu_busy()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + rbuf_offset = 0; > + for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { > + writel(ipc_wbuf[i], > + p_mrst_ipc_base + IPC_WBUF + rbuf_offset); > + if (i >= 3) > + break; > + } > + mrst_ipc_send_cmd(ipc_cmd.cmd_data); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ipc_cmd.cmd_parts.ioc)) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + /* IPC driver expects interrupt when IOC is set to 1.*/ > + if (ipc_cmd.cmd_parts.ioc == 1 && scu_cmd_completed == false) { > + mutex_unlock(&mrst_ipc_mutex); > + printk(KERN_ERR "ERROR! IPC: No Interrupt got on Timeout\n"); > + return -ETIMEDOUT; > + } > + > + /* Read RBUF if there is no error*/ > + if (is_mrst_scu_error()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + > + /* On wake-up fill the user buffer with IPC_RBUF data.*/ > + rbuf_offset = 2; > + for (i = 0; i < p_read_mod_reg_data->num_entries; i++) { > + p_read_mod_reg_data->pmic_mod_reg_data[i].value = > + readb(p_mrst_ipc_base + IPC_RBUF + rbuf_offset); > + rbuf_offset += 4; > + } > + mutex_unlock(&mrst_ipc_mutex); > + return 0; > +} > +EXPORT_SYMBOL(mrst_pmic_ioread_modify); > + > +/** > + * int mrst_ipc_read32() - This function is used by any host driver to request > + * for IPC driver to process an indirect read operation. > + * @struct mrst_ipc_reg_data *p_reg_data: Pointer to register data for indirect > + * reads. > + * > + * This API provides mechanism to read a 32bit data from a user-specified > + * valid 32bit address. > + */ > +int mrst_ipc_read32(struct mrst_ipc_reg_data *p_reg_data) > +{ > + union mrst_ipc_fw_cmd ipc_cmd; > + > + if (p_reg_data == NULL) { > + printk(KERN_ERR "mrst_ipc_read32: p_reg_data is NULL\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + > + mrst_set_ipc_cmd_fields(&ipc_cmd, p_reg_data->ioc, 4, 0); > + /* Override with INDIRECT_READ and size command */ > + ipc_cmd.cmd_parts.cmd = INDIRECT_READ; > + > + /*MRST SCU Busy-Bit check*/ > + if (is_mrst_scu_busy()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + writel(p_reg_data->address, (p_mrst_ipc_base + IPC_SPTR)); > + mrst_ipc_send_cmd(ipc_cmd.cmd_data); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ipc_cmd.cmd_parts.ioc)) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + /* IPC driver expects interrupt when IOC is set to 1.*/ > + if (ipc_cmd.cmd_parts.ioc == 1 && scu_cmd_completed == false) { > + mutex_unlock(&mrst_ipc_mutex); > + printk(KERN_ERR "mrst_ipc_read32: No Interrupt on Timeout\n"); > + return -ETIMEDOUT; > + } > + > + /* Read RBUF if there is no error*/ > + if (is_mrst_scu_error()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + /* Command completed successfully Read the data */ > + p_reg_data->data = readl(p_mrst_ipc_base + IPC_RBUF); > + > + mutex_unlock(&mrst_ipc_mutex); > + return 0; > +} > +EXPORT_SYMBOL(mrst_ipc_read32); > + > +/** > + * int mrst_ipc_write32 - function is used by any host driver to request > + * for IPC driver to process an indirect write operation. > + * @struct mrst_ipc_reg_data *p_reg_data: Pointer to register data for indirect > + * writes. > + * > + * This API provides mechanism to write a 32bit data from a user-specified > + * valid 32bit address. > + */ > +int mrst_ipc_write32(struct mrst_ipc_reg_data *p_reg_data) > +{ > + union mrst_ipc_fw_cmd ipc_cmd; > + > + if (p_reg_data == NULL) { > + printk(KERN_ERR "mrst_ipc_write32: p_reg_data is NULL\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + > + mrst_set_ipc_cmd_fields(&ipc_cmd, p_reg_data->ioc, 4, 0); > + /*Override this function specific fields*/ > + ipc_cmd.cmd_parts.cmd = INDIRECT_WRITE; > + > + /*MRST SCU Busy-Bit check*/ > + if (is_mrst_scu_busy()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + writel(p_reg_data->address, (p_mrst_ipc_base + IPC_DPTR)); > + writel(p_reg_data->data, (p_mrst_ipc_base + IPC_WBUF)); > + mrst_ipc_send_cmd(ipc_cmd.cmd_data); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ipc_cmd.cmd_parts.ioc)) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + /*Check for error in command processing*/ > + if (is_mrst_scu_error()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + mutex_unlock(&mrst_ipc_mutex); > + return 0; > +} > +EXPORT_SYMBOL(mrst_ipc_write32); > + > +/** > + * int mrst_ipc_set_watchdog() - Function provides interface to set kernel watch > + * dog timer using the SCU firmware command. > + * @struct watchdog_reg_data *p_watchdog_reg_data: Pointer to data user > + * defined data structure. > + * > + * This function provides and interface to to set kernel watch-dog > + * timer using the SCU firmware command. > + */ > +int mrst_ipc_set_watchdog(struct watchdog_reg_data *p_watchdog_reg_data) > +{ > + union mrst_ipc_fw_cmd ipc_cmd; > + u32 *ipc_wbuf; > + u8 cbuf[16] = { '\0' }; > + u32 rbuf_offset = 2; > + > + ipc_wbuf = (u32 *)&cbuf; > + > + if (p_watchdog_reg_data == NULL) { > + printk(KERN_ERR "mrst_ipc_set_watchdog: reg_data is NULL\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + > + mrst_set_ipc_cmd_fields(&ipc_cmd, p_watchdog_reg_data->ioc, 2, 0x0); > + /*Override this function specific fields*/ > + ipc_cmd.cmd_parts.cmd = IPC_SET_WATCHDOG_TIMER; > + > + /*MRST SCU Busy-Bit check*/ > + if (is_mrst_scu_busy()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + ipc_wbuf[0] = p_watchdog_reg_data->payload1; > + writel(ipc_wbuf[0], p_mrst_ipc_base + IPC_WBUF + rbuf_offset); > + > + ipc_wbuf[1] = p_watchdog_reg_data->payload2; > + writel(ipc_wbuf[1], p_mrst_ipc_base + IPC_WBUF + rbuf_offset); > + > + /*execute the command by writing to IPC_CMD registers*/ > + mrst_ipc_send_cmd(ipc_cmd.cmd_data); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ipc_cmd.cmd_parts.ioc)) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EBUSY; > + } > + > + /* IPC driver expects interrupt when IOC is set to 1.*/ > + if (ipc_cmd.cmd_parts.ioc == 1 && scu_cmd_completed == false) { > + mutex_unlock(&mrst_ipc_mutex); > + printk(KERN_ERR > + "mrst_ipc_set_watchdog: No Interrupt on timeout\n"); > + return -ETIMEDOUT; > + } > + > + /*Check for error in command processing*/ > + if (is_mrst_scu_error()) { > + mutex_unlock(&mrst_ipc_mutex); > + return -EINVAL; > + } > + mutex_unlock(&mrst_ipc_mutex); > + return 0; > +} > +EXPORT_SYMBOL(mrst_ipc_set_watchdog); > + > +/** > + * int mrst_ipc_program_io_bus_master(): This function will be used by calling > + * host driver to access registers located in the DFT shims. > + * @ipc_io_bus_master_regs *p_reg_data: inputstructure containing > + * ctrl_reg_addr and data_reg_addr. > + * This function will be used by the calling host driver to access registers > + * located in the DFT shims. The API reads or writes to DFT shims based on the > + * operation specified by the CTRL_REG_ADDR register. > + */ > +int mrst_ipc_program_io_bus_master(struct mrst_ipc_io_bus_master_regs > + *p_reg_data) > +{ > + u32 io_bus_master_cmd = 0; > + > + if (p_reg_data == NULL) { > + printk(KERN_ERR > + "mrst_ipc_program_io_bus_master: p_reg_data is NULL\n"); > + WARN_ON(1); > + return -EINVAL; > + } > + > + mutex_lock(&mrst_ipc_mutex); > + /* Read the first byte for command*/ > + io_bus_master_cmd = (p_reg_data->ctrl_reg_addr)&(0xFF000000); > + io_bus_master_cmd = (io_bus_master_cmd >> 24); > + > + if (io_bus_master_cmd == NOP_CMD) { > + lnw_ipc_dbg("NOP_CMD = 0x%x\n", io_bus_master_cmd); > + } else if (io_bus_master_cmd == READ_CMD) { > + lnw_ipc_dbg("Address %#xp = data = %#x\n", > + (unsigned int)(p_mrst_i2c_ser_bus + CTRL_REG_ADDR), > + p_reg_data->ctrl_reg_addr); > + writel(p_reg_data->ctrl_reg_addr, (p_mrst_i2c_ser_bus > + + CTRL_REG_ADDR)); > + p_reg_data->ctrl_reg_data = > + readl(p_mrst_i2c_ser_bus + CTRL_REG_DATA); > + } else if (io_bus_master_cmd == WRITE_CMD) { > + writel(p_reg_data->ctrl_reg_data, (p_mrst_i2c_ser_bus > + + CTRL_REG_DATA)); > + writel(p_reg_data->ctrl_reg_addr, p_mrst_i2c_ser_bus > + + CTRL_REG_ADDR); > + } else { > + printk(KERN_ERR > + "mrst_program_io_bus_master: invalid cmd = 0x%x\n", > + io_bus_master_cmd); > + mutex_unlock(&mrst_ipc_mutex); > + WARN_ON(1); > + return -EINVAL; > + } > + mutex_unlock(&mrst_ipc_mutex); > + return 0; > +} > +EXPORT_SYMBOL(mrst_ipc_program_io_bus_master); > + > +/* Set the command fields for IPC_CMD */ > +static inline int mrst_set_ipc_cmd_fields(union mrst_ipc_fw_cmd *ipc_cmd, > + u8 ioc, u32 size, u8 cmd_id) This inline function defined after being used. It's hard to tell whether gcc will actually inline it. > +{ > + ipc_cmd->cmd_parts.cmd = IPC_PMIC_CMD_READ_WRITE; > + ipc_cmd->cmd_parts.ioc = ioc; > + ipc_cmd->cmd_parts.rfu1 = 0x0; > + ipc_cmd->cmd_parts.cmd_ID = cmd_id; > + ipc_cmd->cmd_parts.size = size; > + return 0; > +} > +/* Wait for command completion from SCU firmware */ > +static inline int wait_for_scu_cmd_completion(u8 mrst_ipc_ioc_bit) This function is quite large, inlining it is probably harmful. > +{ > + union mrst_ipc_sts ipc_sts_reg; > + u64 time_to_wait = 0xFF; > + > + if (mrst_ipc_ioc_bit) { > + /*wait for 10ms do not tie to kernel timer_ticks*/ > + time_to_wait = msecs_to_jiffies(IPC_TIMEOUT); > + /* Wait for command completion from SCU firmware */ > + wait_event_interruptible_timeout(mrst_cmd_wait, > + scu_cmd_completed, time_to_wait); > + return 0; > + } else { > + udelay(IPC_WAIT_TIME); > + ipc_sts_reg.ipc_sts_data = readl(p_mrst_ipc_base + IPC_STS); > + if (ipc_sts_reg.ipc_sts_parts.busy) { > + printk(KERN_ERR "wait_for_scu_cmd_completion:\ > + Timeout ioc = 0 and SCU is busy%d\n", > + ipc_sts_reg.ipc_sts_parts.busy); > + return -EBUSY; > + } > + } > + return 0; /*SCU Not busy*/ > +} > + > +/*MRST SCU Error-Bit check*/ > +static inline int is_mrst_scu_error(void) This inline is questionable too. > +{ > + union mrst_ipc_sts ipc_sts_reg; > + > + ipc_sts_reg.ipc_sts_data = readl(p_mrst_ipc_base + IPC_STS); > + if (ipc_sts_reg.ipc_sts_parts.error) { > + printk(KERN_ERR "is_mrst_scu_error: Command failed Error\ > + code = %#x\n", ipc_sts_reg.ipc_sts_parts.error); > + WARN_ON(1); > + return -EINVAL; > + } > + return 0; /*No error*/ > +} > + > +/*MRST SCU Busy-Bit check*/ > +static inline int is_mrst_scu_busy(void) This one too. > +{ > + union mrst_ipc_sts ipc_sts_reg; > + u32 retry = MAX_RETRY_CNT; > + > + while (retry--) { > + ipc_sts_reg.ipc_sts_data = readl(p_mrst_ipc_base + IPC_STS); > + if (!ipc_sts_reg.ipc_sts_parts.busy) > + break; > + udelay(USLEEP_STS_TIMEOUT); /*10usec*/ > + } > + if (ipc_sts_reg.ipc_sts_parts.busy) { > + printk(KERN_DEBUG "is_mrst_scu_busy: SCU is busy %d\n", > + ipc_sts_reg.ipc_sts_parts.busy); > + return -EBUSY; > + } > + return 0; /*SCU Not busy*/ > +} > + > +/*Send the command to SCU */ > +static void mrst_ipc_send_cmd(u32 cmd_data) > +{ > + scu_cmd_completed = false; > + writel(cmd_data, p_mrst_ipc_base + IPC_CMD); > +} > + > +static int do_mrst_ipc_battery(u32 cmd, u8 ioc, u32 *data) > +{ > + union mrst_ipc_fw_cmd ipc_cca_cmd; > + union mrst_ipc_sts ipc_sts_reg; > + > + /* Fill in the common fields */ > + ipc_cca_cmd.cmd_parts.cmd = IPC_CCA_CMD_READ_WRITE; > + ipc_cca_cmd.cmd_parts.rfu1 = 0x0; > + ipc_cca_cmd.cmd_parts.size = 0; > + ipc_cca_cmd.cmd_parts.rfu2 = 0x0; > + > + /* Caller dependant fields */ > + ipc_cca_cmd.cmd_parts.ioc = ioc; > + ipc_cca_cmd.cmd_parts.cmd_ID = cmd; > + > + /* MRST SCU busy bit check */ > + if (is_mrst_scu_busy()) > + return -EBUSY; > + > + scu_cmd_completed = false; > + > + /* If we have data write the data field */ > + if (data) > + writel(*data, p_mrst_ipc_base + IPC_WBUF + 4); > + /* and the command */ > + writel(ipc_cca_cmd.cmd_data, p_mrst_ipc_base + IPC_CMD); > + > + /* Wait for command completion from SCU firmware */ > + if (wait_for_scu_cmd_completion(ioc)) > + return -EBUSY; > + > + /*Check for error in command processing*/ > + ipc_sts_reg.ipc_sts_data = readl(p_mrst_ipc_base + IPC_STS); > + if (is_mrst_scu_error()) > + return -EINVAL; > + > + return 0; > +} > + > +MODULE_AUTHOR("Sreenidhi Gurudatt "); > +MODULE_DESCRIPTION("Intel Moorestown IPC driver"); > +MODULE_LICENSE("GPL V2"); > + > +module_init(ipc_mrst_init); > +module_exit(ipc_mrst_exit); > diff --git a/arch/x86/kernel/mrst_ipc.h b/arch/x86/kernel/mrst_ipc.h > new file mode 100644 > index 0000000..d234d68 > --- /dev/null > +++ b/arch/x86/kernel/mrst_ipc.h > @@ -0,0 +1,238 @@ > +/* > + * ipc_mrst.h: Driver for Langwell IPC1 > + * Copyright (C) 2009 Intel Corp > + * > + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + * 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: Sreenidhi Gurudatt > + * Contact information: Sreenidhi Gurudatt > + */ > + > +#ifndef __IPC_MRST_H__ > +#define __IPC_MRST_H__ > + > +#include > +#include > + > +#ifdef LNW_IPC_DEBUG > + > +#define lnw_ipc_dbg(fmt, args...) \ > + do { printk(KERN_INFO fmt, ## args); } while (0) > +#else > +#define lnw_ipc_dbg(fmt, args...) do { } while (0) > +#endif > + > +#define MRST_IPC_DRIVER_VERSION "0.01.005" > +#define IPC_TIMEOUT 100 /*Wait in msecs*/ > +#define IPC_WAIT_TIME 5000 /*Wait in usecs*/ > +#define MAX_RETRY_CNT 10 > +#define MAX_NB_BUF_SIZE 100 > +#define IPC_BUF_LEN 16 > +#define MAX_NUM_ENTRIES 5 > +#define MAX_NUM_RMW_ENTRIES 4 > +#define USLEEP_STS_TIMEOUT 100 > + > +#define LNW_IPC1_BASE 0xff11c000 > +#define LNW_IPC1_MMAP_SIZE 1024 > + > +#define LNW_IPC1 > +#define LNW_IPC_CMD 0x00 > +#define LNW_IPC_STS 0x04 > +#define LNW_IPC_DPTR 0x08 > +#define LNW_IPC_WBUF 0x80 > +#define LNW_IPC_RBUF 0x90 > +#define LNW_IPC_RWBUF_SIZE 16 > + > +/* IPC status register layout */ > +#define LNW_IPC_STS_BUSY (1<<0) > +#define LNW_IPC_STS_ERR (1<<1) > +#define LNW_IPC_STS_CMDID (0xF<<4) > +#define LNW_IPC_STS_INITID (0xFF<<8) > +#define LNW_IPC_STS_ERR_CODE (0xFF<<16) > + > +/* IPC command register layout */ > +#define LNW_IPC_CMD_CMD (0xFF<<0) > +#define LNW_IPC_CMD_MSI (1<<8) > +#define LNW_IPC_CMD_ID (0xF<<12) > +#define LNW_IPC_CMD_SIZE (0xFF<<16) > + > +enum IPC_CMD { > + NORMAL_WRITE, /*0x00 Normal Write */ > + MSG_WRITE, /*0x01 Message Write */ > + INDIRECT_READ, /*0x02 Indirect Read */ > + RSVD, /*0x03 Reserved */ > + READ_DMA, /*0x04 Read DMA */ > + INDIRECT_WRITE, /*0x05 Indirect write */ > +}; > + > +int lnw_ipc_send_cmd(unsigned char cmd, int size, int msi); > + > +/** > + * struct mrst_ipc_driver - the basic blah structure > + * @const char *name: Name of the driver (mrst_ipc_mrst). > + * @irq : Pointer to irq function. > + * @flags: Flags. > + */ > +struct mrst_ipc_driver { > + const char *name; > + irqreturn_t(*irq) (int irq, void *ipc); > + int flags; > +}; > + > +/* > + * defines specific to mrst_ipc_driver and > + * not exposed outside > + */ > + > +/*cmd_ID fields for CCA Read/Writes*/ > + > +#define CCA_REG_WRITE 0x0000 > +#define CCA_REG_READ 0x0001 > +#define CCA_REG_GET_PROP 0x0002 > + > +#define IPC_SET_WATCHDOG_TIMER 0xF8 > +#define IPC_CCA_CMD_READ_WRITE 0xEF > +#define IPC_DEVICE_FIRMWARE_UPGRADE 0xFE > +#define IPC_PMIC_CMD_READ_WRITE 0xFF > + > +/*cmd_ID fields for CCA Read/Writes*/ > +#define PMIC_REG_WRITE 0x0000 > +#define PMIC_REG_READ 0x0001 > +#define PMIC_REG_READ_MODIFY 0x0002 > +#define LPE_READ 0x0003 > +#define LPE_WRITE 0x0004 > + > +#define IPC_CMD_GO_TO_DFU_MODE 0x0001 > +#define IPC_CMD_UPDATE_FW 0x0002 > +#define IPC_CMD_FORCE_UPDATE_FW 0x0003 > + > +#define NORMAL_WRITE 0x00 > +#define MESSAGE_WRITE 0x01 > +#define INDIRECT_READ 0x02 > +#define INDIRECT_WRITE 0x05 > +#define READ_DMA 0x04 > + > + > +/* Used to override user option */ > +#define IOC 1 > + > +#define IPC_REG_ISR_FAILED 0xFF > + > +/********************************************* > + * Define IPC_Base_Address and offsets > + ********************************************/ > +#define IPC_BASE_ADDRESS 0xFF11C000 > +#define I2C_SER_BUS 0xFF12B000 > +#define DFU_LOAD_ADDR 0xFFFC0000 > +#define NOP_CMD 0x00 > +#define WRITE_CMD 0x01 > +#define READ_CMD 0x02 > + > +/*256K storage size for loading the FW image.*/ > +#define MAX_FW_SIZE 262144 > + > +/* IPC2 offset addresses */ > +#define IPC_MAX_ADDRESS 0x100 > +/* I2C offset addresses - Confirm this */ > +#define I2C_MAX_ADDRESS 0x10 > +/* Offsets for CTRL_REG_ADDR and CTRL_REG_DATA */ > +#define CTRL_REG_ADDR 0x00 > +#define CTRL_REG_DATA 0x04 > +#define I2C_MAX_ADDRESS 0x10 > + > +#define IPC_CMD 0x00 > +#define IPC_STS 0x04 > +#define IPC_SPTR 0x08 > +#define IPC_DPTR 0x0C > +#define IPC_WBUF 0x80 > +#define IPC_RBUF 0x90 > + > +/** > + * union mrst_ipc_sts - IPC_STS register data structure. > + * @u32 busy:1 - busy field. > + * @u32 error:1 - error field. > + * @u32 rfu1:2 - reserved field. > + * @u32 cmd_id:4 - command_id field. > + * @u32 initiator_id:8 - initiater_id field. > + * @u32 error_code:8 - error_code field. > + * @u32 rfu3:8 - reserved field. > + */ > +union mrst_ipc_sts { > + struct { > + u32 busy:1; > + u32 error:1; > + u32 rfu1:2; > + u32 cmd_id:4; > + u32 initiator_id:8; > + u32 error_code:8; > + u32 rfu3:8; > + } ipc_sts_parts; > + u32 ipc_sts_data; > +}; > + > +/** > + * union mrst_ipc_fw_cmd - IPC_CMD register data structure. > + * @u32 cmd:8 - busy field. > + * @u32 ioc:1 - error field. > + * @u32 rfu1:3 - reserved field. > + * @u32 cmd_id:4 - command_id field. > + * @u32 size:8 - initiater_id field. > + * @u32 rfu2:8 - reserved field. > + */ > +union mrst_ipc_fw_cmd { > + struct { > + u32 cmd:8; > + u32 ioc:1; > + u32 rfu1:3; > + u32 cmd_ID:4; > + u32 size:8; > + u32 rfu2:8; > + } cmd_parts; > + u32 cmd_data; > +}; > + > +/** > + * struct mrst_ipc_intr - IPC_CMD register data structure. > + * @u8 cmd - cmd field. > + * @u32 data - data field. > + */ > +struct mrst_ipc_intr { > + u8 cmd; > + u32 data; > + > +}; > + > +/** > + * struct mrst_ipc_work_struct - IPC work structure with command_id. > + * @struct work_struct mrst_ipc_work - work data structure. > + * @u32 cmd_id - command_id field. > + */ > +struct mrst_ipc_work_struct{ > + struct work_struct ipc_work; > + u32 cmd_id; > +}; > + > +int init_mrst_ipc_driver(void); > +static inline int is_mrst_scu_busy(void); > +static inline int mrst_set_ipc_cmd_fields(union mrst_ipc_fw_cmd *ipc_cmd, > + u8 ioc, u32 size, u8 cmd_id); > +static int do_mrst_ipc_battery(u32 cmd, u8 ioc, u32 *data); > +static void mrst_ipc_send_cmd(u32 cmd_data); > +static inline int wait_for_scu_cmd_completion(u8 mrst_ipc_ioc_bit); > +static inline int is_mrst_scu_error(void); > +static int de_init_mrst_ipc_driver(void); > + > +#endif > -- > 1.5.4.5 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/