From e090d506c3f1b314059fb77b177cd4193bb81d6e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 21 Mar 2010 01:06:05 +0100 Subject: mfd: Add support for the RDC321x southbridge This patch adds a new MFD driver for the RDC321x southbridge. This southbridge is always present in the RDC321x System-on-a-Chip and provides access to some GPIOs as well as a watchdog. Access to these two functions is done using the southbridge PCI device configuration space. Signed-off-by: Florian Fainelli Signed-off-by: Samuel Ortiz --- include/linux/mfd/rdc321x.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 include/linux/mfd/rdc321x.h (limited to 'include/linux') diff --git a/include/linux/mfd/rdc321x.h b/include/linux/mfd/rdc321x.h new file mode 100644 index 000000000000..4bdf19c8eedf --- /dev/null +++ b/include/linux/mfd/rdc321x.h @@ -0,0 +1,26 @@ +#ifndef __RDC321X_MFD_H +#define __RDC321X_MFD_H + +#include +#include + +/* Offsets to be accessed in the southbridge PCI + * device configuration register */ +#define RDC321X_WDT_CTRL 0x44 +#define RDC321X_GPIO_CTRL_REG1 0x48 +#define RDC321X_GPIO_DATA_REG1 0x4c +#define RDC321X_GPIO_CTRL_REG2 0x84 +#define RDC321X_GPIO_DATA_REG2 0x88 + +#define RDC321X_MAX_GPIO 58 + +struct rdc321x_gpio_pdata { + struct pci_dev *sb_pdev; + unsigned max_gpios; +}; + +struct rdc321x_wdt_pdata { + struct pci_dev *sb_pdev; +}; + +#endif /* __RDC321X_MFD_H */ -- cgit v1.2.3-59-g8ed1b From 872c1b14e78661086ade8b501888850da2636eee Mon Sep 17 00:00:00 2001 From: Henrik Kretzschmar Date: Fri, 26 Mar 2010 02:40:13 +0100 Subject: mfd: Section cleanup of 88pm860x driver This patch fixes three section mismatches. WARNING: drivers/mfd/88pm860x.o(.text+0x12): Section mismatch in reference from the function pm860x_device_exit() to the function .devexit.text:device_irq_exit() The function pm860x_device_exit() references a function in an exit section. Often the function device_irq_exit() has valid usage outside the exit section and the fix is to remove the __devexit annotation of device_irq_exit. WARNING: drivers/mfd/88pm860x.o(.text+0xb0): Section mismatch in reference from the function pm860x_device_init() to the function .devinit.text:device_8606_init() The function pm860x_device_init() references the function __devinit device_8606_init(). This is often because pm860x_device_init lacks a __devinit annotation or the annotation of device_8606_init is wrong. WARNING: drivers/mfd/88pm860x.o(.text+0xbe): Section mismatch in reference from the function pm860x_device_init() to the function .devinit.text:device_8607_init() The function pm860x_device_init() references the function __devinit device_8607_init(). This is often because pm860x_device_init lacks a __devinit annotation or the annotation of device_8607_init is wrong. Signed-off-by: Henrik Kretzschmar Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 6 +++--- include/linux/mfd/88pm860x.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 405d2d5183cf..2c65a2c57294 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -566,7 +566,7 @@ out: return ret; } -static void __devexit device_irq_exit(struct pm860x_chip *chip) +static void device_irq_exit(struct pm860x_chip *chip) { if (chip->core_irq) free_irq(chip->core_irq, chip); @@ -703,7 +703,7 @@ out: return; } -int pm860x_device_init(struct pm860x_chip *chip, +int __devinit pm860x_device_init(struct pm860x_chip *chip, struct pm860x_platform_data *pdata) { chip->core_irq = 0; @@ -731,7 +731,7 @@ int pm860x_device_init(struct pm860x_chip *chip, return 0; } -void pm860x_device_exit(struct pm860x_chip *chip) +void __devexit pm860x_device_exit(struct pm860x_chip *chip) { device_irq_exit(chip); mfd_remove_devices(chip->dev); diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index e3c4ff8c3e38..bfd23bef7363 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -370,7 +370,7 @@ extern int pm860x_set_bits(struct i2c_client *, int, unsigned char, unsigned char); extern int pm860x_device_init(struct pm860x_chip *chip, - struct pm860x_platform_data *pdata); -extern void pm860x_device_exit(struct pm860x_chip *chip); + struct pm860x_platform_data *pdata) __devinit ; +extern void pm860x_device_exit(struct pm860x_chip *chip) __devexit ; #endif /* __LINUX_MFD_88PM860X_H */ -- cgit v1.2.3-59-g8ed1b From bd3581323cc02aefc79a21780a4ca8c578642892 Mon Sep 17 00:00:00 2001 From: "Ira W. Snyder" Date: Wed, 7 Apr 2010 09:43:00 +0200 Subject: mfd: Janz CMOD-IO PCI MODULbus Carrier Board support The Janz CMOD-IO PCI MODULbus carrier board is a PCI to MODULbus bridge, which may host many different types of MODULbus daughterboards, including CAN and GPIO controllers. Signed-off-by: Ira W. Snyder Reviewed-by: Wolfgang Grandegger Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 1 + drivers/mfd/janz-cmodio.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/janz.h | 54 ++++++++ 4 files changed, 369 insertions(+) create mode 100644 drivers/mfd/janz-cmodio.c create mode 100644 include/linux/mfd/janz.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 93d0e31c0438..05c137d16b04 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -418,6 +418,16 @@ config MFD_RDC321X southbridge which provides access to GPIOs and Watchdog using the southbridge PCI device configuration space. +config MFD_JANZ_CMODIO + tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" + select MFD_CORE + depends on PCI + help + This is the core driver for the Janz CMOD-IO PCI MODULbus + carrier board. This device is a PCI to MODULbus bridge which may + host many different types of MODULbus daughterboards, including + CAN and GPIO controllers. + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f177e064b31b..21eff85b288a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o +obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c new file mode 100644 index 000000000000..9ed630799acc --- /dev/null +++ b/drivers/mfd/janz-cmodio.c @@ -0,0 +1,304 @@ +/* + * Janz CMOD-IO MODULbus Carrier Board PCI Driver + * + * Copyright (c) 2010 Ira W. Snyder + * + * Lots of inspiration and code was copied from drivers/mfd/sm501.c + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "janz-cmodio" + +/* Size of each MODULbus module in PCI BAR4 */ +#define CMODIO_MODULBUS_SIZE 0x200 + +/* Maximum number of MODULbus modules on a CMOD-IO carrier board */ +#define CMODIO_MAX_MODULES 4 + +/* Module Parameters */ +static unsigned int num_modules = CMODIO_MAX_MODULES; +static unsigned char *modules[CMODIO_MAX_MODULES] = { + "empty", "empty", "empty", "empty", +}; + +module_param_array(modules, charp, &num_modules, S_IRUGO); +MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board"); + +/* Unique Device Id */ +static unsigned int cmodio_id; + +struct cmodio_device { + /* Parent PCI device */ + struct pci_dev *pdev; + + /* PLX control registers */ + struct janz_cmodio_onboard_regs __iomem *ctrl; + + /* hex switch position */ + u8 hex; + + /* mfd-core API */ + struct mfd_cell cells[CMODIO_MAX_MODULES]; + struct resource resources[3 * CMODIO_MAX_MODULES]; + struct janz_platform_data pdata[CMODIO_MAX_MODULES]; +}; + +/* + * Subdevices using the mfd-core API + */ + +static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv, + char *name, unsigned int devno, + unsigned int modno) +{ + struct janz_platform_data *pdata; + struct mfd_cell *cell; + struct resource *res; + struct pci_dev *pci; + + pci = priv->pdev; + cell = &priv->cells[devno]; + res = &priv->resources[devno * 3]; + pdata = &priv->pdata[devno]; + + cell->name = name; + cell->resources = res; + cell->num_resources = 3; + + /* Setup the subdevice ID -- must be unique */ + cell->id = cmodio_id++; + + /* Add platform data */ + pdata->modno = modno; + cell->platform_data = pdata; + cell->data_size = sizeof(*pdata); + + /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ + res->flags = IORESOURCE_MEM; + res->parent = &pci->resource[3]; + res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno); + res->end = res->start + CMODIO_MODULBUS_SIZE - 1; + res++; + + /* PLX Control Registers -- PCI BAR4 is interrupt and other registers */ + res->flags = IORESOURCE_MEM; + res->parent = &pci->resource[4]; + res->start = pci->resource[4].start; + res->end = pci->resource[4].end; + res++; + + /* + * IRQ + * + * The start and end fields are used as an offset to the irq_base + * parameter passed into the mfd_add_devices() function call. All + * devices share the same IRQ. + */ + res->flags = IORESOURCE_IRQ; + res->parent = NULL; + res->start = 0; + res->end = 0; + res++; + + return 0; +} + +/* Probe each submodule using kernel parameters */ +static int __devinit cmodio_probe_submodules(struct cmodio_device *priv) +{ + struct pci_dev *pdev = priv->pdev; + unsigned int num_probed = 0; + char *name; + int i; + + for (i = 0; i < num_modules; i++) { + name = modules[i]; + if (!strcmp(name, "") || !strcmp(name, "empty")) + continue; + + dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name); + cmodio_setup_subdevice(priv, name, num_probed, i); + num_probed++; + } + + /* print an error message if no modules were probed */ + if (num_probed == 0) { + dev_err(&priv->pdev->dev, "no MODULbus modules specified, " + "please set the ``modules'' kernel " + "parameter according to your " + "hardware configuration\n"); + return -ENODEV; + } + + return mfd_add_devices(&pdev->dev, 0, priv->cells, + num_probed, NULL, pdev->irq); +} + +/* + * SYSFS Attributes + */ + +static ssize_t mbus_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cmodio_device *priv = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex); +} + +static DEVICE_ATTR(modulbus_number, S_IRUGO, mbus_show, NULL); + +static struct attribute *cmodio_sysfs_attrs[] = { + &dev_attr_modulbus_number.attr, + NULL, +}; + +static const struct attribute_group cmodio_sysfs_attr_group = { + .attrs = cmodio_sysfs_attrs, +}; + +/* + * PCI Driver + */ + +static int __devinit cmodio_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct cmodio_device *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&dev->dev, "unable to allocate private data\n"); + ret = -ENOMEM; + goto out_return; + } + + pci_set_drvdata(dev, priv); + priv->pdev = dev; + + /* Hardware Initialization */ + ret = pci_enable_device(dev); + if (ret) { + dev_err(&dev->dev, "unable to enable device\n"); + goto out_free_priv; + } + + pci_set_master(dev); + ret = pci_request_regions(dev, DRV_NAME); + if (ret) { + dev_err(&dev->dev, "unable to request regions\n"); + goto out_pci_disable_device; + } + + /* Onboard configuration registers */ + priv->ctrl = pci_ioremap_bar(dev, 4); + if (!priv->ctrl) { + dev_err(&dev->dev, "unable to remap onboard regs\n"); + ret = -ENOMEM; + goto out_pci_release_regions; + } + + /* Read the hex switch on the carrier board */ + priv->hex = ioread8(&priv->ctrl->int_enable); + + /* Add the MODULbus number (hex switch value) to the device's sysfs */ + ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); + if (ret) { + dev_err(&dev->dev, "unable to create sysfs attributes\n"); + goto out_unmap_ctrl; + } + + /* + * Disable all interrupt lines, each submodule will enable its + * own interrupt line if needed + */ + iowrite8(0xf, &priv->ctrl->int_disable); + + /* Register drivers for all submodules */ + ret = cmodio_probe_submodules(priv); + if (ret) { + dev_err(&dev->dev, "unable to probe submodules\n"); + goto out_sysfs_remove_group; + } + + return 0; + +out_sysfs_remove_group: + sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); +out_unmap_ctrl: + iounmap(priv->ctrl); +out_pci_release_regions: + pci_release_regions(dev); +out_pci_disable_device: + pci_disable_device(dev); +out_free_priv: + kfree(priv); +out_return: + return ret; +} + +static void __devexit cmodio_pci_remove(struct pci_dev *dev) +{ + struct cmodio_device *priv = pci_get_drvdata(dev); + + mfd_remove_devices(&dev->dev); + sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group); + iounmap(priv->ctrl); + pci_release_regions(dev); + pci_disable_device(dev); + kfree(priv); +} + +#define PCI_VENDOR_ID_JANZ 0x13c3 + +/* The list of devices that this module will support */ +static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = { + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, cmodio_pci_ids); + +static struct pci_driver cmodio_pci_driver = { + .name = DRV_NAME, + .id_table = cmodio_pci_ids, + .probe = cmodio_pci_probe, + .remove = __devexit_p(cmodio_pci_remove), +}; + +/* + * Module Init / Exit + */ + +static int __init cmodio_init(void) +{ + return pci_register_driver(&cmodio_pci_driver); +} + +static void __exit cmodio_exit(void) +{ + pci_unregister_driver(&cmodio_pci_driver); +} + +MODULE_AUTHOR("Ira W. Snyder "); +MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver"); +MODULE_LICENSE("GPL"); + +module_init(cmodio_init); +module_exit(cmodio_exit); diff --git a/include/linux/mfd/janz.h b/include/linux/mfd/janz.h new file mode 100644 index 000000000000..e9994c469803 --- /dev/null +++ b/include/linux/mfd/janz.h @@ -0,0 +1,54 @@ +/* + * Common Definitions for Janz MODULbus devices + * + * Copyright (c) 2010 Ira W. Snyder + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef JANZ_H +#define JANZ_H + +struct janz_platform_data { + /* MODULbus Module Number */ + unsigned int modno; +}; + +/* PLX bridge chip onboard registers */ +struct janz_cmodio_onboard_regs { + u8 unused1; + + /* + * Read access: interrupt status + * Write access: interrupt disable + */ + u8 int_disable; + u8 unused2; + + /* + * Read access: MODULbus number (hex switch) + * Write access: interrupt enable + */ + u8 int_enable; + u8 unused3; + + /* write-only */ + u8 reset_assert; + u8 unused4; + + /* write-only */ + u8 reset_deassert; + u8 unused5; + + /* read-write access to serial EEPROM */ + u8 eep; + u8 unused6; + + /* write-only access to EEPROM chip select */ + u8 enid; +}; + +#endif /* JANZ_H */ -- cgit v1.2.3-59-g8ed1b From d183fcc975cbbc9c427deb2d7948ab03673995c9 Mon Sep 17 00:00:00 2001 From: Todd Fischer Date: Mon, 5 Apr 2010 20:23:56 -0600 Subject: mfd: Move TPS6507x register definition to header file. Other sub-drivers for the TPS6507x chip will need to use register definition so move it out of the source file and into a header file. Signed-off-by: Todd Fischer Acked-by: Mark Brown Signed-off-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/regulator/tps6507x-regulator.c | 60 +-------------- include/linux/mfd/tps6507x.h | 134 +++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 59 deletions(-) create mode 100644 include/linux/mfd/tps6507x.h (limited to 'include/linux') diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 74841abcc9cc..23c0597ab1f5 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -25,65 +25,7 @@ #include #include #include - -/* Register definitions */ -#define TPS6507X_REG_PPATH1 0X01 -#define TPS6507X_REG_INT 0X02 -#define TPS6507X_REG_CHGCONFIG0 0X03 -#define TPS6507X_REG_CHGCONFIG1 0X04 -#define TPS6507X_REG_CHGCONFIG2 0X05 -#define TPS6507X_REG_CHGCONFIG3 0X06 -#define TPS6507X_REG_REG_ADCONFIG 0X07 -#define TPS6507X_REG_TSCMODE 0X08 -#define TPS6507X_REG_ADRESULT_1 0X09 -#define TPS6507X_REG_ADRESULT_2 0X0A -#define TPS6507X_REG_PGOOD 0X0B -#define TPS6507X_REG_PGOODMASK 0X0C -#define TPS6507X_REG_CON_CTRL1 0X0D -#define TPS6507X_REG_CON_CTRL2 0X0E -#define TPS6507X_REG_CON_CTRL3 0X0F -#define TPS6507X_REG_DEFDCDC1 0X10 -#define TPS6507X_REG_DEFDCDC2_LOW 0X11 -#define TPS6507X_REG_DEFDCDC2_HIGH 0X12 -#define TPS6507X_REG_DEFDCDC3_LOW 0X13 -#define TPS6507X_REG_DEFDCDC3_HIGH 0X14 -#define TPS6507X_REG_DEFSLEW 0X15 -#define TPS6507X_REG_LDO_CTRL1 0X16 -#define TPS6507X_REG_DEFLDO2 0X17 -#define TPS6507X_REG_WLED_CTRL1 0X18 -#define TPS6507X_REG_WLED_CTRL2 0X19 - -/* CON_CTRL1 bitfields */ -#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4) -#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3) -#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2) -#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1) -#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0) - -/* DEFDCDC1 bitfields */ -#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN BIT(7) -#define TPS6507X_DEFDCDC1_DCDC1_MASK 0X3F - -/* DEFDCDC2_LOW bitfields */ -#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK 0X3F - -/* DEFDCDC2_HIGH bitfields */ -#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK 0X3F - -/* DEFDCDC3_LOW bitfields */ -#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK 0X3F - -/* DEFDCDC3_HIGH bitfields */ -#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK 0X3F - -/* TPS6507X_REG_LDO_CTRL1 bitfields */ -#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK 0X0F - -/* TPS6507X_REG_DEFLDO2 bitfields */ -#define TPS6507X_REG_DEFLDO2_LDO2_MASK 0X3F - -/* VDCDC MASK */ -#define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F +#include /* DCDC's */ #define TPS6507X_DCDC_1 0 diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h new file mode 100644 index 000000000000..155bee1f7823 --- /dev/null +++ b/include/linux/mfd/tps6507x.h @@ -0,0 +1,134 @@ +/* linux/mfd/tps6507x.h + * + * Functions to access TPS65070 power management chip. + * + * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) + * + * + * For licencing details see kernel-base/COPYING + */ + +#ifndef __LINUX_MFD_TPS6507X_H +#define __LINUX_MFD_TPS6507X_H + +/* + * ---------------------------------------------------------------------------- + * Registers, all 8 bits + * ---------------------------------------------------------------------------- + */ + + +/* Register definitions */ +#define TPS6507X_REG_PPATH1 0X01 +#define TPS6507X_CHG_USB BIT(7) +#define TPS6507X_CHG_AC BIT(6) +#define TPS6507X_CHG_USB_PW_ENABLE BIT(5) +#define TPS6507X_CHG_AC_PW_ENABLE BIT(4) +#define TPS6507X_CHG_AC_CURRENT BIT(2) +#define TPS6507X_CHG_USB_CURRENT BIT(0) + +#define TPS6507X_REG_INT 0X02 +#define TPS6507X_REG_MASK_AC_USB BIT(7) +#define TPS6507X_REG_MASK_TSC BIT(6) +#define TPS6507X_REG_MASK_PB_IN BIT(5) +#define TPS6507X_REG_TSC_INT BIT(3) +#define TPS6507X_REG_PB_IN_INT BIT(2) +#define TPS6507X_REG_AC_USB_APPLIED BIT(1) +#define TPS6507X_REG_AC_USB_REMOVED BIT(0) + +#define TPS6507X_REG_CHGCONFIG0 0X03 + +#define TPS6507X_REG_CHGCONFIG1 0X04 +#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4) +#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3) +#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2) +#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1) +#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0) + +#define TPS6507X_REG_CHGCONFIG2 0X05 + +#define TPS6507X_REG_CHGCONFIG3 0X06 + +#define TPS6507X_REG_ADCONFIG 0X07 +#define TPS6507X_ADCONFIG_AD_ENABLE BIT(7) +#define TPS6507X_ADCONFIG_START_CONVERSION BIT(6) +#define TPS6507X_ADCONFIG_CONVERSION_DONE BIT(5) +#define TPS6507X_ADCONFIG_VREF_ENABLE BIT(4) +#define TPS6507X_ADCONFIG_INPUT_AD_IN1 0 +#define TPS6507X_ADCONFIG_INPUT_AD_IN2 1 +#define TPS6507X_ADCONFIG_INPUT_AD_IN3 2 +#define TPS6507X_ADCONFIG_INPUT_AD_IN4 3 +#define TPS6507X_ADCONFIG_INPUT_TS_PIN 4 +#define TPS6507X_ADCONFIG_INPUT_BAT_CURRENT 5 +#define TPS6507X_ADCONFIG_INPUT_AC_VOLTAGE 6 +#define TPS6507X_ADCONFIG_INPUT_SYS_VOLTAGE 7 +#define TPS6507X_ADCONFIG_INPUT_CHARGER_VOLTAGE 8 +#define TPS6507X_ADCONFIG_INPUT_BAT_VOLTAGE 9 +#define TPS6507X_ADCONFIG_INPUT_THRESHOLD_VOLTAGE 10 +#define TPS6507X_ADCONFIG_INPUT_ISET1_VOLTAGE 11 +#define TPS6507X_ADCONFIG_INPUT_ISET2_VOLTAGE 12 +#define TPS6507X_ADCONFIG_INPUT_REAL_TSC 14 +#define TPS6507X_ADCONFIG_INPUT_TSC 15 + +#define TPS6507X_REG_TSCMODE 0X08 +#define TPS6507X_TSCMODE_X_POSITION 0 +#define TPS6507X_TSCMODE_Y_POSITION 1 +#define TPS6507X_TSCMODE_PRESSURE 2 +#define TPS6507X_TSCMODE_X_PLATE 3 +#define TPS6507X_TSCMODE_Y_PLATE 4 +#define TPS6507X_TSCMODE_STANDBY 5 +#define TPS6507X_TSCMODE_ADC_INPUT 6 +#define TPS6507X_TSCMODE_DISABLE 7 + +#define TPS6507X_REG_ADRESULT_1 0X09 + +#define TPS6507X_REG_ADRESULT_2 0X0A +#define TPS6507X_REG_ADRESULT_2_MASK (BIT(1) | BIT(0)) + +#define TPS6507X_REG_PGOOD 0X0B + +#define TPS6507X_REG_PGOODMASK 0X0C + +#define TPS6507X_REG_CON_CTRL1 0X0D +#define TPS6507X_CON_CTRL1_DCDC1_ENABLE BIT(4) +#define TPS6507X_CON_CTRL1_DCDC2_ENABLE BIT(3) +#define TPS6507X_CON_CTRL1_DCDC3_ENABLE BIT(2) +#define TPS6507X_CON_CTRL1_LDO1_ENABLE BIT(1) +#define TPS6507X_CON_CTRL1_LDO2_ENABLE BIT(0) + +#define TPS6507X_REG_CON_CTRL2 0X0E + +#define TPS6507X_REG_CON_CTRL3 0X0F + +#define TPS6507X_REG_DEFDCDC1 0X10 +#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN BIT(7) +#define TPS6507X_DEFDCDC1_DCDC1_MASK 0X3F + +#define TPS6507X_REG_DEFDCDC2_LOW 0X11 +#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK 0X3F + +#define TPS6507X_REG_DEFDCDC2_HIGH 0X12 +#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK 0X3F + +#define TPS6507X_REG_DEFDCDC3_LOW 0X13 +#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK 0X3F + +#define TPS6507X_REG_DEFDCDC3_HIGH 0X14 +#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK 0X3F + +#define TPS6507X_REG_DEFSLEW 0X15 + +#define TPS6507X_REG_LDO_CTRL1 0X16 +#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK 0X0F + +#define TPS6507X_REG_DEFLDO2 0X17 +#define TPS6507X_REG_DEFLDO2_LDO2_MASK 0X3F + +#define TPS6507X_REG_WLED_CTRL1 0X18 + +#define TPS6507X_REG_WLED_CTRL2 0X19 + +/* VDCDC MASK */ +#define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F + +#endif /* __LINUX_MFD_TPS6507X_H */ -- cgit v1.2.3-59-g8ed1b From 0bc20bba357f18a0e52f45afc452d0b69cf06f76 Mon Sep 17 00:00:00 2001 From: Todd Fischer Date: Mon, 5 Apr 2010 20:23:57 -0600 Subject: mfd: Add tps6507x board data structure Add mfd structure which refrences sub-driver initialization data. For example, for a giving hardware implementation, the voltage regulator sub-driver initialization data provides the mapping betten a voltage regulator and what the output voltage is being used for. Signed-off-by: Todd Fischer Acked-by: Mark Brown Signed-off-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- arch/arm/mach-davinci/board-da850-evm.c | 7 ++++++- drivers/regulator/tps6507x-regulator.c | 14 ++++++++++++-- include/linux/mfd/tps6507x.h | 11 +++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index abd04932917b..95adfd42d0e6 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -533,10 +534,14 @@ struct regulator_init_data tps65070_regulator_data[] = { }, }; +static struct tps6507x_board tps_board = { + .tps6507x_pmic_init_data = &tps65070_regulator_data[0], +}; + static struct i2c_board_info __initdata da850evm_tps65070_info[] = { { I2C_BOARD_INFO("tps6507x", 0x48), - .platform_data = &tps65070_regulator_data[0], + .platform_data = &tps_board, }, }; diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 23c0597ab1f5..c3f1bf822fd0 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -489,6 +489,7 @@ static int __devinit tps_6507x_probe(struct i2c_client *client, struct regulator_init_data *init_data; struct regulator_dev *rdev; struct tps_pmic *tps; + struct tps6507x_board *tps_board; int i; int error; @@ -496,13 +497,22 @@ static int __devinit tps_6507x_probe(struct i2c_client *client, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; + /** + * tps_board points to pmic related constants + * coming from the board-evm file. + */ + + tps_board = dev_get_platdata(&client->dev); + if (!tps_board) + return -EINVAL; + /** * init_data points to array of regulator_init structures * coming from the board-evm file. */ - init_data = client->dev.platform_data; + init_data = tps_board->tps6507x_pmic_init_data; if (!init_data) - return -EIO; + return -EINVAL; tps = kzalloc(sizeof(*tps), GFP_KERNEL); if (!tps) diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h index 155bee1f7823..fd73af5fb95a 100644 --- a/include/linux/mfd/tps6507x.h +++ b/include/linux/mfd/tps6507x.h @@ -131,4 +131,15 @@ /* VDCDC MASK */ #define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F +/** + * struct tps6507x_board - packages regulator and touchscreen init data + * @tps6507x_regulator_data: regulator initialization values + * + * Board data may be used to initialize regulator and touchscreen. + */ + +struct tps6507x_board { + struct regulator_init_data *tps6507x_pmic_init_data; +}; + #endif /* __LINUX_MFD_TPS6507X_H */ -- cgit v1.2.3-59-g8ed1b From 31dd6a2672e337f5de188df3e5169ee732798236 Mon Sep 17 00:00:00 2001 From: Todd Fischer Date: Thu, 8 Apr 2010 09:04:55 +0200 Subject: mfd: Add TPS6507x support TPS6507x are multi function (PM, touchscreen) chipsets from TI. This commit also changes the corresponding regulator driver from being standalone to an MFD subdevice. Signed-off-by: Todd Fischer Acked-by: Mark Brown Signed-off-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 12 +++ drivers/mfd/Makefile | 1 + drivers/mfd/tps6507x.c | 156 +++++++++++++++++++++++++++++++++ drivers/regulator/tps6507x-regulator.c | 153 ++++++++++++++++---------------- include/linux/mfd/tps6507x.h | 22 +++++ 5 files changed, 265 insertions(+), 79 deletions(-) create mode 100644 drivers/mfd/tps6507x.c (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 05c137d16b04..40e0a0a7df5b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -116,6 +116,18 @@ config TPS65010 This driver can also be built as a module. If so, the module will be called tps65010. +config TPS6507X + tristate "TPS6507x Power Management / Touch Screen chips" + select MFD_CORE + depends on I2C + help + If you say yes here you get support for the TPS6507x series of + Power Management / Touch Screen chips. These include voltage + regulators, lithium ion/polymer battery charging, touch screen + and other features that are often used in portable devices. + This driver can also be built as a module. If so, the module + will be called tps6507x. + config MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 21eff85b288a..abf6f1766dcd 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_TPS65010) += tps65010.o +obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c new file mode 100644 index 000000000000..c59f74570b3b --- /dev/null +++ b/drivers/mfd/tps6507x.c @@ -0,0 +1,156 @@ +/* + * tps6507x.c -- TPS6507x chip family multi-function driver + * + * Copyright (c) 2010 RidgeRun (todd.fischer@ridgerun.com) + * + * Author: Todd Fischer + * todd.fischer@ridgerun.com + * + * Credits: + * + * Using code from wm831x-*.c, wm8400-core, Wolfson Microelectronics PLC. + * + * For licencing details see kernel-base/COPYING + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct mfd_cell tps6507x_devs[] = { + { + .name = "tps6507x-pmic", + }, +}; + + +static int tps6507x_i2c_read_device(struct tps6507x_dev *tps6507x, char reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = tps6507x->i2c_client; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = bytes; + xfer[1].buf = dest; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg, + int bytes, void *src) +{ + struct i2c_client *i2c = tps6507x->i2c_client; + /* we add 1 byte for device register */ + u8 msg[TPS6507X_MAX_REGISTER + 1]; + int ret; + + if (bytes > (TPS6507X_MAX_REGISTER + 1)) + return -EINVAL; + + msg[0] = reg; + memcpy(&msg[1], src, bytes); + + ret = i2c_master_send(i2c, msg, bytes + 1); + if (ret < 0) + return ret; + if (ret != bytes + 1) + return -EIO; + return 0; +} + +static int tps6507x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tps6507x_dev *tps6507x; + int ret = 0; + + tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL); + if (tps6507x == NULL) { + kfree(i2c); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, tps6507x); + tps6507x->dev = &i2c->dev; + tps6507x->i2c_client = i2c; + tps6507x->read_dev = tps6507x_i2c_read_device; + tps6507x->write_dev = tps6507x_i2c_write_device; + + ret = mfd_add_devices(tps6507x->dev, -1, + tps6507x_devs, ARRAY_SIZE(tps6507x_devs), + NULL, 0); + + if (ret < 0) + goto err; + + return ret; + +err: + mfd_remove_devices(tps6507x->dev); + kfree(tps6507x); + return ret; +} + +static int tps6507x_i2c_remove(struct i2c_client *i2c) +{ + struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c); + + mfd_remove_devices(tps6507x->dev); + kfree(tps6507x); + + return 0; +} + +static const struct i2c_device_id tps6507x_i2c_id[] = { + { "tps6507x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); + + +static struct i2c_driver tps6507x_i2c_driver = { + .driver = { + .name = "tps6507x", + .owner = THIS_MODULE, + }, + .probe = tps6507x_i2c_probe, + .remove = tps6507x_i2c_remove, + .id_table = tps6507x_i2c_id, +}; + +static int __init tps6507x_i2c_init(void) +{ + return i2c_add_driver(&tps6507x_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(tps6507x_i2c_init); + +static void __exit tps6507x_i2c_exit(void) +{ + i2c_del_driver(&tps6507x_i2c_driver); +} +module_exit(tps6507x_i2c_exit); + +MODULE_DESCRIPTION("TPS6507x chip family multi-function driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index ea44741fcd47..14b4576281c5 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -104,22 +103,67 @@ struct tps_info { const u16 *table; }; +static const struct tps_info tps6507x_pmic_regs[] = { + { + .name = "VDCDC1", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC2", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "VDCDC3", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), + .table = VDCDCx_VSEL_table, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO1_VSEL_table), + .table = LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 725000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(LDO2_VSEL_table), + .table = LDO2_VSEL_table, + }, +}; + struct tps6507x_pmic { struct regulator_desc desc[TPS6507X_NUM_REGULATOR]; - struct i2c_client *client; + struct tps6507x_dev *mfd; struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR]; const struct tps_info *info[TPS6507X_NUM_REGULATOR]; struct mutex io_lock; }; - static inline int tps6507x_pmic_read(struct tps6507x_pmic *tps, u8 reg) { - return i2c_smbus_read_byte_data(tps->client, reg); + u8 val; + int err; + + err = tps->mfd->read_dev(tps->mfd, reg, 1, &val); + + if (err) + return err; + + return val; } static inline int tps6507x_pmic_write(struct tps6507x_pmic *tps, u8 reg, u8 val) { - return i2c_smbus_write_byte_data(tps->client, reg, val); + return tps->mfd->write_dev(tps->mfd, reg, 1, &val); } static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) @@ -130,7 +174,7 @@ static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) data = tps6507x_pmic_read(tps, reg); if (data < 0) { - dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); err = data; goto out; } @@ -138,7 +182,7 @@ static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) data |= mask; err = tps6507x_pmic_write(tps, reg, data); if (err) - dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); out: mutex_unlock(&tps->io_lock); @@ -153,7 +197,7 @@ static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) data = tps6507x_pmic_read(tps, reg); if (data < 0) { - dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); err = data; goto out; } @@ -161,7 +205,7 @@ static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask) data &= ~mask; err = tps6507x_pmic_write(tps, reg, data); if (err) - dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); out: mutex_unlock(&tps->io_lock); @@ -176,7 +220,7 @@ static int tps6507x_pmic_reg_read(struct tps6507x_pmic *tps, u8 reg) data = tps6507x_pmic_read(tps, reg); if (data < 0) - dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg); mutex_unlock(&tps->io_lock); return data; @@ -190,7 +234,7 @@ static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val) err = tps6507x_pmic_write(tps, reg, val); if (err < 0) - dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg); + dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg); mutex_unlock(&tps->io_lock); return err; @@ -483,11 +527,12 @@ static struct regulator_ops tps6507x_pmic_ldo_ops = { .list_voltage = tps6507x_pmic_ldo_list_voltage, }; -static int __devinit tps6507x_pmic_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static __devinit +int tps6507x_pmic_probe(struct platform_device *pdev) { + struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); static int desc_id; - const struct tps_info *info = (void *)id->driver_data; + const struct tps_info *info = &tps6507x_pmic_regs[0]; struct regulator_init_data *init_data; struct regulator_dev *rdev; struct tps6507x_pmic *tps; @@ -495,16 +540,12 @@ static int __devinit tps6507x_pmic_probe(struct i2c_client *client, int i; int error; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - /** * tps_board points to pmic related constants * coming from the board-evm file. */ - tps_board = dev_get_platdata(&client->dev); + tps_board = dev_get_platdata(tps6507x_dev->dev); if (!tps_board) return -EINVAL; @@ -523,7 +564,7 @@ static int __devinit tps6507x_pmic_probe(struct i2c_client *client, mutex_init(&tps->io_lock); /* common for all regulators */ - tps->client = client; + tps->mfd = tps6507x_dev; for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) { /* Register the regulators */ @@ -537,10 +578,11 @@ static int __devinit tps6507x_pmic_probe(struct i2c_client *client, tps->desc[i].owner = THIS_MODULE; rdev = regulator_register(&tps->desc[i], - &client->dev, init_data, tps); + tps6507x_dev->dev, init_data, tps); if (IS_ERR(rdev)) { - dev_err(&client->dev, "failed to register %s\n", - id->name); + dev_err(tps6507x_dev->dev, + "failed to register %s regulator\n", + pdev->name); error = PTR_ERR(rdev); goto fail; } @@ -549,7 +591,7 @@ static int __devinit tps6507x_pmic_probe(struct i2c_client *client, tps->rdev[i] = rdev; } - i2c_set_clientdata(client, tps); + tps6507x_dev->pmic = tps; return 0; @@ -567,14 +609,12 @@ fail: * * Unregister TPS driver as an i2c client device driver */ -static int __devexit tps6507x_pmic_remove(struct i2c_client *client) +static int __devexit tps6507x_pmic_remove(struct platform_device *pdev) { - struct tps6507x_pmic *tps = i2c_get_clientdata(client); + struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); + struct tps6507x_pmic *tps = tps6507x_dev->pmic; int i; - /* clear the client data in i2c */ - i2c_set_clientdata(client, NULL); - for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); @@ -583,59 +623,13 @@ static int __devexit tps6507x_pmic_remove(struct i2c_client *client) return 0; } -static const struct tps_info tps6507x_pmic_regs[] = { - { - .name = "VDCDC1", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), - .table = VDCDCx_VSEL_table, - }, - { - .name = "VDCDC2", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), - .table = VDCDCx_VSEL_table, - }, - { - .name = "VDCDC3", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), - .table = VDCDCx_VSEL_table, - }, - { - .name = "LDO1", - .min_uV = 1000000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(LDO1_VSEL_table), - .table = LDO1_VSEL_table, - }, - { - .name = "LDO2", - .min_uV = 725000, - .max_uV = 3300000, - .table_len = ARRAY_SIZE(LDO2_VSEL_table), - .table = LDO2_VSEL_table, - }, -}; - -static const struct i2c_device_id tps6507x_pmic_id[] = { - {.name = "tps6507x", - .driver_data = (unsigned long) tps6507x_pmic_regs,}, - { }, -}; -MODULE_DEVICE_TABLE(i2c, tps6507x_pmic_id); - -static struct i2c_driver tps6507x_i2c_driver = { +static struct platform_driver tps6507x_pmic_driver = { .driver = { - .name = "tps6507x", + .name = "tps6507x-pmic", .owner = THIS_MODULE, }, .probe = tps6507x_pmic_probe, .remove = __devexit_p(tps6507x_pmic_remove), - .id_table = tps6507x_pmic_id, }; /** @@ -645,7 +639,7 @@ static struct i2c_driver tps6507x_i2c_driver = { */ static int __init tps6507x_pmic_init(void) { - return i2c_add_driver(&tps6507x_i2c_driver); + return platform_driver_register(&tps6507x_pmic_driver); } subsys_initcall(tps6507x_pmic_init); @@ -656,10 +650,11 @@ subsys_initcall(tps6507x_pmic_init); */ static void __exit tps6507x_pmic_cleanup(void) { - i2c_del_driver(&tps6507x_i2c_driver); + platform_driver_unregister(&tps6507x_pmic_driver); } module_exit(tps6507x_pmic_cleanup); MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("TPS6507x voltage regulator driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6507x-pmic"); diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h index fd73af5fb95a..9543cb716428 100644 --- a/include/linux/mfd/tps6507x.h +++ b/include/linux/mfd/tps6507x.h @@ -131,6 +131,8 @@ /* VDCDC MASK */ #define TPS6507X_DEFDCDCX_DCDC_MASK 0X3F +#define TPS6507X_MAX_REGISTER 0X19 + /** * struct tps6507x_board - packages regulator and touchscreen init data * @tps6507x_regulator_data: regulator initialization values @@ -142,4 +144,24 @@ struct tps6507x_board { struct regulator_init_data *tps6507x_pmic_init_data; }; +/** + * struct tps6507x_dev - tps6507x sub-driver chip access routines + * @read_dev() - I2C register read function + * @write_dev() - I2C register write function + * + * Device data may be used to access the TPS6507x chip + */ + +struct tps6507x_dev { + struct device *dev; + struct i2c_client *i2c_client; + int (*read_dev)(struct tps6507x_dev *tps6507x, char reg, int size, + void *dest); + int (*write_dev)(struct tps6507x_dev *tps6507x, char reg, int size, + void *src); + + /* Client devices */ + struct tps6507x_pmic *pmic; +}; + #endif /* __LINUX_MFD_TPS6507X_H */ -- cgit v1.2.3-59-g8ed1b From 752599667048898b6969e06e4637f906b04ec752 Mon Sep 17 00:00:00 2001 From: Todd Fischer Date: Mon, 5 Apr 2010 17:53:12 -0600 Subject: input: Touchscreen driver for TPS6507x Add touch screen input driver for TPS6507x family of multi-function chips. Uses the TPS6507x MFD driver. No interrupt support due to testing limitations of current hardware. Signed-off-by: Todd Fischer Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/Kconfig | 13 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/tps6507x-ts.c | 400 ++++++++++++++++++++++++++++++++ drivers/mfd/tps6507x.c | 3 + include/linux/input/tps6507x-ts.h | 24 ++ include/linux/mfd/tps6507x.h | 2 + 6 files changed, 443 insertions(+) create mode 100644 drivers/input/touchscreen/tps6507x-ts.c create mode 100644 include/linux/input/tps6507x-ts.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b9f58ca82fd1..6703c6b9800a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -590,4 +590,17 @@ config TOUCHSCREEN_PCAP To compile this driver as a module, choose M here: the module will be called pcap_ts. + +config TOUCHSCREEN_TPS6507X + tristate "TPS6507x based touchscreens" + depends on I2C + help + Say Y here if you have a TPS6507x based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tps6507x_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 8ad36eef90a2..497964a7a214 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -46,3 +46,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c new file mode 100644 index 000000000000..5de80a1a730b --- /dev/null +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -0,0 +1,400 @@ +/* + * drivers/input/touchscreen/tps6507x_ts.c + * + * Touchscreen driver for the tps6507x chip. + * + * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) + * + * Credits: + * + * Using code from tsc2007, MtekVision Co., Ltd. + * + * For licencing details see kernel-base/COPYING + * + * TPS65070, TPS65073, TPS650731, and TPS650732 support + * 10 bit touch screen interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */ +#define TPS_DEFAULT_MIN_PRESSURE 0x30 +#define MAX_10BIT ((1 << 10) - 1) + +#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \ + TPS6507X_ADCONFIG_START_CONVERSION | \ + TPS6507X_ADCONFIG_INPUT_REAL_TSC) +#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC) + +struct ts_event { + u16 x; + u16 y; + u16 pressure; +}; + +struct tps6507x_ts { + struct input_dev *input_dev; + struct device *dev; + char phys[32]; + struct workqueue_struct *wq; + struct delayed_work work; + unsigned polling; /* polling is active */ + struct ts_event tc; + struct tps6507x_dev *mfd; + u16 model; + unsigned pendown; + int irq; + void (*clear_penirq)(void); + unsigned long poll_period; /* ms */ + u16 min_pressure; + int vref; /* non-zero to leave vref on */ +}; + +static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) +{ + int err; + + err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data); + + if (err) + return err; + + return 0; +} + +static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data) +{ + return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data); +} + +static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc, + u8 tsc_mode, u16 *value) +{ + s32 ret; + u8 adc_status; + u8 result; + + /* Route input signal to A/D converter */ + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode); + if (ret) { + dev_err(tsc->dev, "TSC mode read failed\n"); + goto err; + } + + /* Start A/D conversion */ + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, + TPS6507X_ADCONFIG_CONVERT_TS); + if (ret) { + dev_err(tsc->dev, "ADC config write failed\n"); + return ret; + } + + do { + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG, + &adc_status); + if (ret) { + dev_err(tsc->dev, "ADC config read failed\n"); + goto err; + } + } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION); + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result); + if (ret) { + dev_err(tsc->dev, "ADC result 2 read failed\n"); + goto err; + } + + *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8; + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result); + if (ret) { + dev_err(tsc->dev, "ADC result 1 read failed\n"); + goto err; + } + + *value |= result; + + dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value); + +err: + return ret; +} + +/* Need to call tps6507x_adc_standby() after using A/D converter for the + * touch screen interrupt to work properly. + */ + +static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) +{ + s32 ret; + s32 loops = 0; + u8 val; + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, + TPS6507X_ADCONFIG_INPUT_TSC); + if (ret) + return ret; + + ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, + TPS6507X_TSCMODE_STANDBY); + if (ret) + return ret; + + ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); + if (ret) + return ret; + + while (val & TPS6507X_REG_TSC_INT) { + mdelay(10); + ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); + if (ret) + return ret; + loops++; + } + + return ret; +} + +static void tps6507x_ts_handler(struct work_struct *work) +{ + struct tps6507x_ts *tsc = container_of(work, + struct tps6507x_ts, work.work); + struct input_dev *input_dev = tsc->input_dev; + int pendown; + int schd; + int poll = 0; + s32 ret; + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, + &tsc->tc.pressure); + if (ret) + goto done; + + pendown = tsc->tc.pressure > tsc->min_pressure; + + if (unlikely(!pendown && tsc->pendown)) { + dev_dbg(tsc->dev, "UP\n"); + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + tsc->pendown = 0; + } + + if (pendown) { + + if (!tsc->pendown) { + dev_dbg(tsc->dev, "DOWN\n"); + input_report_key(input_dev, BTN_TOUCH, 1); + } else + dev_dbg(tsc->dev, "still down\n"); + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION, + &tsc->tc.x); + if (ret) + goto done; + + ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION, + &tsc->tc.y); + if (ret) + goto done; + + input_report_abs(input_dev, ABS_X, tsc->tc.x); + input_report_abs(input_dev, ABS_Y, tsc->tc.y); + input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure); + input_sync(input_dev); + tsc->pendown = 1; + poll = 1; + } + +done: + /* always poll if not using interrupts */ + poll = 1; + + if (poll) { + schd = queue_delayed_work(tsc->wq, &tsc->work, + tsc->poll_period * HZ / 1000); + if (schd) + tsc->polling = 1; + else { + tsc->polling = 0; + dev_err(tsc->dev, "re-schedule failed"); + } + } else + tsc->polling = 0; + + ret = tps6507x_adc_standby(tsc); +} + +static int tps6507x_ts_probe(struct platform_device *pdev) +{ + int error; + struct tps6507x_ts *tsc; + struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); + struct touchscreen_init_data *init_data; + struct input_dev *input_dev; + struct tps6507x_board *tps_board; + int schd; + + /** + * tps_board points to pmic related constants + * coming from the board-evm file. + */ + + tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data; + + if (!tps_board) { + dev_err(tps6507x_dev->dev, + "Could not find tps6507x platform data\n"); + return -EIO; + } + + /** + * init_data points to array of regulator_init structures + * coming from the board-evm file. + */ + + init_data = tps_board->tps6507x_ts_init_data; + + tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL); + if (!tsc) { + dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto err0; + } + + tps6507x_dev->ts = tsc; + tsc->mfd = tps6507x_dev; + tsc->dev = tps6507x_dev->dev; + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(tsc->dev, "Failed to allocate input device.\n"); + error = -ENOMEM; + goto err1; + } + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); + + input_dev->name = "TPS6507x Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = tsc->dev; + + snprintf(tsc->phys, sizeof(tsc->phys), + "%s/input0", dev_name(tsc->dev)); + input_dev->phys = tsc->phys; + + dev_dbg(tsc->dev, "device: %s\n", input_dev->phys); + + input_set_drvdata(input_dev, tsc); + + tsc->input_dev = input_dev; + + INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler); + tsc->wq = create_workqueue("TPS6507x Touchscreen"); + + if (init_data) { + tsc->poll_period = init_data->poll_period; + tsc->vref = init_data->vref; + tsc->min_pressure = init_data->min_pressure; + input_dev->id.vendor = init_data->vendor; + input_dev->id.product = init_data->product; + input_dev->id.version = init_data->version; + } else { + tsc->poll_period = TSC_DEFAULT_POLL_PERIOD; + tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE; + } + + error = tps6507x_adc_standby(tsc); + if (error) + goto err2; + + error = input_register_device(input_dev); + if (error) + goto err2; + + schd = queue_delayed_work(tsc->wq, &tsc->work, + tsc->poll_period * HZ / 1000); + + if (schd) + tsc->polling = 1; + else { + tsc->polling = 0; + dev_err(tsc->dev, "schedule failed"); + goto err2; + } + + return 0; + +err2: + cancel_delayed_work(&tsc->work); + flush_workqueue(tsc->wq); + destroy_workqueue(tsc->wq); + tsc->wq = 0; + input_free_device(input_dev); +err1: + kfree(tsc); + tps6507x_dev->ts = NULL; +err0: + return error; +} + +static int __devexit tps6507x_ts_remove(struct platform_device *pdev) +{ + struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); + struct tps6507x_ts *tsc = tps6507x_dev->ts; + struct input_dev *input_dev = tsc->input_dev; + + if (!tsc) + return 0; + + cancel_delayed_work(&tsc->work); + flush_workqueue(tsc->wq); + destroy_workqueue(tsc->wq); + tsc->wq = 0; + + input_free_device(input_dev); + + tps6507x_dev->ts = NULL; + kfree(tsc); + + return 0; +} + +static struct platform_driver tps6507x_ts_driver = { + .driver = { + .name = "tps6507x-ts", + .owner = THIS_MODULE, + }, + .probe = tps6507x_ts_probe, + .remove = __devexit_p(tps6507x_ts_remove), +}; + +static int __init tps6507x_ts_init(void) +{ + return platform_driver_register(&tps6507x_ts_driver); +} +module_init(tps6507x_ts_init); + +static void __exit tps6507x_ts_exit(void) +{ + platform_driver_unregister(&tps6507x_ts_driver); +} +module_exit(tps6507x_ts_exit); + +MODULE_AUTHOR("Todd Fischer "); +MODULE_DESCRIPTION("TPS6507x - TouchScreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6507x-tsc"); diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index c59f74570b3b..d859dffed39f 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -26,6 +26,9 @@ static struct mfd_cell tps6507x_devs[] = { { .name = "tps6507x-pmic", }, + { + .name = "tps6507x-ts", + }, }; diff --git a/include/linux/input/tps6507x-ts.h b/include/linux/input/tps6507x-ts.h new file mode 100644 index 000000000000..ab1440313924 --- /dev/null +++ b/include/linux/input/tps6507x-ts.h @@ -0,0 +1,24 @@ +/* linux/i2c/tps6507x-ts.h + * + * Functions to access TPS65070 touch screen chip. + * + * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) + * + * + * For licencing details see kernel-base/COPYING + */ + +#ifndef __LINUX_I2C_TPS6507X_TS_H +#define __LINUX_I2C_TPS6507X_TS_H + +/* Board specific touch screen initial values */ +struct touchscreen_init_data { + int poll_period; /* ms */ + int vref; /* non-zero to leave vref on */ + __u16 min_pressure; /* min reading to be treated as a touch */ + __u16 vendor; + __u16 product; + __u16 version; +}; + +#endif /* __LINUX_I2C_TPS6507X_TS_H */ diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h index 9543cb716428..c923e4864f55 100644 --- a/include/linux/mfd/tps6507x.h +++ b/include/linux/mfd/tps6507x.h @@ -142,6 +142,7 @@ struct tps6507x_board { struct regulator_init_data *tps6507x_pmic_init_data; + struct touchscreen_init_data *tps6507x_ts_init_data; }; /** @@ -162,6 +163,7 @@ struct tps6507x_dev { /* Client devices */ struct tps6507x_pmic *pmic; + struct tps6507x_ts *ts; }; #endif /* __LINUX_MFD_TPS6507X_H */ -- cgit v1.2.3-59-g8ed1b From b03b4d7cdd8f18395494478634707c6f132080d6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 8 Apr 2010 10:02:39 +0200 Subject: mfd: Ensure WM831x charger interrupts are acknowledged when suspending The charger interrupts on the WM831x are unconditionally a wake source for the system. If the power driver is not able to monitor them (for example, due to the IRQ line not having been wired up on the system) then any charger interrupt will prevent the system suspending for any meaningful amount of time since nothing will ack them. Avoid this issue by manually acknowledging these interrupts when we suspend the WM831x core device if they are masked. If software is actually using the interrupts then they will be unmasked and this change will have no effect. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 47 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm831x/core.h | 5 +++-- 2 files changed, 50 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index da00ca9e0c71..1a968f34d679 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1494,6 +1494,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8310: parent = WM8310; wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; if (rev > 0) { wm831x->has_gpio_ena = 1; wm831x->has_cs_sts = 1; @@ -1505,6 +1506,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8311: parent = WM8311; wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; if (rev > 0) { wm831x->has_gpio_ena = 1; wm831x->has_cs_sts = 1; @@ -1516,6 +1518,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8312: parent = WM8312; wm831x->num_gpio = 16; + wm831x->charger_irq_wake = 1; if (rev > 0) { wm831x->has_gpio_ena = 1; wm831x->has_cs_sts = 1; @@ -1654,6 +1657,42 @@ static void wm831x_device_exit(struct wm831x *wm831x) kfree(wm831x); } +static int wm831x_device_suspend(struct wm831x *wm831x) +{ + int reg, mask; + + /* If the charger IRQs are a wake source then make sure we ack + * them even if they're not actively being used (eg, no power + * driver or no IRQ line wired up) then acknowledge the + * interrupts otherwise suspend won't last very long. + */ + if (wm831x->charger_irq_wake) { + reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK); + + mask = WM831X_CHG_BATT_HOT_EINT | + WM831X_CHG_BATT_COLD_EINT | + WM831X_CHG_BATT_FAIL_EINT | + WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT | + WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT | + WM831X_CHG_START_EINT; + + /* If any of the interrupts are masked read the statuses */ + if (reg & mask) + reg = wm831x_reg_read(wm831x, + WM831X_INTERRUPT_STATUS_2); + + if (reg & mask) { + dev_info(wm831x->dev, + "Acknowledging masked charger IRQs: %x\n", + reg & mask); + wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2, + reg & mask); + } + } + + return 0; +} + static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, int bytes, void *dest) { @@ -1728,6 +1767,13 @@ static int wm831x_i2c_remove(struct i2c_client *i2c) return 0; } +static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + return wm831x_device_suspend(wm831x); +} + static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8310", WM8310 }, { "wm8311", WM8311 }, @@ -1745,6 +1791,7 @@ static struct i2c_driver wm831x_i2c_driver = { }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, + .suspend = wm831x_i2c_suspend, .id_table = wm831x_i2c_id, }; diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 5915f6e3d9ab..eb5bd4e0e03c 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -256,8 +256,9 @@ struct wm831x { int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ /* Chip revision based flags */ - unsigned has_gpio_ena:1; /* Has GPIO enable bit */ - unsigned has_cs_sts:1; /* Has current sink status bit */ + unsigned has_gpio_ena:1; /* Has GPIO enable bit */ + unsigned has_cs_sts:1; /* Has current sink status bit */ + unsigned charger_irq_wake:1; /* Are charger IRQs a wake source? */ int num_gpio; -- cgit v1.2.3-59-g8ed1b From b4ecd326b789f1029c5d4a5239d9bd12ecac353d Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Mon, 10 May 2010 23:39:47 +0200 Subject: mfd: Add Toshiba's TC35892 MFD core The TC35892 I/O Expander provides 24 GPIOs, a keypad controller, timers, and a rotator wheel interface. This patch adds the MFD core. Acked-by: Linus Walleij Signed-off-by: Rabin Vincent Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 11 ++ drivers/mfd/Makefile | 1 + drivers/mfd/tc35892.c | 347 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tc35892.h | 132 +++++++++++++++++ 4 files changed, 491 insertions(+) create mode 100644 drivers/mfd/tc35892.c create mode 100644 include/linux/mfd/tc35892.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 40e0a0a7df5b..614fa7b8a054 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -171,6 +171,17 @@ config TWL4030_CODEC select MFD_CORE default n +config MFD_TC35892 + bool "Support Toshiba TC35892" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + help + Support for the Toshiba TC35892 I/O Expander. + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config MFD_TMIO bool default n diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index abf6f1766dcd..5eb79794d5a5 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o +obj-$(CONFIG_MFD_TC35892) += tc35892.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c new file mode 100644 index 000000000000..715f095dd7a6 --- /dev/null +++ b/drivers/mfd/tc35892.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad for ST-Ericsson + * Author: Rabin Vincent for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * tc35892_reg_read() - read a single TC35892 register + * @tc35892: Device to read from + * @reg: Register to read + */ +int tc35892_reg_read(struct tc35892 *tc35892, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(tc35892->i2c, reg); + if (ret < 0) + dev_err(tc35892->dev, "failed to read reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_reg_read); + +/** + * tc35892_reg_read() - write a single TC35892 register + * @tc35892: Device to write to + * @reg: Register to read + * @data: Value to write + */ +int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data); + if (ret < 0) + dev_err(tc35892->dev, "failed to write reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_reg_write); + +/** + * tc35892_block_read() - read multiple TC35892 registers + * @tc35892: Device to read from + * @reg: First register + * @length: Number of registers + * @values: Buffer to write to + */ +int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values); + if (ret < 0) + dev_err(tc35892->dev, "failed to read regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_block_read); + +/** + * tc35892_block_write() - write multiple TC35892 registers + * @tc35892: Device to write to + * @reg: First register + * @length: Number of registers + * @values: Values to write + */ +int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, + const u8 *values) +{ + int ret; + + ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length, + values); + if (ret < 0) + dev_err(tc35892->dev, "failed to write regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_block_write); + +/** + * tc35892_set_bits() - set the value of a bitfield in a TC35892 register + * @tc35892: Device to write to + * @reg: Register to write + * @mask: Mask of bits to set + * @values: Value to set + */ +int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val) +{ + int ret; + + mutex_lock(&tc35892->lock); + + ret = tc35892_reg_read(tc35892, reg); + if (ret < 0) + goto out; + + ret &= ~mask; + ret |= val; + + ret = tc35892_reg_write(tc35892, reg, ret); + +out: + mutex_unlock(&tc35892->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tc35892_set_bits); + +static struct resource gpio_resources[] = { + { + .start = TC35892_INT_GPIIRQ, + .end = TC35892_INT_GPIIRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell tc35892_devs[] = { + { + .name = "tc35892-gpio", + .num_resources = ARRAY_SIZE(gpio_resources), + .resources = &gpio_resources[0], + }, +}; + +static irqreturn_t tc35892_irq(int irq, void *data) +{ + struct tc35892 *tc35892 = data; + int status; + + status = tc35892_reg_read(tc35892, TC35892_IRQST); + if (status < 0) + return IRQ_NONE; + + while (status) { + int bit = __ffs(status); + + handle_nested_irq(tc35892->irq_base + bit); + status &= ~(1 << bit); + } + + /* + * A dummy read or write (to any register) appears to be necessary to + * have the last interrupt clear (for example, GPIO IC write) take + * effect. + */ + tc35892_reg_read(tc35892, TC35892_IRQST); + + return IRQ_HANDLED; +} + +static void tc35892_irq_dummy(unsigned int irq) +{ + /* No mask/unmask at this level */ +} + +static struct irq_chip tc35892_irq_chip = { + .name = "tc35892", + .mask = tc35892_irq_dummy, + .unmask = tc35892_irq_dummy, +}; + +static int tc35892_irq_init(struct tc35892 *tc35892) +{ + int base = tc35892->irq_base; + int irq; + + for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { + set_irq_chip_data(irq, tc35892); + set_irq_chip_and_handler(irq, &tc35892_irq_chip, + handle_edge_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void tc35892_irq_remove(struct tc35892 *tc35892) +{ + int base = tc35892->irq_base; + int irq; + + for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int tc35892_chip_init(struct tc35892 *tc35892) +{ + int manf, ver, ret; + + manf = tc35892_reg_read(tc35892, TC35892_MANFCODE); + if (manf < 0) + return manf; + + ver = tc35892_reg_read(tc35892, TC35892_VERSION); + if (ver < 0) + return ver; + + if (manf != TC35892_MANFCODE_MAGIC) { + dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf); + return -EINVAL; + } + + dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver); + + /* Put everything except the IRQ module into reset */ + ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL, + TC35892_RSTCTRL_TIMRST + | TC35892_RSTCTRL_ROTRST + | TC35892_RSTCTRL_KBDRST + | TC35892_RSTCTRL_GPIRST); + if (ret < 0) + return ret; + + /* Clear the reset interrupt. */ + return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1); +} + +static int __devinit tc35892_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tc35892_platform_data *pdata = i2c->dev.platform_data; + struct tc35892 *tc35892; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL); + if (!tc35892) + return -ENOMEM; + + mutex_init(&tc35892->lock); + + tc35892->dev = &i2c->dev; + tc35892->i2c = i2c; + tc35892->pdata = pdata; + tc35892->irq_base = pdata->irq_base; + tc35892->num_gpio = id->driver_data; + + i2c_set_clientdata(i2c, tc35892); + + ret = tc35892_chip_init(tc35892); + if (ret) + goto out_free; + + ret = tc35892_irq_init(tc35892); + if (ret) + goto out_free; + + ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tc35892", tc35892); + if (ret) { + dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret); + goto out_removeirq; + } + + ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs, + ARRAY_SIZE(tc35892_devs), NULL, + tc35892->irq_base); + if (ret) { + dev_err(tc35892->dev, "failed to add children\n"); + goto out_freeirq; + } + + return 0; + +out_freeirq: + free_irq(tc35892->i2c->irq, tc35892); +out_removeirq: + tc35892_irq_remove(tc35892); +out_free: + i2c_set_clientdata(i2c, NULL); + kfree(tc35892); + return ret; +} + +static int __devexit tc35892_remove(struct i2c_client *client) +{ + struct tc35892 *tc35892 = i2c_get_clientdata(client); + + mfd_remove_devices(tc35892->dev); + + free_irq(tc35892->i2c->irq, tc35892); + tc35892_irq_remove(tc35892); + + i2c_set_clientdata(client, NULL); + kfree(tc35892); + + return 0; +} + +static const struct i2c_device_id tc35892_id[] = { + { "tc35892", 24 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc35892_id); + +static struct i2c_driver tc35892_driver = { + .driver.name = "tc35892", + .driver.owner = THIS_MODULE, + .probe = tc35892_probe, + .remove = __devexit_p(tc35892_remove), + .id_table = tc35892_id, +}; + +static int __init tc35892_init(void) +{ + return i2c_add_driver(&tc35892_driver); +} +subsys_initcall(tc35892_init); + +static void __exit tc35892_exit(void) +{ + i2c_del_driver(&tc35892_driver); +} +module_exit(tc35892_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC35892 MFD core driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/include/linux/mfd/tc35892.h b/include/linux/mfd/tc35892.h new file mode 100644 index 000000000000..e47f770d3068 --- /dev/null +++ b/include/linux/mfd/tc35892.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + */ + +#ifndef __LINUX_MFD_TC35892_H +#define __LINUX_MFD_TC35892_H + +#include + +#define TC35892_RSTCTRL_IRQRST (1 << 4) +#define TC35892_RSTCTRL_TIMRST (1 << 3) +#define TC35892_RSTCTRL_ROTRST (1 << 2) +#define TC35892_RSTCTRL_KBDRST (1 << 1) +#define TC35892_RSTCTRL_GPIRST (1 << 0) + +#define TC35892_IRQST 0x91 + +#define TC35892_MANFCODE_MAGIC 0x03 +#define TC35892_MANFCODE 0x80 +#define TC35892_VERSION 0x81 +#define TC35892_IOCFG 0xA7 + +#define TC35892_CLKMODE 0x88 +#define TC35892_CLKCFG 0x89 +#define TC35892_CLKEN 0x8A + +#define TC35892_RSTCTRL 0x82 +#define TC35892_EXTRSTN 0x83 +#define TC35892_RSTINTCLR 0x84 + +#define TC35892_GPIOIS0 0xC9 +#define TC35892_GPIOIS1 0xCA +#define TC35892_GPIOIS2 0xCB +#define TC35892_GPIOIBE0 0xCC +#define TC35892_GPIOIBE1 0xCD +#define TC35892_GPIOIBE2 0xCE +#define TC35892_GPIOIEV0 0xCF +#define TC35892_GPIOIEV1 0xD0 +#define TC35892_GPIOIEV2 0xD1 +#define TC35892_GPIOIE0 0xD2 +#define TC35892_GPIOIE1 0xD3 +#define TC35892_GPIOIE2 0xD4 +#define TC35892_GPIORIS0 0xD6 +#define TC35892_GPIORIS1 0xD7 +#define TC35892_GPIORIS2 0xD8 +#define TC35892_GPIOMIS0 0xD9 +#define TC35892_GPIOMIS1 0xDA +#define TC35892_GPIOMIS2 0xDB +#define TC35892_GPIOIC0 0xDC +#define TC35892_GPIOIC1 0xDD +#define TC35892_GPIOIC2 0xDE + +#define TC35892_GPIODATA0 0xC0 +#define TC35892_GPIOMASK0 0xc1 +#define TC35892_GPIODATA1 0xC2 +#define TC35892_GPIOMASK1 0xc3 +#define TC35892_GPIODATA2 0xC4 +#define TC35892_GPIOMASK2 0xC5 + +#define TC35892_GPIODIR0 0xC6 +#define TC35892_GPIODIR1 0xC7 +#define TC35892_GPIODIR2 0xC8 + +#define TC35892_GPIOSYNC0 0xE6 +#define TC35892_GPIOSYNC1 0xE7 +#define TC35892_GPIOSYNC2 0xE8 + +#define TC35892_GPIOWAKE0 0xE9 +#define TC35892_GPIOWAKE1 0xEA +#define TC35892_GPIOWAKE2 0xEB + +#define TC35892_GPIOODM0 0xE0 +#define TC35892_GPIOODE0 0xE1 +#define TC35892_GPIOODM1 0xE2 +#define TC35892_GPIOODE1 0xE3 +#define TC35892_GPIOODM2 0xE4 +#define TC35892_GPIOODE2 0xE5 + +#define TC35892_INT_GPIIRQ 0 +#define TC35892_INT_TI0IRQ 1 +#define TC35892_INT_TI1IRQ 2 +#define TC35892_INT_TI2IRQ 3 +#define TC35892_INT_ROTIRQ 5 +#define TC35892_INT_KBDIRQ 6 +#define TC35892_INT_PORIRQ 7 + +#define TC35892_NR_INTERNAL_IRQS 8 +#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x)) + +struct tc35892 { + struct mutex lock; + struct device *dev; + struct i2c_client *i2c; + + int irq_base; + int num_gpio; + struct tc35892_platform_data *pdata; +}; + +extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data); +extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg); +extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, + u8 *values); +extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, + const u8 *values); +extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val); + +/** + * struct tc35892_gpio_platform_data - TC35892 GPIO platform data + * @gpio_base: first gpio number assigned to TC35892. A maximum of + * %TC35892_NR_GPIOS GPIOs will be allocated. + */ +struct tc35892_gpio_platform_data { + int gpio_base; +}; + +/** + * struct tc35892_platform_data - TC35892 platform data + * @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used. + * @gpio: GPIO-specific platform data + */ +struct tc35892_platform_data { + int irq_base; + struct tc35892_gpio_platform_data *gpio; +}; + +#define TC35892_NR_GPIOS 24 +#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS) + +#endif -- cgit v1.2.3-59-g8ed1b From 812f9e9d424dde9ccb35975c0281edb6f8543735 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 1 May 2010 18:26:07 +0200 Subject: mfd: Renamed ab3100.h to abx500.h The goal here is to make way for a more general interface for the analog baseband chips ab3100 ab3550 ab550 and future chips. This patch have been divided into two parts since both changing name and content of a file is not recommended in git. Signed-off-by: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- arch/arm/mach-u300/i2c.c | 2 +- drivers/mfd/ab3100-core.c | 2 +- drivers/mfd/ab3100-otp.c | 2 +- drivers/regulator/ab3100.c | 2 +- drivers/rtc/rtc-ab3100.c | 2 +- include/linux/mfd/ab3100.h | 129 --------------------------------------------- include/linux/mfd/abx500.h | 129 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 134 deletions(-) delete mode 100644 include/linux/mfd/ab3100.h create mode 100644 include/linux/mfd/abx500.h (limited to 'include/linux') diff --git a/arch/arm/mach-u300/i2c.c b/arch/arm/mach-u300/i2c.c index c73ed06b6065..d893ee035c23 100644 --- a/arch/arm/mach-u300/i2c.c +++ b/arch/arm/mach-u300/i2c.c @@ -9,7 +9,7 @@ */ #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 16898211cd1a..f8c4a333ff1c 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include /* These are the only registers inside AB3100 used in this main file */ diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 2d14655fdebd..7093f1aef1e5 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 1afd008ca957..04ca7f62c57e 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include /* LDO registers and some handy masking definitions for AB3100 */ #define AB3100_LDO_A 0x40 diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index 4704aac2b5af..b46b85d581b0 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include /* Clock rate in Hz */ #define AB3100_RTC_CLOCK_RATE 32768 diff --git a/include/linux/mfd/ab3100.h b/include/linux/mfd/ab3100.h deleted file mode 100644 index 9a881c305a50..000000000000 --- a/include/linux/mfd/ab3100.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2007-2009 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * AB3100 core access functions - * Author: Linus Walleij - */ - -#include -#include - -#ifndef MFD_AB3100_H -#define MFD_AB3100_H - -#define ABUNKNOWN 0 -#define AB3000 1 -#define AB3100 2 - -/* - * AB3100, EVENTA1, A2 and A3 event register flags - * these are catenated into a single 32-bit flag in the code - * for event notification broadcasts. - */ -#define AB3100_EVENTA1_ONSWA (0x01<<16) -#define AB3100_EVENTA1_ONSWB (0x02<<16) -#define AB3100_EVENTA1_ONSWC (0x04<<16) -#define AB3100_EVENTA1_DCIO (0x08<<16) -#define AB3100_EVENTA1_OVER_TEMP (0x10<<16) -#define AB3100_EVENTA1_SIM_OFF (0x20<<16) -#define AB3100_EVENTA1_VBUS (0x40<<16) -#define AB3100_EVENTA1_VSET_USB (0x80<<16) - -#define AB3100_EVENTA2_READY_TX (0x01<<8) -#define AB3100_EVENTA2_READY_RX (0x02<<8) -#define AB3100_EVENTA2_OVERRUN_ERROR (0x04<<8) -#define AB3100_EVENTA2_FRAMING_ERROR (0x08<<8) -#define AB3100_EVENTA2_CHARG_OVERCURRENT (0x10<<8) -#define AB3100_EVENTA2_MIDR (0x20<<8) -#define AB3100_EVENTA2_BATTERY_REM (0x40<<8) -#define AB3100_EVENTA2_ALARM (0x80<<8) - -#define AB3100_EVENTA3_ADC_TRIG5 (0x01) -#define AB3100_EVENTA3_ADC_TRIG4 (0x02) -#define AB3100_EVENTA3_ADC_TRIG3 (0x04) -#define AB3100_EVENTA3_ADC_TRIG2 (0x08) -#define AB3100_EVENTA3_ADC_TRIGVBAT (0x10) -#define AB3100_EVENTA3_ADC_TRIGVTX (0x20) -#define AB3100_EVENTA3_ADC_TRIG1 (0x40) -#define AB3100_EVENTA3_ADC_TRIG0 (0x80) - -/* AB3100, STR register flags */ -#define AB3100_STR_ONSWA (0x01) -#define AB3100_STR_ONSWB (0x02) -#define AB3100_STR_ONSWC (0x04) -#define AB3100_STR_DCIO (0x08) -#define AB3100_STR_BOOT_MODE (0x10) -#define AB3100_STR_SIM_OFF (0x20) -#define AB3100_STR_BATT_REMOVAL (0x40) -#define AB3100_STR_VBUS (0x80) - -/* - * AB3100 contains 8 regulators, one external regulator controller - * and a buck converter, further the LDO E and buck converter can - * have separate settings if they are in sleep mode, this is - * modeled as a separate regulator. - */ -#define AB3100_NUM_REGULATORS 10 - -/** - * struct ab3100 - * @access_mutex: lock out concurrent accesses to the AB3100 registers - * @dev: pointer to the containing device - * @i2c_client: I2C client for this chip - * @testreg_client: secondary client for test registers - * @chip_name: name of this chip variant - * @chip_id: 8 bit chip ID for this chip variant - * @event_subscribers: event subscribers are listed here - * @startup_events: a copy of the first reading of the event registers - * @startup_events_read: whether the first events have been read - * - * This struct is PRIVATE and devices using it should NOT - * access ANY fields. It is used as a token for calling the - * AB3100 functions. - */ -struct ab3100 { - struct mutex access_mutex; - struct device *dev; - struct i2c_client *i2c_client; - struct i2c_client *testreg_client; - char chip_name[32]; - u8 chip_id; - struct blocking_notifier_head event_subscribers; - u32 startup_events; - bool startup_events_read; -}; - -/** - * struct ab3100_platform_data - * Data supplied to initialize board connections to the AB3100 - * @reg_constraints: regulator constraints for target board - * the order of these constraints are: LDO A, C, D, E, - * F, G, H, K, EXT and BUCK. - * @reg_initvals: initial values for the regulator registers - * plus two sleep settings for LDO E and the BUCK converter. - * exactly AB3100_NUM_REGULATORS+2 values must be sent in. - * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK, - * BUCK sleep, LDO D. (LDO D need to be initialized last.) - * @external_voltage: voltage level of the external regulator. - */ -struct ab3100_platform_data { - struct regulator_init_data reg_constraints[AB3100_NUM_REGULATORS]; - u8 reg_initvals[AB3100_NUM_REGULATORS+2]; - int external_voltage; -}; - -int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval); -int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval); -int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, - u8 first_reg, u8 *regvals, u8 numregs); -int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, - u8 reg, u8 andmask, u8 ormask); -u8 ab3100_get_chip_type(struct ab3100 *ab3100); -int ab3100_event_register(struct ab3100 *ab3100, - struct notifier_block *nb); -int ab3100_event_unregister(struct ab3100 *ab3100, - struct notifier_block *nb); -int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100, - u32 *fatevent); - -#endif diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h new file mode 100644 index 000000000000..9a881c305a50 --- /dev/null +++ b/include/linux/mfd/abx500.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2007-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * AB3100 core access functions + * Author: Linus Walleij + */ + +#include +#include + +#ifndef MFD_AB3100_H +#define MFD_AB3100_H + +#define ABUNKNOWN 0 +#define AB3000 1 +#define AB3100 2 + +/* + * AB3100, EVENTA1, A2 and A3 event register flags + * these are catenated into a single 32-bit flag in the code + * for event notification broadcasts. + */ +#define AB3100_EVENTA1_ONSWA (0x01<<16) +#define AB3100_EVENTA1_ONSWB (0x02<<16) +#define AB3100_EVENTA1_ONSWC (0x04<<16) +#define AB3100_EVENTA1_DCIO (0x08<<16) +#define AB3100_EVENTA1_OVER_TEMP (0x10<<16) +#define AB3100_EVENTA1_SIM_OFF (0x20<<16) +#define AB3100_EVENTA1_VBUS (0x40<<16) +#define AB3100_EVENTA1_VSET_USB (0x80<<16) + +#define AB3100_EVENTA2_READY_TX (0x01<<8) +#define AB3100_EVENTA2_READY_RX (0x02<<8) +#define AB3100_EVENTA2_OVERRUN_ERROR (0x04<<8) +#define AB3100_EVENTA2_FRAMING_ERROR (0x08<<8) +#define AB3100_EVENTA2_CHARG_OVERCURRENT (0x10<<8) +#define AB3100_EVENTA2_MIDR (0x20<<8) +#define AB3100_EVENTA2_BATTERY_REM (0x40<<8) +#define AB3100_EVENTA2_ALARM (0x80<<8) + +#define AB3100_EVENTA3_ADC_TRIG5 (0x01) +#define AB3100_EVENTA3_ADC_TRIG4 (0x02) +#define AB3100_EVENTA3_ADC_TRIG3 (0x04) +#define AB3100_EVENTA3_ADC_TRIG2 (0x08) +#define AB3100_EVENTA3_ADC_TRIGVBAT (0x10) +#define AB3100_EVENTA3_ADC_TRIGVTX (0x20) +#define AB3100_EVENTA3_ADC_TRIG1 (0x40) +#define AB3100_EVENTA3_ADC_TRIG0 (0x80) + +/* AB3100, STR register flags */ +#define AB3100_STR_ONSWA (0x01) +#define AB3100_STR_ONSWB (0x02) +#define AB3100_STR_ONSWC (0x04) +#define AB3100_STR_DCIO (0x08) +#define AB3100_STR_BOOT_MODE (0x10) +#define AB3100_STR_SIM_OFF (0x20) +#define AB3100_STR_BATT_REMOVAL (0x40) +#define AB3100_STR_VBUS (0x80) + +/* + * AB3100 contains 8 regulators, one external regulator controller + * and a buck converter, further the LDO E and buck converter can + * have separate settings if they are in sleep mode, this is + * modeled as a separate regulator. + */ +#define AB3100_NUM_REGULATORS 10 + +/** + * struct ab3100 + * @access_mutex: lock out concurrent accesses to the AB3100 registers + * @dev: pointer to the containing device + * @i2c_client: I2C client for this chip + * @testreg_client: secondary client for test registers + * @chip_name: name of this chip variant + * @chip_id: 8 bit chip ID for this chip variant + * @event_subscribers: event subscribers are listed here + * @startup_events: a copy of the first reading of the event registers + * @startup_events_read: whether the first events have been read + * + * This struct is PRIVATE and devices using it should NOT + * access ANY fields. It is used as a token for calling the + * AB3100 functions. + */ +struct ab3100 { + struct mutex access_mutex; + struct device *dev; + struct i2c_client *i2c_client; + struct i2c_client *testreg_client; + char chip_name[32]; + u8 chip_id; + struct blocking_notifier_head event_subscribers; + u32 startup_events; + bool startup_events_read; +}; + +/** + * struct ab3100_platform_data + * Data supplied to initialize board connections to the AB3100 + * @reg_constraints: regulator constraints for target board + * the order of these constraints are: LDO A, C, D, E, + * F, G, H, K, EXT and BUCK. + * @reg_initvals: initial values for the regulator registers + * plus two sleep settings for LDO E and the BUCK converter. + * exactly AB3100_NUM_REGULATORS+2 values must be sent in. + * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK, + * BUCK sleep, LDO D. (LDO D need to be initialized last.) + * @external_voltage: voltage level of the external regulator. + */ +struct ab3100_platform_data { + struct regulator_init_data reg_constraints[AB3100_NUM_REGULATORS]; + u8 reg_initvals[AB3100_NUM_REGULATORS+2]; + int external_voltage; +}; + +int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval); +int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval); +int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, + u8 first_reg, u8 *regvals, u8 numregs); +int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, + u8 reg, u8 andmask, u8 ormask); +u8 ab3100_get_chip_type(struct ab3100 *ab3100); +int ab3100_event_register(struct ab3100 *ab3100, + struct notifier_block *nb); +int ab3100_event_unregister(struct ab3100 *ab3100, + struct notifier_block *nb); +int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100, + u32 *fatevent); + +#endif -- cgit v1.2.3-59-g8ed1b From fa661258a27aa74aaf741882053d195291cefb75 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Sat, 1 May 2010 18:26:20 +0200 Subject: mfd: AB3100 register access change to abx500 API The interface for the AB3100 is changed to make way for the ABX500 family of chips: AB3550, AB5500 and future ST-Ericsson Analog Baseband chips. The register access functions are moved out to a separate struct abx500_ops. In this way the interface is moved from the implementation and the sub functionality drivers can keep their interface intact when chip infrastructure and communication mechanisms changes. We also define the AB3550 device IDs and the AB3550 platform data struct and convert the catenated 32bit event to an array of 3 x 8bits. Signed-off-by: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 12 +++- drivers/mfd/Makefile | 1 + drivers/mfd/ab3100-core.c | 95 ++++++++++++++++++--------- drivers/mfd/ab3100-otp.c | 11 ++-- drivers/mfd/abx500-core.c | 157 +++++++++++++++++++++++++++++++++++++++++++++ drivers/regulator/ab3100.c | 33 +++++----- drivers/rtc/rtc-ab3100.c | 39 +++++------ include/linux/mfd/abx500.h | 134 +++++++++++++++++++++++++++++++++----- 8 files changed, 390 insertions(+), 92 deletions(-) create mode 100644 drivers/mfd/abx500-core.c (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 614fa7b8a054..1c3d737ec406 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -374,9 +374,19 @@ config PCF50633_GPIO Say yes here if you want to include support GPIO for pins on the PCF50633 chip. +config ABX500_CORE + bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" + default y if ARCH_U300 + help + Say yes here if you have the ABX500 Mixed Signal IC family + chips. This core driver expose register access functions. + Functionality specific drivers using these functions can + remain unchanged when IC changes. Binding of the functions to + actual register access is done by the IC core driver. + config AB3100_CORE bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" - depends on I2C=y + depends on I2C=y && ABX500_CORE default y if ARCH_U300 help Select this to enable the AB3100 Mixed Signal IC core diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5eb79794d5a5..4b8ad04ff8e2 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_MFD_MAX8925) += max8925.o obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o +obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB4500_CORE) += ab4500-core.o diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index f8c4a333ff1c..53ebfee548fa 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -59,24 +59,15 @@ * The AB3100 is usually assigned address 0x48 (7-bit) * The chip is defined in the platform i2c_board_data section. */ - -u8 ab3100_get_chip_type(struct ab3100 *ab3100) +static int ab3100_get_chip_id(struct device *dev) { - u8 chip = ABUNKNOWN; - - switch (ab3100->chip_id & 0xf0) { - case 0xa0: - chip = AB3000; - break; - case 0xc0: - chip = AB3100; - break; - } - return chip; + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return (int)ab3100->chip_id; } -EXPORT_SYMBOL(ab3100_get_chip_type); -int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval) +static int ab3100_set_register_interruptible(struct ab3100 *ab3100, + u8 reg, u8 regval) { u8 regandval[2] = {reg, regval}; int err; @@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval) mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_set_register_interruptible); +static int set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 value) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return ab3100_set_register_interruptible(ab3100, reg, value); +} /* * The test registers exist at an I2C bus address up one @@ -148,8 +145,8 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100, return err; } - -int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval) +static int ab3100_get_register_interruptible(struct ab3100 *ab3100, + u8 reg, u8 *regval) { int err; @@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval) mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_get_register_interruptible); +static int get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return ab3100_get_register_interruptible(ab3100, reg, value); +} -int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, +static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, u8 first_reg, u8 *regvals, u8 numregs) { int err; @@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_get_register_page_interruptible); +static int get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return ab3100_get_register_page_interruptible(ab3100, + first_reg, regvals, numregs); +} -int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, +static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 andmask, u8 ormask) { u8 regandval[2] = {reg, 0}; @@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, mutex_unlock(&ab3100->access_mutex); return err; } -EXPORT_SYMBOL(ab3100_mask_and_set_register_interruptible); +static int mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); + + return ab3100_mask_and_set_register_interruptible(ab3100, + reg, bitmask, (bitmask & bitvalues)); +} /* * Register a simple callback for handling any AB3100 events. @@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100, EXPORT_SYMBOL(ab3100_event_unregister); -int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100, - u32 *fatevent) +static int ab3100_event_registers_startup_state_get(struct device *dev, + u8 *event) { + struct ab3100 *ab3100 = dev_get_drvdata(dev->parent); if (!ab3100->startup_events_read) return -EAGAIN; /* Try again later */ - *fatevent = ab3100->startup_events; + memcpy(event, ab3100->startup_events, 3); return 0; } -EXPORT_SYMBOL(ab3100_event_registers_startup_state_get); + +static struct abx500_ops ab3100_ops = { + .get_chip_id = ab3100_get_chip_id, + .set_register = set_register_interruptible, + .get_register = get_register_interruptible, + .get_register_page = get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab3100_event_registers_startup_state_get, + .startup_irq_enabled = NULL, +}; /* * This is a threaded interrupt handler so we can make some @@ -390,7 +419,9 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data) event_regs[2]; if (!ab3100->startup_events_read) { - ab3100->startup_events = fatevent; + ab3100->startup_events[0] = event_regs[0]; + ab3100->startup_events[1] = event_regs[1]; + ab3100->startup_events[2] = event_regs[2]; ab3100->startup_events_read = true; } /* @@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100) dev_warn(ab3100->dev, "AB3100 P1E variant detected, " "forcing chip to 32KHz\n"); - err = ab3100_set_test_register_interruptible(ab3100, 0x02, 0x08); + err = ab3100_set_test_register_interruptible(ab3100, + 0x02, 0x08); } exit_no_setup: @@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client, if (err) goto exit_no_irq; + err = abx500_register_ops(&client->dev, &ab3100_ops); + if (err) + goto exit_no_ops; + /* Set parent and a pointer back to the container in device data */ for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) { ab3100_platform_devs[i]->dev.parent = @@ -915,6 +951,7 @@ static int __init ab3100_probe(struct i2c_client *client, return 0; + exit_no_ops: exit_no_irq: exit_no_setup: i2c_unregister_device(ab3100->testreg_client); diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 7093f1aef1e5..63d2b727ddbb 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -30,7 +30,6 @@ /** * struct ab3100_otp * @dev containing device - * @ab3100 a pointer to the parent ab3100 device struct * @locked whether the OTP is locked, after locking, no more bits * can be changed but before locking it is still possible * to change bits from 1->0. @@ -49,7 +48,6 @@ */ struct ab3100_otp { struct device *dev; - struct ab3100 *ab3100; bool locked; u32 freq; bool paf; @@ -63,19 +61,19 @@ struct ab3100_otp { static int __init ab3100_otp_read(struct ab3100_otp *otp) { - struct ab3100 *ab = otp->ab3100; u8 otpval[8]; u8 otpp; int err; - err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp); + err = abx500_get_register_interruptible(otp->dev, 0, + AB3100_OTPP, &otpp); if (err) { dev_err(otp->dev, "unable to read OTPP register\n"); return err; } - err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0, - otpval, 8); + err = abx500_get_register_page_interruptible(otp->dev, 0, + AB3100_OTP0, otpval, 8); if (err) { dev_err(otp->dev, "unable to read OTP register page\n"); return err; @@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) otp->dev = &pdev->dev; /* Replace platform data coming in with a local struct */ - otp->ab3100 = platform_get_drvdata(pdev); platform_set_drvdata(pdev, otp); err = ab3100_otp_read(otp); diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c new file mode 100644 index 000000000000..3b3b97ec32a7 --- /dev/null +++ b/drivers/mfd/abx500-core.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2007-2010 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Register access functions for the ABX500 Mixed Signal IC family. + * Author: Mattias Wallin + */ + +#include +#include +#include +#include + +static LIST_HEAD(abx500_list); + +struct abx500_device_entry { + struct list_head list; + struct abx500_ops ops; + struct device *dev; +}; + +static void lookup_ops(struct device *dev, struct abx500_ops **ops) +{ + struct abx500_device_entry *dev_entry; + + *ops = NULL; + list_for_each_entry(dev_entry, &abx500_list, list) { + if (dev_entry->dev == dev) { + *ops = &dev_entry->ops; + return; + } + } +} + +int abx500_register_ops(struct device *dev, struct abx500_ops *ops) +{ + struct abx500_device_entry *dev_entry; + + dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL); + if (IS_ERR(dev_entry)) { + dev_err(dev, "register_ops kzalloc failed"); + return -ENOMEM; + } + dev_entry->dev = dev; + memcpy(&dev_entry->ops, ops, sizeof(struct abx500_ops)); + + list_add_tail(&dev_entry->list, &abx500_list); + return 0; +} +EXPORT_SYMBOL(abx500_register_ops); + +void abx500_remove_ops(struct device *dev) +{ + struct abx500_device_entry *dev_entry, *tmp; + + list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list) + { + if (dev_entry->dev == dev) { + list_del(&dev_entry->list); + kfree(dev_entry); + } + } +} +EXPORT_SYMBOL(abx500_remove_ops); + +int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->set_register != NULL)) + return ops->set_register(dev, bank, reg, value); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_set_register_interruptible); + +int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_register != NULL)) + return ops->get_register(dev, bank, reg, value); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_register_interruptible); + +int abx500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_register_page != NULL)) + return ops->get_register_page(dev, bank, + first_reg, regvals, numregs); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_register_page_interruptible); + +int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->mask_and_set_register != NULL)) + return ops->mask_and_set_register(dev, bank, + reg, bitmask, bitvalues); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_mask_and_set_register_interruptible); + +int abx500_get_chip_id(struct device *dev) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_chip_id != NULL)) + return ops->get_chip_id(dev); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_chip_id); + +int abx500_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->event_registers_startup_state_get != NULL)) + return ops->event_registers_startup_state_get(dev, event); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_event_registers_startup_state_get); + +int abx500_startup_irq_enabled(struct device *dev, unsigned int irq) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->startup_irq_enabled != NULL)) + return ops->startup_irq_enabled(dev, irq); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_startup_irq_enabled); + +MODULE_AUTHOR("Mattias Wallin "); +MODULE_DESCRIPTION("ABX500 core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 04ca7f62c57e..7b14a67bdca2 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -41,7 +41,7 @@ * struct ab3100_regulator * A struct passed around the individual regulator functions * @platform_device: platform device holding this regulator - * @ab3100: handle to the AB3100 parent chip + * @dev: handle to the device * @plfdata: AB3100 platform data passed in at probe time * @regreg: regulator register number in the AB3100 * @fixed_voltage: a fixed voltage for this regulator, if this @@ -52,7 +52,7 @@ */ struct ab3100_regulator { struct regulator_dev *rdev; - struct ab3100 *ab3100; + struct device *dev; struct ab3100_platform_data *plfdata; u8 regreg; int fixed_voltage; @@ -183,7 +183,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg) int err; u8 regval; - err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_warn(®->dev, "failed to get regid %d value\n", @@ -197,7 +197,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg) regval |= AB3100_REG_ON_MASK; - err = ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, regval); if (err) { dev_warn(®->dev, "failed to set regid %d value\n", @@ -245,14 +245,14 @@ static int ab3100_disable_regulator(struct regulator_dev *reg) if (abreg->regreg == AB3100_LDO_D) { dev_info(®->dev, "disabling LDO D - shut down system\n"); /* Setting LDO D to 0x00 cuts the power to the SoC */ - return ab3100_set_register_interruptible(abreg->ab3100, + return abx500_set_register_interruptible(abreg->dev, 0, AB3100_LDO_D, 0x00U); } /* * All other regulators are handled here */ - err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_err(®->dev, "unable to get register 0x%x\n", @@ -260,7 +260,7 @@ static int ab3100_disable_regulator(struct regulator_dev *reg) return err; } regval &= ~AB3100_REG_ON_MASK; - return ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg, + return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, regval); } @@ -270,7 +270,7 @@ static int ab3100_is_enabled_regulator(struct regulator_dev *reg) u8 regval; int err; - err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_err(®->dev, "unable to get register 0x%x\n", @@ -305,7 +305,7 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg) * For variable types, read out setting and index into * supplied voltage list. */ - err = ab3100_get_register_interruptible(abreg->ab3100, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_warn(®->dev, @@ -373,7 +373,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg, if (bestindex < 0) return bestindex; - err = ab3100_get_register_interruptible(abreg->ab3100, + err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg, ®val); if (err) { dev_warn(®->dev, @@ -386,7 +386,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg, regval &= ~0xE0; regval |= (bestindex << 5); - err = ab3100_set_register_interruptible(abreg->ab3100, + err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg, regval); if (err) dev_warn(®->dev, "failed to set regulator register %02x\n", @@ -414,7 +414,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, /* LDO E and BUCK have special suspend voltages you can set */ bestindex = ab3100_get_best_voltage_index(reg, uV, uV); - err = ab3100_get_register_interruptible(abreg->ab3100, + err = abx500_get_register_interruptible(abreg->dev, 0, targetreg, ®val); if (err) { dev_warn(®->dev, @@ -427,7 +427,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, regval &= ~0xE0; regval |= (bestindex << 5); - err = ab3100_set_register_interruptible(abreg->ab3100, + err = abx500_set_register_interruptible(abreg->dev, 0, targetreg, regval); if (err) dev_warn(®->dev, "failed to set regulator register %02x\n", @@ -574,13 +574,12 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { struct ab3100_platform_data *plfdata = pdev->dev.platform_data; - struct ab3100 *ab3100 = platform_get_drvdata(pdev); int err = 0; u8 data; int i; /* Check chip state */ - err = ab3100_get_register_interruptible(ab3100, + err = abx500_get_register_interruptible(&pdev->dev, 0, AB3100_LDO_D, &data); if (err) { dev_err(&pdev->dev, "could not read initial status of LDO_D\n"); @@ -595,7 +594,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev) /* Set up regulators */ for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) { - err = ab3100_set_register_interruptible(ab3100, + err = abx500_set_register_interruptible(&pdev->dev, 0, ab3100_reg_init_order[i], plfdata->reg_initvals[i]); if (err) { @@ -617,7 +616,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev) * see what it looks like for a certain machine, go * into the machine I2C setup. */ - reg->ab3100 = ab3100; + reg->dev = &pdev->dev; reg->plfdata = plfdata; /* diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index b46b85d581b0..d26780ea254b 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -45,7 +45,6 @@ */ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2, AB3100_TI3, AB3100_TI4, AB3100_TI5}; unsigned char buf[6]; @@ -61,27 +60,26 @@ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs) buf[5] = (fat_time >> 40) & 0xFF; for (i = 0; i < 6; i++) { - err = ab3100_set_register_interruptible(ab3100_data, + err = abx500_set_register_interruptible(dev, 0, regs[i], buf[i]); if (err) return err; } /* Set the flag to mark that the clock is now set */ - return ab3100_mask_and_set_register_interruptible(ab3100_data, + return abx500_mask_and_set_register_interruptible(dev, 0, AB3100_RTC, - 0xFE, 0x01); + 0x01, 0x01); } static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); unsigned long time; u8 rtcval; int err; - err = ab3100_get_register_interruptible(ab3100_data, + err = abx500_get_register_interruptible(dev, 0, AB3100_RTC, &rtcval); if (err) return err; @@ -94,7 +92,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) u8 buf[6]; /* Read out time registers */ - err = ab3100_get_register_page_interruptible(ab3100_data, + err = abx500_get_register_page_interruptible(dev, 0, AB3100_TI0, buf, 6); if (err != 0) @@ -114,7 +112,6 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm) static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); unsigned long time; u64 fat_time; u8 buf[6]; @@ -122,7 +119,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) int err; /* Figure out if alarm is enabled or not */ - err = ab3100_get_register_interruptible(ab3100_data, + err = abx500_get_register_interruptible(dev, 0, AB3100_RTC, &rtcval); if (err) return err; @@ -133,7 +130,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) /* No idea how this could be represented */ alarm->pending = 0; /* Read out alarm registers, only 4 bytes */ - err = ab3100_get_register_page_interruptible(ab3100_data, + err = abx500_get_register_page_interruptible(dev, 0, AB3100_AL0, buf, 4); if (err) return err; @@ -148,7 +145,6 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3}; unsigned char buf[4]; unsigned long secs; @@ -165,21 +161,19 @@ static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) /* Set the alarm */ for (i = 0; i < 4; i++) { - err = ab3100_set_register_interruptible(ab3100_data, + err = abx500_set_register_interruptible(dev, 0, regs[i], buf[i]); if (err) return err; } /* Then enable the alarm */ - return ab3100_mask_and_set_register_interruptible(ab3100_data, - AB3100_RTC, ~(1 << 2), + return abx500_mask_and_set_register_interruptible(dev, 0, + AB3100_RTC, (1 << 2), alarm->enabled << 2); } static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled) { - struct ab3100 *ab3100_data = dev_get_drvdata(dev); - /* * It's not possible to enable/disable the alarm IRQ for this RTC. * It does not actually trigger any IRQ: instead its only function is @@ -188,12 +182,12 @@ static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled) * and need to be handled there instead. */ if (enabled) - return ab3100_mask_and_set_register_interruptible(ab3100_data, - AB3100_RTC, ~(1 << 2), + return abx500_mask_and_set_register_interruptible(dev, 0, + AB3100_RTC, (1 << 2), 1 << 2); else - return ab3100_mask_and_set_register_interruptible(ab3100_data, - AB3100_RTC, ~(1 << 2), + return abx500_mask_and_set_register_interruptible(dev, 0, + AB3100_RTC, (1 << 2), 0); } @@ -210,10 +204,9 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev) int err; u8 regval; struct rtc_device *rtc; - struct ab3100 *ab3100_data = platform_get_drvdata(pdev); /* The first RTC register needs special treatment */ - err = ab3100_get_register_interruptible(ab3100_data, + err = abx500_get_register_interruptible(&pdev->dev, 0, AB3100_RTC, ®val); if (err) { dev_err(&pdev->dev, "unable to read RTC register\n"); @@ -231,7 +224,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev) * This bit remains until RTC power is lost. */ regval = 1 | RTC_SETTING; - err = ab3100_set_register_interruptible(ab3100_data, + err = abx500_set_register_interruptible(&pdev->dev, 0, AB3100_RTC, regval); /* Ignore any error on this write */ } diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 9a881c305a50..390726fcbcb1 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -3,17 +3,37 @@ * License terms: GNU General Public License (GPL) version 2 * AB3100 core access functions * Author: Linus Walleij + * + * ABX500 core access functions. + * The abx500 interface is used for the Analog Baseband chip + * ab3100, ab3550, ab5500 and possibly comming. It is not used for + * ab4500 and ab8500 since they are another family of chip. + * + * Author: Mattias Wallin + * Author: Mattias Nilsson + * Author: Bengt Jonsson + * Author: Rickard Andersson */ #include #include -#ifndef MFD_AB3100_H -#define MFD_AB3100_H +#ifndef MFD_ABX500_H +#define MFD_ABX500_H -#define ABUNKNOWN 0 -#define AB3000 1 -#define AB3100 2 +#define AB3100_P1A 0xc0 +#define AB3100_P1B 0xc1 +#define AB3100_P1C 0xc2 +#define AB3100_P1D 0xc3 +#define AB3100_P1E 0xc4 +#define AB3100_P1F 0xc5 +#define AB3100_P1G 0xc6 +#define AB3100_R2A 0xc7 +#define AB3100_R2B 0xc8 +#define AB3550_P1A 0x10 +#define AB5500_1_0 0x20 +#define AB5500_2_0 0x21 +#define AB5500_2_1 0x22 /* * AB3100, EVENTA1, A2 and A3 event register flags @@ -89,7 +109,7 @@ struct ab3100 { char chip_name[32]; u8 chip_id; struct blocking_notifier_head event_subscribers; - u32 startup_events; + u8 startup_events[3]; bool startup_events_read; }; @@ -112,18 +132,102 @@ struct ab3100_platform_data { int external_voltage; }; -int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval); -int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval); -int ab3100_get_register_page_interruptible(struct ab3100 *ab3100, - u8 first_reg, u8 *regvals, u8 numregs); -int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100, - u8 reg, u8 andmask, u8 ormask); -u8 ab3100_get_chip_type(struct ab3100 *ab3100); int ab3100_event_register(struct ab3100 *ab3100, struct notifier_block *nb); int ab3100_event_unregister(struct ab3100 *ab3100, struct notifier_block *nb); -int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100, - u32 *fatevent); +/* AB3550, STR register flags */ +#define AB3550_STR_ONSWA (0x01) +#define AB3550_STR_ONSWB (0x02) +#define AB3550_STR_ONSWC (0x04) +#define AB3550_STR_DCIO (0x08) +#define AB3550_STR_BOOT_MODE (0x10) +#define AB3550_STR_SIM_OFF (0x20) +#define AB3550_STR_BATT_REMOVAL (0x40) +#define AB3550_STR_VBUS (0x80) + +/* Interrupt mask registers */ +#define AB3550_IMR1 0x29 +#define AB3550_IMR2 0x2a +#define AB3550_IMR3 0x2b +#define AB3550_IMR4 0x2c +#define AB3550_IMR5 0x2d + +enum ab3550_devid { + AB3550_DEVID_ADC, + AB3550_DEVID_DAC, + AB3550_DEVID_LEDS, + AB3550_DEVID_POWER, + AB3550_DEVID_REGULATORS, + AB3550_DEVID_SIM, + AB3550_DEVID_UART, + AB3550_DEVID_RTC, + AB3550_DEVID_CHARGER, + AB3550_DEVID_FUELGAUGE, + AB3550_DEVID_VIBRATOR, + AB3550_DEVID_CODEC, + AB3550_NUM_DEVICES, +}; + +/** + * struct abx500_init_setting + * Initial value of the registers for driver to use during setup. + */ +struct abx500_init_settings { + u8 bank; + u8 reg; + u8 setting; +}; + +/** + * struct ab3550_platform_data + * Data supplied to initialize board connections to the AB3550 + */ +struct ab3550_platform_data { + struct {unsigned int base; unsigned int count; } irq; + void *dev_data[AB3550_NUM_DEVICES]; + size_t dev_data_sz[AB3550_NUM_DEVICES]; + struct abx500_init_settings *init_settings; + unsigned int init_settings_sz; +}; + +int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value); +int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value); +int abx500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs); +int abx500_set_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs); +/** + * abx500_mask_and_set_register_inerruptible() - Modifies selected bits of a + * target register + * + * @dev: The AB sub device. + * @bank: The i2c bank number. + * @bitmask: The bit mask to use. + * @bitvalues: The new bit values. + * + * Updates the value of an AB register: + * value -> ((value & ~bitmask) | (bitvalues & bitmask)) + */ +int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues); +int abx500_get_chip_id(struct device *dev); +int abx500_event_registers_startup_state_get(struct device *dev, u8 *event); +int abx500_startup_irq_enabled(struct device *dev, unsigned int irq); + +struct abx500_ops { + int (*get_chip_id) (struct device *); + int (*get_register) (struct device *, u8, u8, u8 *); + int (*set_register) (struct device *, u8, u8, u8); + int (*get_register_page) (struct device *, u8, u8, u8 *, u8); + int (*set_register_page) (struct device *, u8, u8, u8 *, u8); + int (*mask_and_set_register) (struct device *, u8, u8, u8, u8); + int (*event_registers_startup_state_get) (struct device *, u8 *); + int (*startup_irq_enabled) (struct device *, unsigned int); +}; + +int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops); #endif -- cgit v1.2.3-59-g8ed1b From 62579266cf9caca5b999560be2b5ceee42fc4d4d Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 19 May 2010 11:39:02 +0200 Subject: mfd: New AB8500 driver Add a new driver to support the AB8500 Power Management chip, replacing the current AB4500. The new driver replaces the old one, instead of an incremental modification, because this is a substantial overhaul including: - Split of the driver into -core and -spi portions, to allow another interface layer to be added - Addition of interrupt support - Switch to MFD core API for handling subdevices - Simplification of the APIs to remove a redundant block parameter - Rename of the APIs and macros from ab4500_* to ab8500_* - Rename of the files from ab4500* to ab8500* - Change of the driver name from ab4500 to ab8500 Acked-by: Linus Walleij Acked-by: Srinidhi Kasagar Signed-off-by: Rabin Vincent Signed-off-by: Samuel Ortiz --- arch/arm/mach-ux500/board-mop500.c | 2 +- drivers/mfd/Kconfig | 9 +- drivers/mfd/Makefile | 2 +- drivers/mfd/ab4500-core.c | 209 ----------------- drivers/mfd/ab8500-core.c | 444 +++++++++++++++++++++++++++++++++++++ drivers/mfd/ab8500-spi.c | 133 +++++++++++ include/linux/mfd/ab4500.h | 262 ---------------------- include/linux/mfd/ab8500.h | 128 +++++++++++ 8 files changed, 712 insertions(+), 477 deletions(-) delete mode 100644 drivers/mfd/ab4500-core.c create mode 100644 drivers/mfd/ab8500-core.c create mode 100644 drivers/mfd/ab8500-spi.c delete mode 100644 include/linux/mfd/ab4500.h create mode 100644 include/linux/mfd/ab8500.h (limited to 'include/linux') diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 072196c57263..bb8d7b771817 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -50,7 +50,7 @@ struct pl022_config_chip ab4500_chip_info = { static struct spi_board_info u8500_spi_devices[] = { { - .modalias = "ab4500", + .modalias = "ab8500", .controller_data = &ab4500_chip_info, .max_speed_hz = 12000000, .bus_num = 0, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b84b7078e909..9da0e504bbe9 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -420,11 +420,12 @@ config EZX_PCAP This enables the PCAP ASIC present on EZX Phones. This is needed for MMC, TouchScreen, Sound, USB, etc.. -config AB4500_CORE - tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip" - depends on SPI +config AB8500_CORE + bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" + depends on SPI=y && GENERIC_HARDIRQS + select MFD_CORE help - Select this option to enable access to AB4500 power management + Select this option to enable access to AB8500 power management chip. This connects to U8500 on the SSP/SPI bus and exports read/write functions for the devices to get access to this chip. This chip embeds various other multimedia funtionalities as well. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ca1517e948af..fb503e77dc60 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -64,8 +64,8 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o -obj-$(CONFIG_AB4500_CORE) += ab4500-core.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o +obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o diff --git a/drivers/mfd/ab4500-core.c b/drivers/mfd/ab4500-core.c deleted file mode 100644 index c275daa3ab1a..000000000000 --- a/drivers/mfd/ab4500-core.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2009 ST-Ericsson - * - * Author: Srinidhi KASAGAR - * - * This program is free software; you can redistribute it - * and/or modify it under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation. - * - * AB4500 is a companion power management chip used with U8500. - * On this platform, this is interfaced with SSP0 controller - * which is a ARM primecell pl022. - * - * At the moment the module just exports read/write features. - * Interrupt management to be added - TODO. - */ -#include -#include -#include -#include -#include -#include -#include - -/* just required if probe fails, we need to - * unregister the device - */ -static struct spi_driver ab4500_driver; - -/* - * This funtion writes to any AB4500 registers using - * SPI protocol & before it writes it packs the data - * in the below 24 bit frame format - * - * *|------------------------------------| - * *| 23|22...18|17.......10|9|8|7......0| - * *| r/w bank adr data | - * * ------------------------------------ - * - * This function shouldn't be called from interrupt - * context - */ -int ab4500_write(struct ab4500 *ab4500, unsigned char block, - unsigned long addr, unsigned char data) -{ - struct spi_transfer xfer; - struct spi_message msg; - int err; - unsigned long spi_data = - block << 18 | addr << 10 | data; - - mutex_lock(&ab4500->lock); - ab4500->tx_buf[0] = spi_data; - ab4500->rx_buf[0] = 0; - - xfer.tx_buf = ab4500->tx_buf; - xfer.rx_buf = NULL; - xfer.len = sizeof(unsigned long); - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - err = spi_sync(ab4500->spi, &msg); - mutex_unlock(&ab4500->lock); - - return err; -} -EXPORT_SYMBOL(ab4500_write); - -int ab4500_read(struct ab4500 *ab4500, unsigned char block, - unsigned long addr) -{ - struct spi_transfer xfer; - struct spi_message msg; - unsigned long spi_data = - 1 << 23 | block << 18 | addr << 10; - - mutex_lock(&ab4500->lock); - ab4500->tx_buf[0] = spi_data; - ab4500->rx_buf[0] = 0; - - xfer.tx_buf = ab4500->tx_buf; - xfer.rx_buf = ab4500->rx_buf; - xfer.len = sizeof(unsigned long); - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - spi_sync(ab4500->spi, &msg); - mutex_unlock(&ab4500->lock); - - return ab4500->rx_buf[0]; -} -EXPORT_SYMBOL(ab4500_read); - -/* ref: ab3100 core */ -#define AB4500_DEVICE(devname, devid) \ -static struct platform_device ab4500_##devname##_device = { \ - .name = devid, \ - .id = -1, \ -} - -/* list of childern devices of ab4500 - all are - * not populated here - TODO - */ -AB4500_DEVICE(charger, "ab4500-charger"); -AB4500_DEVICE(audio, "ab4500-audio"); -AB4500_DEVICE(usb, "ab4500-usb"); -AB4500_DEVICE(tvout, "ab4500-tvout"); -AB4500_DEVICE(sim, "ab4500-sim"); -AB4500_DEVICE(gpadc, "ab4500-gpadc"); -AB4500_DEVICE(clkmgt, "ab4500-clkmgt"); -AB4500_DEVICE(misc, "ab4500-misc"); - -static struct platform_device *ab4500_platform_devs[] = { - &ab4500_charger_device, - &ab4500_audio_device, - &ab4500_usb_device, - &ab4500_tvout_device, - &ab4500_sim_device, - &ab4500_gpadc_device, - &ab4500_clkmgt_device, - &ab4500_misc_device, -}; - -static int __init ab4500_probe(struct spi_device *spi) -{ - struct ab4500 *ab4500; - unsigned char revision; - int err = 0; - int i; - - ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL); - if (!ab4500) { - dev_err(&spi->dev, "could not allocate AB4500\n"); - err = -ENOMEM; - goto not_detect; - } - - ab4500->spi = spi; - spi_set_drvdata(spi, ab4500); - - mutex_init(&ab4500->lock); - - /* read the revision register */ - revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG); - - /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */ - if (revision == 0x0 || revision == 0x10) - dev_info(&spi->dev, "Detected chip: %s, revision = %x\n", - ab4500_driver.driver.name, revision); - else { - dev_err(&spi->dev, "unknown chip: 0x%x\n", revision); - goto not_detect; - } - - for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) { - ab4500_platform_devs[i]->dev.parent = - &spi->dev; - platform_set_drvdata(ab4500_platform_devs[i], ab4500); - } - - /* register the ab4500 platform devices */ - platform_add_devices(ab4500_platform_devs, - ARRAY_SIZE(ab4500_platform_devs)); - - return err; - - not_detect: - spi_unregister_driver(&ab4500_driver); - kfree(ab4500); - return err; -} - -static int __devexit ab4500_remove(struct spi_device *spi) -{ - struct ab4500 *ab4500 = - spi_get_drvdata(spi); - - kfree(ab4500); - - return 0; -} - -static struct spi_driver ab4500_driver = { - .driver = { - .name = "ab4500", - .owner = THIS_MODULE, - }, - .probe = ab4500_probe, - .remove = __devexit_p(ab4500_remove) -}; - -static int __devinit ab4500_init(void) -{ - return spi_register_driver(&ab4500_driver); -} - -static void __exit ab4500_exit(void) -{ - spi_unregister_driver(&ab4500_driver); -} - -subsys_initcall(ab4500_init); -module_exit(ab4500_exit); - -MODULE_AUTHOR("Srinidhi KASAGAR + * Author: Rabin Vincent + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Interrupt register offsets + * Bank : 0x0E + */ +#define AB8500_IT_SOURCE1_REG 0x0E00 +#define AB8500_IT_SOURCE2_REG 0x0E01 +#define AB8500_IT_SOURCE3_REG 0x0E02 +#define AB8500_IT_SOURCE4_REG 0x0E03 +#define AB8500_IT_SOURCE5_REG 0x0E04 +#define AB8500_IT_SOURCE6_REG 0x0E05 +#define AB8500_IT_SOURCE7_REG 0x0E06 +#define AB8500_IT_SOURCE8_REG 0x0E07 +#define AB8500_IT_SOURCE19_REG 0x0E12 +#define AB8500_IT_SOURCE20_REG 0x0E13 +#define AB8500_IT_SOURCE21_REG 0x0E14 +#define AB8500_IT_SOURCE22_REG 0x0E15 +#define AB8500_IT_SOURCE23_REG 0x0E16 +#define AB8500_IT_SOURCE24_REG 0x0E17 + +/* + * latch registers + */ +#define AB8500_IT_LATCH1_REG 0x0E20 +#define AB8500_IT_LATCH2_REG 0x0E21 +#define AB8500_IT_LATCH3_REG 0x0E22 +#define AB8500_IT_LATCH4_REG 0x0E23 +#define AB8500_IT_LATCH5_REG 0x0E24 +#define AB8500_IT_LATCH6_REG 0x0E25 +#define AB8500_IT_LATCH7_REG 0x0E26 +#define AB8500_IT_LATCH8_REG 0x0E27 +#define AB8500_IT_LATCH9_REG 0x0E28 +#define AB8500_IT_LATCH10_REG 0x0E29 +#define AB8500_IT_LATCH19_REG 0x0E32 +#define AB8500_IT_LATCH20_REG 0x0E33 +#define AB8500_IT_LATCH21_REG 0x0E34 +#define AB8500_IT_LATCH22_REG 0x0E35 +#define AB8500_IT_LATCH23_REG 0x0E36 +#define AB8500_IT_LATCH24_REG 0x0E37 + +/* + * mask registers + */ + +#define AB8500_IT_MASK1_REG 0x0E40 +#define AB8500_IT_MASK2_REG 0x0E41 +#define AB8500_IT_MASK3_REG 0x0E42 +#define AB8500_IT_MASK4_REG 0x0E43 +#define AB8500_IT_MASK5_REG 0x0E44 +#define AB8500_IT_MASK6_REG 0x0E45 +#define AB8500_IT_MASK7_REG 0x0E46 +#define AB8500_IT_MASK8_REG 0x0E47 +#define AB8500_IT_MASK9_REG 0x0E48 +#define AB8500_IT_MASK10_REG 0x0E49 +#define AB8500_IT_MASK11_REG 0x0E4A +#define AB8500_IT_MASK12_REG 0x0E4B +#define AB8500_IT_MASK13_REG 0x0E4C +#define AB8500_IT_MASK14_REG 0x0E4D +#define AB8500_IT_MASK15_REG 0x0E4E +#define AB8500_IT_MASK16_REG 0x0E4F +#define AB8500_IT_MASK17_REG 0x0E50 +#define AB8500_IT_MASK18_REG 0x0E51 +#define AB8500_IT_MASK19_REG 0x0E52 +#define AB8500_IT_MASK20_REG 0x0E53 +#define AB8500_IT_MASK21_REG 0x0E54 +#define AB8500_IT_MASK22_REG 0x0E55 +#define AB8500_IT_MASK23_REG 0x0E56 +#define AB8500_IT_MASK24_REG 0x0E57 + +#define AB8500_REV_REG 0x1080 + +/* + * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt + * numbers are indexed into this array with (num / 8). + * + * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at + * offset 0. + */ +static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21, +}; + +static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + int ret; + + dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data); + + ret = ab8500->write(ab8500, addr, data); + if (ret < 0) + dev_err(ab8500->dev, "failed to write reg %#x: %d\n", + addr, ret); + + return ret; +} + +/** + * ab8500_write() - write an AB8500 register + * @ab8500: device to write to + * @addr: address of the register + * @data: value to write + */ +int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + int ret; + + mutex_lock(&ab8500->lock); + ret = __ab8500_write(ab8500, addr, data); + mutex_unlock(&ab8500->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ab8500_write); + +static int __ab8500_read(struct ab8500 *ab8500, u16 addr) +{ + int ret; + + ret = ab8500->read(ab8500, addr); + if (ret < 0) + dev_err(ab8500->dev, "failed to read reg %#x: %d\n", + addr, ret); + + dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret); + + return ret; +} + +/** + * ab8500_read() - read an AB8500 register + * @ab8500: device to read from + * @addr: address of the register + */ +int ab8500_read(struct ab8500 *ab8500, u16 addr) +{ + int ret; + + mutex_lock(&ab8500->lock); + ret = __ab8500_read(ab8500, addr); + mutex_unlock(&ab8500->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ab8500_read); + +/** + * ab8500_set_bits() - set a bitfield in an AB8500 register + * @ab8500: device to read from + * @addr: address of the register + * @mask: mask of the bitfield to modify + * @data: value to set to the bitfield + */ +int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data) +{ + int ret; + + mutex_lock(&ab8500->lock); + + ret = __ab8500_read(ab8500, addr); + if (ret < 0) + goto out; + + ret &= ~mask; + ret |= data; + + ret = __ab8500_write(ab8500, addr, ret); + +out: + mutex_unlock(&ab8500->lock); + return ret; +} +EXPORT_SYMBOL_GPL(ab8500_set_bits); + +static void ab8500_irq_lock(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + + mutex_lock(&ab8500->irq_lock); +} + +static void ab8500_irq_sync_unlock(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + int i; + + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + u8 old = ab8500->oldmask[i]; + u8 new = ab8500->mask[i]; + int reg; + + if (new == old) + continue; + + ab8500->oldmask[i] = new; + + reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; + ab8500_write(ab8500, reg, new); + } + + mutex_unlock(&ab8500->irq_lock); +} + +static void ab8500_irq_mask(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + int offset = irq - ab8500->irq_base; + int index = offset / 8; + int mask = 1 << (offset % 8); + + ab8500->mask[index] |= mask; +} + +static void ab8500_irq_unmask(unsigned int irq) +{ + struct ab8500 *ab8500 = get_irq_chip_data(irq); + int offset = irq - ab8500->irq_base; + int index = offset / 8; + int mask = 1 << (offset % 8); + + ab8500->mask[index] &= ~mask; +} + +static struct irq_chip ab8500_irq_chip = { + .name = "ab8500", + .bus_lock = ab8500_irq_lock, + .bus_sync_unlock = ab8500_irq_sync_unlock, + .mask = ab8500_irq_mask, + .unmask = ab8500_irq_unmask, +}; + +static irqreturn_t ab8500_irq(int irq, void *dev) +{ + struct ab8500 *ab8500 = dev; + int i; + + dev_vdbg(ab8500->dev, "interrupt\n"); + + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + int regoffset = ab8500_irq_regoffset[i]; + int status; + + status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset); + if (status <= 0) + continue; + + do { + int bit = __ffs(status); + int line = i * 8 + bit; + + handle_nested_irq(ab8500->irq_base + line); + status &= ~(1 << bit); + } while (status); + } + + return IRQ_HANDLED; +} + +static int ab8500_irq_init(struct ab8500 *ab8500) +{ + int base = ab8500->irq_base; + int irq; + + for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { + set_irq_chip_data(irq, ab8500); + set_irq_chip_and_handler(irq, &ab8500_irq_chip, + handle_simple_irq); + set_irq_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } + + return 0; +} + +static void ab8500_irq_remove(struct ab8500 *ab8500) +{ + int base = ab8500->irq_base; + int irq; + + for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip_and_handler(irq, NULL, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static struct resource ab8500_gpadc_resources[] = { + { + .name = "HW_CONV_END", + .start = AB8500_INT_GP_HW_ADC_CONV_END, + .end = AB8500_INT_GP_HW_ADC_CONV_END, + .flags = IORESOURCE_IRQ, + }, + { + .name = "SW_CONV_END", + .start = AB8500_INT_GP_SW_ADC_CONV_END, + .end = AB8500_INT_GP_SW_ADC_CONV_END, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource ab8500_rtc_resources[] = { + { + .name = "60S", + .start = AB8500_INT_RTC_60S, + .end = AB8500_INT_RTC_60S, + .flags = IORESOURCE_IRQ, + }, + { + .name = "ALARM", + .start = AB8500_INT_RTC_ALARM, + .end = AB8500_INT_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell ab8500_devs[] = { + { + .name = "ab8500-gpadc", + .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), + .resources = ab8500_gpadc_resources, + }, + { + .name = "ab8500-rtc", + .num_resources = ARRAY_SIZE(ab8500_rtc_resources), + .resources = ab8500_rtc_resources, + }, + { .name = "ab8500-charger", }, + { .name = "ab8500-audio", }, + { .name = "ab8500-usb", }, + { .name = "ab8500-pwm", }, +}; + +int __devinit ab8500_init(struct ab8500 *ab8500) +{ + struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); + int ret; + int i; + + if (plat) + ab8500->irq_base = plat->irq_base; + + mutex_init(&ab8500->lock); + mutex_init(&ab8500->irq_lock); + + ret = ab8500_read(ab8500, AB8500_REV_REG); + if (ret < 0) + return ret; + + /* + * 0x0 - Early Drop + * 0x10 - Cut 1.0 + * 0x11 - Cut 1.1 + */ + if (ret == 0x0 || ret == 0x10 || ret == 0x11) { + ab8500->revision = ret; + dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret); + } else { + dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret); + return -EINVAL; + } + + if (plat && plat->init) + plat->init(ab8500); + + /* Clear and mask all interrupts */ + for (i = 0; i < 10; i++) { + ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i); + ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff); + } + + for (i = 18; i < 24; i++) { + ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i); + ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff); + } + + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) + ab8500->mask[i] = ab8500->oldmask[i] = 0xff; + + if (ab8500->irq_base) { + ret = ab8500_irq_init(ab8500); + if (ret) + return ret; + + ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, + IRQF_ONESHOT, "ab8500", ab8500); + if (ret) + goto out_removeirq; + } + + ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; + + return ret; + +out_freeirq: + if (ab8500->irq_base) { + free_irq(ab8500->irq, ab8500); +out_removeirq: + ab8500_irq_remove(ab8500); + } + return ret; +} + +int __devexit ab8500_exit(struct ab8500 *ab8500) +{ + mfd_remove_devices(ab8500->dev); + if (ab8500->irq_base) { + free_irq(ab8500->irq, ab8500); + ab8500_irq_remove(ab8500); + } + + return 0; +} + +MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent"); +MODULE_DESCRIPTION("AB8500 MFD core"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c new file mode 100644 index 000000000000..b81d4f768ef6 --- /dev/null +++ b/drivers/mfd/ab8500-spi.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Srinidhi Kasagar + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This funtion writes to any AB8500 registers using + * SPI protocol & before it writes it packs the data + * in the below 24 bit frame format + * + * *|------------------------------------| + * *| 23|22...18|17.......10|9|8|7......0| + * *| r/w bank adr data | + * * ------------------------------------ + * + * This function shouldn't be called from interrupt + * context + */ +static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + struct spi_device *spi = container_of(ab8500->dev, struct spi_device, + dev); + unsigned long spi_data = addr << 10 | data; + struct spi_transfer xfer; + struct spi_message msg; + + ab8500->tx_buf[0] = spi_data; + ab8500->rx_buf[0] = 0; + + xfer.tx_buf = ab8500->tx_buf; + xfer.rx_buf = NULL; + xfer.len = sizeof(unsigned long); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(spi, &msg); +} + +static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr) +{ + struct spi_device *spi = container_of(ab8500->dev, struct spi_device, + dev); + unsigned long spi_data = 1 << 23 | addr << 10; + struct spi_transfer xfer; + struct spi_message msg; + int ret; + + ab8500->tx_buf[0] = spi_data; + ab8500->rx_buf[0] = 0; + + xfer.tx_buf = ab8500->tx_buf; + xfer.rx_buf = ab8500->rx_buf; + xfer.len = sizeof(unsigned long); + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + if (!ret) + ret = ab8500->rx_buf[0]; + + return ret; +} + +static int __devinit ab8500_spi_probe(struct spi_device *spi) +{ + struct ab8500 *ab8500; + int ret; + + ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); + if (!ab8500) + return -ENOMEM; + + ab8500->dev = &spi->dev; + ab8500->irq = spi->irq; + + ab8500->read = ab8500_spi_read; + ab8500->write = ab8500_spi_write; + + spi_set_drvdata(spi, ab8500); + + ret = ab8500_init(ab8500); + if (ret) + kfree(ab8500); + + return ret; +} + +static int __devexit ab8500_spi_remove(struct spi_device *spi) +{ + struct ab8500 *ab8500 = spi_get_drvdata(spi); + + ab8500_exit(ab8500); + kfree(ab8500); + + return 0; +} + +static struct spi_driver ab8500_spi_driver = { + .driver = { + .name = "ab8500", + .owner = THIS_MODULE, + }, + .probe = ab8500_spi_probe, + .remove = __devexit_p(ab8500_spi_remove) +}; + +static int __init ab8500_spi_init(void) +{ + return spi_register_driver(&ab8500_spi_driver); +} +subsys_initcall(ab8500_spi_init); + +static void __exit ab8500_spi_exit(void) +{ + spi_unregister_driver(&ab8500_spi_driver); +} +module_exit(ab8500_spi_exit); + +MODULE_AUTHOR("Srinidhi KASAGAR - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, as - * published by the Free Software Foundation. - * - * AB4500 device core funtions, for client access - */ -#ifndef MFD_AB4500_H -#define MFD_AB4500_H - -#include - -/* - * AB4500 bank addresses - */ -#define AB4500_SYS_CTRL1_BLOCK 0x1 -#define AB4500_SYS_CTRL2_BLOCK 0x2 -#define AB4500_REGU_CTRL1 0x3 -#define AB4500_REGU_CTRL2 0x4 -#define AB4500_USB 0x5 -#define AB4500_TVOUT 0x6 -#define AB4500_DBI 0x7 -#define AB4500_ECI_AV_ACC 0x8 -#define AB4500_RESERVED 0x9 -#define AB4500_GPADC 0xA -#define AB4500_CHARGER 0xB -#define AB4500_GAS_GAUGE 0xC -#define AB4500_AUDIO 0xD -#define AB4500_INTERRUPT 0xE -#define AB4500_RTC 0xF -#define AB4500_MISC 0x10 -#define AB4500_DEBUG 0x12 -#define AB4500_PROD_TEST 0x13 -#define AB4500_OTP_EMUL 0x15 - -/* - * System control 1 register offsets. - * Bank = 0x01 - */ -#define AB4500_TURNON_STAT_REG 0x0100 -#define AB4500_RESET_STAT_REG 0x0101 -#define AB4500_PONKEY1_PRESS_STAT_REG 0x0102 - -#define AB4500_FSM_STAT1_REG 0x0140 -#define AB4500_FSM_STAT2_REG 0x0141 -#define AB4500_SYSCLK_REQ_STAT_REG 0x0142 -#define AB4500_USB_STAT1_REG 0x0143 -#define AB4500_USB_STAT2_REG 0x0144 -#define AB4500_STATUS_SPARE1_REG 0x0145 -#define AB4500_STATUS_SPARE2_REG 0x0146 - -#define AB4500_CTRL1_REG 0x0180 -#define AB4500_CTRL2_REG 0x0181 - -/* - * System control 2 register offsets. - * bank = 0x02 - */ -#define AB4500_CTRL3_REG 0x0200 -#define AB4500_MAIN_WDOG_CTRL_REG 0x0201 -#define AB4500_MAIN_WDOG_TIMER_REG 0x0202 -#define AB4500_LOW_BAT_REG 0x0203 -#define AB4500_BATT_OK_REG 0x0204 -#define AB4500_SYSCLK_TIMER_REG 0x0205 -#define AB4500_SMPSCLK_CTRL_REG 0x0206 -#define AB4500_SMPSCLK_SEL1_REG 0x0207 -#define AB4500_SMPSCLK_SEL2_REG 0x0208 -#define AB4500_SMPSCLK_SEL3_REG 0x0209 -#define AB4500_SYSULPCLK_CONF_REG 0x020A -#define AB4500_SYSULPCLK_CTRL1_REG 0x020B -#define AB4500_SYSCLK_CTRL_REG 0x020C -#define AB4500_SYSCLK_REQ1_VALID_REG 0x020D -#define AB4500_SYSCLK_REQ_VALID_REG 0x020E -#define AB4500_SYSCTRL_SPARE_REG 0x020F -#define AB4500_PAD_CONF_REG 0x0210 - -/* - * Regu control1 register offsets - * Bank = 0x03 - */ -#define AB4500_REGU_SERIAL_CTRL1_REG 0x0300 -#define AB4500_REGU_SERIAL_CTRL2_REG 0x0301 -#define AB4500_REGU_SERIAL_CTRL3_REG 0x0302 -#define AB4500_REGU_REQ_CTRL1_REG 0x0303 -#define AB4500_REGU_REQ_CTRL2_REG 0x0304 -#define AB4500_REGU_REQ_CTRL3_REG 0x0305 -#define AB4500_REGU_REQ_CTRL4_REG 0x0306 -#define AB4500_REGU_MISC1_REG 0x0380 -#define AB4500_REGU_OTGSUPPLY_CTRL_REG 0x0381 -#define AB4500_REGU_VUSB_CTRL_REG 0x0382 -#define AB4500_REGU_VAUDIO_SUPPLY_REG 0x0383 -#define AB4500_REGU_CTRL1_SPARE_REG 0x0384 - -/* - * Regu control2 Vmod register offsets - */ -#define AB4500_REGU_VMOD_REGU_REG 0x0440 -#define AB4500_REGU_VMOD_SEL1_REG 0x0441 -#define AB4500_REGU_VMOD_SEL2_REG 0x0442 -#define AB4500_REGU_CTRL_DISCH_REG 0x0443 -#define AB4500_REGU_CTRL_DISCH2_REG 0x0444 - -/* - * USB/ULPI register offsets - * Bank : 0x5 - */ -#define AB4500_USB_LINE_STAT_REG 0x0580 -#define AB4500_USB_LINE_CTRL1_REG 0x0581 -#define AB4500_USB_LINE_CTRL2_REG 0x0582 -#define AB4500_USB_LINE_CTRL3_REG 0x0583 -#define AB4500_USB_LINE_CTRL4_REG 0x0584 -#define AB4500_USB_LINE_CTRL5_REG 0x0585 -#define AB4500_USB_OTG_CTRL_REG 0x0587 -#define AB4500_USB_OTG_STAT_REG 0x0588 -#define AB4500_USB_OTG_STAT_REG 0x0588 -#define AB4500_USB_CTRL_SPARE_REG 0x0589 -#define AB4500_USB_PHY_CTRL_REG 0x058A - -/* - * TVOUT / CTRL register offsets - * Bank : 0x06 - */ -#define AB4500_TVOUT_CTRL_REG 0x0680 - -/* - * DBI register offsets - * Bank : 0x07 - */ -#define AB4500_DBI_REG1_REG 0x0700 -#define AB4500_DBI_REG2_REG 0x0701 - -/* - * ECI regsiter offsets - * Bank : 0x08 - */ -#define AB4500_ECI_CTRL_REG 0x0800 -#define AB4500_ECI_HOOKLEVEL_REG 0x0801 -#define AB4500_ECI_DATAOUT_REG 0x0802 -#define AB4500_ECI_DATAIN_REG 0x0803 - -/* - * AV Connector register offsets - * Bank : 0x08 - */ -#define AB4500_AV_CONN_REG 0x0840 - -/* - * Accessory detection register offsets - * Bank : 0x08 - */ -#define AB4500_ACC_DET_DB1_REG 0x0880 -#define AB4500_ACC_DET_DB2_REG 0x0881 - -/* - * GPADC register offsets - * Bank : 0x0A - */ -#define AB4500_GPADC_CTRL1_REG 0x0A00 -#define AB4500_GPADC_CTRL2_REG 0x0A01 -#define AB4500_GPADC_CTRL3_REG 0x0A02 -#define AB4500_GPADC_AUTO_TIMER_REG 0x0A03 -#define AB4500_GPADC_STAT_REG 0x0A04 -#define AB4500_GPADC_MANDATAL_REG 0x0A05 -#define AB4500_GPADC_MANDATAH_REG 0x0A06 -#define AB4500_GPADC_AUTODATAL_REG 0x0A07 -#define AB4500_GPADC_AUTODATAH_REG 0x0A08 -#define AB4500_GPADC_MUX_CTRL_REG 0x0A09 - -/* - * Charger / status register offfsets - * Bank : 0x0B - */ -#define AB4500_CH_STATUS1_REG 0x0B00 -#define AB4500_CH_STATUS2_REG 0x0B01 -#define AB4500_CH_USBCH_STAT1_REG 0x0B02 -#define AB4500_CH_USBCH_STAT2_REG 0x0B03 -#define AB4500_CH_FSM_STAT_REG 0x0B04 -#define AB4500_CH_STAT_REG 0x0B05 - -/* - * Charger / control register offfsets - * Bank : 0x0B - */ -#define AB4500_CH_VOLT_LVL_REG 0x0B40 - -/* - * Charger / main control register offfsets - * Bank : 0x0B - */ -#define AB4500_MCH_CTRL1 0x0B80 -#define AB4500_MCH_CTRL2 0x0B81 -#define AB4500_MCH_IPT_CURLVL_REG 0x0B82 -#define AB4500_CH_WD_REG 0x0B83 - -/* - * Charger / USB control register offsets - * Bank : 0x0B - */ -#define AB4500_USBCH_CTRL1_REG 0x0BC0 -#define AB4500_USBCH_CTRL2_REG 0x0BC1 -#define AB4500_USBCH_IPT_CRNTLVL_REG 0x0BC2 - -/* - * RTC bank register offsets - * Bank : 0xF - */ -#define AB4500_RTC_SOFF_STAT_REG 0x0F00 -#define AB4500_RTC_CC_CONF_REG 0x0F01 -#define AB4500_RTC_READ_REQ_REG 0x0F02 -#define AB4500_RTC_WATCH_TSECMID_REG 0x0F03 -#define AB4500_RTC_WATCH_TSECHI_REG 0x0F04 -#define AB4500_RTC_WATCH_TMIN_LOW_REG 0x0F05 -#define AB4500_RTC_WATCH_TMIN_MID_REG 0x0F06 -#define AB4500_RTC_WATCH_TMIN_HI_REG 0x0F07 -#define AB4500_RTC_ALRM_MIN_LOW_REG 0x0F08 -#define AB4500_RTC_ALRM_MIN_MID_REG 0x0F09 -#define AB4500_RTC_ALRM_MIN_HI_REG 0x0F0A -#define AB4500_RTC_STAT_REG 0x0F0B -#define AB4500_RTC_BKUP_CHG_REG 0x0F0C -#define AB4500_RTC_FORCE_BKUP_REG 0x0F0D -#define AB4500_RTC_CALIB_REG 0x0F0E -#define AB4500_RTC_SWITCH_STAT_REG 0x0F0F - -/* - * PWM Out generators - * Bank: 0x10 - */ -#define AB4500_PWM_OUT_CTRL1_REG 0x1060 -#define AB4500_PWM_OUT_CTRL2_REG 0x1061 -#define AB4500_PWM_OUT_CTRL3_REG 0x1062 -#define AB4500_PWM_OUT_CTRL4_REG 0x1063 -#define AB4500_PWM_OUT_CTRL5_REG 0x1064 -#define AB4500_PWM_OUT_CTRL6_REG 0x1065 -#define AB4500_PWM_OUT_CTRL7_REG 0x1066 - -#define AB4500_I2C_PAD_CTRL_REG 0x1067 -#define AB4500_REV_REG 0x1080 - -/** - * struct ab4500 - * @spi: spi device structure - * @tx_buf: transmit buffer - * @rx_buf: receive buffer - * @lock: sync primitive - */ -struct ab4500 { - struct spi_device *spi; - unsigned long tx_buf[4]; - unsigned long rx_buf[4]; - struct mutex lock; -}; - -int ab4500_write(struct ab4500 *ab4500, unsigned char block, - unsigned long addr, unsigned char data); -int ab4500_read(struct ab4500 *ab4500, unsigned char block, - unsigned long addr); - -#endif /* MFD_AB4500_H */ diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h new file mode 100644 index 000000000000..b63ff3ba3351 --- /dev/null +++ b/include/linux/mfd/ab8500.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Srinidhi Kasagar + */ +#ifndef MFD_AB8500_H +#define MFD_AB8500_H + +#include + +/* + * Interrupts + */ + +#define AB8500_INT_MAIN_EXT_CH_NOT_OK 0 +#define AB8500_INT_UN_PLUG_TV_DET 1 +#define AB8500_INT_PLUG_TV_DET 2 +#define AB8500_INT_TEMP_WARM 3 +#define AB8500_INT_PON_KEY2DB_F 4 +#define AB8500_INT_PON_KEY2DB_R 5 +#define AB8500_INT_PON_KEY1DB_F 6 +#define AB8500_INT_PON_KEY1DB_R 7 +#define AB8500_INT_BATT_OVV 8 +#define AB8500_INT_MAIN_CH_UNPLUG_DET 10 +#define AB8500_INT_MAIN_CH_PLUG_DET 11 +#define AB8500_INT_USB_ID_DET_F 12 +#define AB8500_INT_USB_ID_DET_R 13 +#define AB8500_INT_VBUS_DET_F 14 +#define AB8500_INT_VBUS_DET_R 15 +#define AB8500_INT_VBUS_CH_DROP_END 16 +#define AB8500_INT_RTC_60S 17 +#define AB8500_INT_RTC_ALARM 18 +#define AB8500_INT_BAT_CTRL_INDB 20 +#define AB8500_INT_CH_WD_EXP 21 +#define AB8500_INT_VBUS_OVV 22 +#define AB8500_INT_MAIN_CH_DROP_END 23 +#define AB8500_INT_CCN_CONV_ACC 24 +#define AB8500_INT_INT_AUD 25 +#define AB8500_INT_CCEOC 26 +#define AB8500_INT_CC_INT_CALIB 27 +#define AB8500_INT_LOW_BAT_F 28 +#define AB8500_INT_LOW_BAT_R 29 +#define AB8500_INT_BUP_CHG_NOT_OK 30 +#define AB8500_INT_BUP_CHG_OK 31 +#define AB8500_INT_GP_HW_ADC_CONV_END 32 +#define AB8500_INT_ACC_DETECT_1DB_F 33 +#define AB8500_INT_ACC_DETECT_1DB_R 34 +#define AB8500_INT_ACC_DETECT_22DB_F 35 +#define AB8500_INT_ACC_DETECT_22DB_R 36 +#define AB8500_INT_ACC_DETECT_21DB_F 37 +#define AB8500_INT_ACC_DETECT_21DB_R 38 +#define AB8500_INT_GP_SW_ADC_CONV_END 39 +#define AB8500_INT_BTEMP_LOW 72 +#define AB8500_INT_BTEMP_LOW_MEDIUM 73 +#define AB8500_INT_BTEMP_MEDIUM_HIGH 74 +#define AB8500_INT_BTEMP_HIGH 75 +#define AB8500_INT_USB_CHARGER_NOT_OK 81 +#define AB8500_INT_ID_WAKEUP_R 82 +#define AB8500_INT_ID_DET_R1R 84 +#define AB8500_INT_ID_DET_R2R 85 +#define AB8500_INT_ID_DET_R3R 86 +#define AB8500_INT_ID_DET_R4R 87 +#define AB8500_INT_ID_WAKEUP_F 88 +#define AB8500_INT_ID_DET_R1F 90 +#define AB8500_INT_ID_DET_R2F 91 +#define AB8500_INT_ID_DET_R3F 92 +#define AB8500_INT_ID_DET_R4F 93 +#define AB8500_INT_USB_CHG_DET_DONE 94 +#define AB8500_INT_USB_CH_TH_PROT_F 96 +#define AB8500_INT_USB_CH_TH_PROP_R 97 +#define AB8500_INT_MAIN_CH_TH_PROP_F 98 +#define AB8500_INT_MAIN_CH_TH_PROT_R 99 +#define AB8500_INT_USB_CHARGER_NOT_OKF 103 + +#define AB8500_NR_IRQS 104 +#define AB8500_NUM_IRQ_REGS 13 + +/** + * struct ab8500 - ab8500 internal structure + * @dev: parent device + * @lock: read/write operations lock + * @irq_lock: genirq bus lock + * @revision: chip revision + * @irq: irq line + * @write: register write + * @read: register read + * @rx_buf: rx buf for SPI + * @tx_buf: tx buf for SPI + * @mask: cache of IRQ regs for bus lock + * @oldmask: cache of previous IRQ regs for bus lock + */ +struct ab8500 { + struct device *dev; + struct mutex lock; + struct mutex irq_lock; + int revision; + int irq_base; + int irq; + + int (*write) (struct ab8500 *a8500, u16 addr, u8 data); + int (*read) (struct ab8500 *a8500, u16 addr); + + unsigned long tx_buf[4]; + unsigned long rx_buf[4]; + + u8 mask[AB8500_NUM_IRQ_REGS]; + u8 oldmask[AB8500_NUM_IRQ_REGS]; +}; + +/** + * struct ab8500_platform_data - AB8500 platform data + * @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used + * @init: board-specific initialization after detection of ab8500 + */ +struct ab8500_platform_data { + int irq_base; + void (*init) (struct ab8500 *); +}; + +extern int ab8500_write(struct ab8500 *a8500, u16 addr, u8 data); +extern int ab8500_read(struct ab8500 *a8500, u16 addr); +extern int ab8500_set_bits(struct ab8500 *a8500, u16 addr, u8 mask, u8 data); + +extern int __devinit ab8500_init(struct ab8500 *ab8500); +extern int __devexit ab8500_exit(struct ab8500 *ab8500); + +#endif /* MFD_AB8500_H */ -- cgit v1.2.3-59-g8ed1b