From 6e3ad118041f56db752a5eb2b557517d14592af7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 8 Aug 2011 17:04:40 +0900 Subject: mfd: Convert pcf50633 to use new register map API Signed-off-by: Mark Brown Tested-by: Lars-Peter Clausen Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd/Kconfig') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 21574bdf485..13f0040413c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -500,6 +500,7 @@ config MFD_WM8994 config MFD_PCF50633 tristate "Support for NXP PCF50633" depends on I2C + select REGMAP_I2C help Say yes here if you have NXP PCF50633 chip on your board. This core driver provides register access and IRQ handling -- cgit v1.2.3-70-g09d2 From 2b247d06c6148bfe34a431431064f5553b17f0cc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 23 Aug 2011 18:56:19 +0100 Subject: mfd: Select REGMAP_I2C from WM8400 Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mfd/Kconfig') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 13f0040413c..9c1347dc7a4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -390,6 +390,7 @@ config MFD_WM8400 tristate "Support Wolfson Microelectronics WM8400" select MFD_CORE depends on I2C + select REGMAP_I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio CODEC. This driver provides common support for accessing -- cgit v1.2.3-70-g09d2 From 8a0a8e8e42a4e30a1fc4c40205fa790e264d00f3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Sep 2011 16:43:36 +0200 Subject: mfd: remove CONFIG_MFD_SUPPORT We currently have two symbols to control compilation the MFD subsystem, MFD_SUPPORT and MFD_CORE. The MFD_SUPPORT is actually not required at all, it only hides the submenu when not set, with the effect that Kconfig warns about missing dependencies when another driver selects an MFD driver while MFD_SUPPORT is disabled. Turning the MFD submenu back from menuconfig into a plain menu simplifies the Kconfig syntax for those kinds of users and avoids the surprise when the menu suddenly appears because another driver was enabled that selects this symbol. Signed-off-by: Arnd Bergmann --- arch/arm/mach-omap2/Kconfig | 1 - drivers/gpio/Kconfig | 3 +-- drivers/i2c/busses/Kconfig | 1 - drivers/media/radio/Kconfig | 1 - drivers/mfd/Kconfig | 22 ++++------------------ 5 files changed, 5 insertions(+), 23 deletions(-) (limited to 'drivers/mfd/Kconfig') diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 57b66d590c5..1aee224c9e4 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -14,7 +14,6 @@ config ARCH_OMAP2PLUS_TYPICAL select SERIAL_OMAP_CONSOLE select I2C select I2C_OMAP - select MFD_SUPPORT select MENELAUS if ARCH_OMAP2 select TWL4030_CORE if ARCH_OMAP3 || ARCH_OMAP4 select TWL4030_POWER if ARCH_OMAP3 || ARCH_OMAP4 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4..fbc5fd449a0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -180,7 +180,7 @@ config GPIO_SCH config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" - depends on MFD_SUPPORT && PCI + depends on PCI select MFD_CORE select MFD_VX855 help @@ -417,7 +417,6 @@ config GPIO_TIMBERDALE config GPIO_RDC321X tristate "RDC R-321x GPIO support" depends on PCI - select MFD_SUPPORT select MFD_CORE select MFD_RDC321X help diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 646068e5100..d625a484fa8 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -110,7 +110,6 @@ config I2C_I801 config I2C_ISCH tristate "Intel SCH SMBus 1.0" depends on PCI - select MFD_CORE select LPC_SCH help Say Y here if you want to use SMBus controller on the Intel SCH diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 52798a111e1..ccd5f0d8a01 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -426,7 +426,6 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" depends on I2C && VIDEO_V4L2 - select MFD_CORE select MFD_WL1273_CORE select FW_LOADER ---help--- diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9c1347dc7a4..ac8bd4feb04 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2,23 +2,8 @@ # Multifunction miscellaneous devices # -menuconfig MFD_SUPPORT - bool "Multifunction device drivers" - depends on HAS_IOMEM - default y - help - Multifunction devices embed several functions (e.g. GPIOs, - touchscreens, keyboards, current regulators, power management chips, - etc...) in one single integrated circuit. They usually talk to the - main CPU through one or more IRQ lines and low speed data busses (SPI, - I2C, etc..). They appear as one single device to the main system - through the data bus and the MFD framework allows for sub devices - (a.k.a. functions) to appear as discrete platform devices. - MFDs are typically found on embedded platforms. - - This option alone does not add any kernel code. - -if MFD_SUPPORT +if HAS_IOMEM +menu "Multifunction device drivers" config MFD_CORE tristate @@ -772,7 +757,8 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. -endif # MFD_SUPPORT +endmenu +endif menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 -- cgit v1.2.3-70-g09d2 From 1f5a371c075a7101fe75a75cde5aad928460a42e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 21 Sep 2011 13:03:07 +0200 Subject: mfd: Add Intel MSIC driver Add support for Intel MSIC chip found on Intel Medfield platforms. This chip embeds several subdevices: audio, ADC, GPIO, power button, etc. The driver creates platform device for each subdevice. We also provide an MSIC register access API which should replace the more generic SCU IPC interface currently used. Existing drivers can choose whether they convert to this new API or stick with the SCU IPC interface. Signed-off-by: Mika Westerberg Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/intel_msic.c | 501 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/intel_msic.h | 456 +++++++++++++++++++++++++++++++++++++ 4 files changed, 967 insertions(+) create mode 100644 drivers/mfd/intel_msic.c create mode 100644 include/linux/mfd/intel_msic.h (limited to 'drivers/mfd/Kconfig') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ac8bd4feb04..b01fbe27822 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -757,6 +757,15 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. +config MFD_INTEL_MSIC + bool "Support for Intel MSIC" + depends on INTEL_SCU_IPC + select MFD_CORE + help + Select this option to enable access to Intel MSIC (Avatele + Passage) chip. This chip embeds audio, battery, GPIO, etc. + devices used in Intel Medfield platforms. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c58020303d1..7d53a7c530c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -102,3 +102,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o +obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c new file mode 100644 index 00000000000..bd086b9e852 --- /dev/null +++ b/drivers/mfd/intel_msic.c @@ -0,0 +1,501 @@ +/* + * Driver for Intel MSIC + * + * Copyright (C) 2011, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define MSIC_VENDOR(id) ((id >> 6) & 3) +#define MSIC_VERSION(id) (id & 0x3f) +#define MSIC_MAJOR(id) ('A' + ((id >> 3) & 7)) +#define MSIC_MINOR(id) (id & 7) + +/* + * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE. + * Since IRQ block starts from address 0x002 we need to substract that from + * the actual IRQ status register address. + */ +#define MSIC_IRQ_STATUS(x) (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2)) +#define MSIC_IRQ_STATUS_ACCDET MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET) + +/* + * The SCU hardware has limitation of 16 bytes per read/write buffer on + * Medfield. + */ +#define SCU_IPC_RWBUF_LIMIT 16 + +/** + * struct intel_msic - an MSIC MFD instance + * @pdev: pointer to the platform device + * @vendor: vendor ID + * @version: chip version + * @irq_base: base address of the mapped MSIC SRAM interrupt tree + */ +struct intel_msic { + struct platform_device *pdev; + unsigned vendor; + unsigned version; + void __iomem *irq_base; +}; + +static struct resource msic_touch_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_adc_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_battery_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_gpio_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_audio_resources[] = { + { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }, + /* + * We will pass IRQ_BASE to the driver now but this can be removed + * when/if the driver starts to use intel_msic_irq_read(). + */ + { + .name = "IRQ_BASE", + .flags = IORESOURCE_MEM, + .start = MSIC_IRQ_STATUS_ACCDET, + .end = MSIC_IRQ_STATUS_ACCDET, + }, +}; + +static struct resource msic_hdmi_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_thermal_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_power_btn_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_ocd_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +/* + * Devices that are part of the MSIC and are available via firmware + * populated SFI DEVS table. + */ +static struct mfd_cell msic_devs[] = { + [INTEL_MSIC_BLOCK_TOUCH] = { + .name = "msic_touch", + .num_resources = ARRAY_SIZE(msic_touch_resources), + .resources = msic_touch_resources, + }, + [INTEL_MSIC_BLOCK_ADC] = { + .name = "msic_adc", + .num_resources = ARRAY_SIZE(msic_adc_resources), + .resources = msic_adc_resources, + }, + [INTEL_MSIC_BLOCK_BATTERY] = { + .name = "msic_battery", + .num_resources = ARRAY_SIZE(msic_battery_resources), + .resources = msic_battery_resources, + }, + [INTEL_MSIC_BLOCK_GPIO] = { + .name = "msic_gpio", + .num_resources = ARRAY_SIZE(msic_gpio_resources), + .resources = msic_gpio_resources, + }, + [INTEL_MSIC_BLOCK_AUDIO] = { + .name = "msic_audio", + .num_resources = ARRAY_SIZE(msic_audio_resources), + .resources = msic_audio_resources, + }, + [INTEL_MSIC_BLOCK_HDMI] = { + .name = "msic_hdmi", + .num_resources = ARRAY_SIZE(msic_hdmi_resources), + .resources = msic_hdmi_resources, + }, + [INTEL_MSIC_BLOCK_THERMAL] = { + .name = "msic_thermal", + .num_resources = ARRAY_SIZE(msic_thermal_resources), + .resources = msic_thermal_resources, + }, + [INTEL_MSIC_BLOCK_POWER_BTN] = { + .name = "msic_power_btn", + .num_resources = ARRAY_SIZE(msic_power_btn_resources), + .resources = msic_power_btn_resources, + }, + [INTEL_MSIC_BLOCK_OCD] = { + .name = "msic_ocd", + .num_resources = ARRAY_SIZE(msic_ocd_resources), + .resources = msic_ocd_resources, + }, +}; + +/* + * Other MSIC related devices which are not directly available via SFI DEVS + * table. These can be pseudo devices, regulators etc. which are needed for + * different purposes. + * + * These devices appear only after the MSIC driver itself is initialized so + * we can guarantee that the SCU IPC interface is ready. + */ +static struct mfd_cell msic_other_devs[] = { + /* Audio codec in the MSIC */ + { + .id = -1, + .name = "sn95031", + }, +}; + +/** + * intel_msic_reg_read - read a single MSIC register + * @reg: register to read + * @val: register value is placed here + * + * Read a single register from MSIC. Returns %0 on success and negative + * errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_read(unsigned short reg, u8 *val) +{ + return intel_scu_ipc_ioread8(reg, val); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_read); + +/** + * intel_msic_reg_write - write a single MSIC register + * @reg: register to write + * @val: value to write to that register + * + * Write a single MSIC register. Returns 0 on success and negative + * errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_write(unsigned short reg, u8 val) +{ + return intel_scu_ipc_iowrite8(reg, val); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_write); + +/** + * intel_msic_reg_update - update a single MSIC register + * @reg: register to update + * @val: value to write to the register + * @mask: specifies which of the bits are updated (%0 = don't update, + * %1 = update) + * + * Perform an update to a register @reg. @mask is used to specify which + * bits are updated. Returns %0 in case of success and negative errno in + * case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask) +{ + return intel_scu_ipc_update_register(reg, val, mask); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_update); + +/** + * intel_msic_bulk_read - read an array of registers + * @reg: array of register addresses to read + * @buf: array where the read values are placed + * @count: number of registers to read + * + * Function reads @count registers from the MSIC using addresses passed in + * @reg. Read values are placed in @buf. Reads are performed atomically + * wrt. MSIC. + * + * Returns %0 in case of success and negative errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count) +{ + if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT)) + return -EINVAL; + + return intel_scu_ipc_readv(reg, buf, count); +} +EXPORT_SYMBOL_GPL(intel_msic_bulk_read); + +/** + * intel_msic_bulk_write - write an array of values to the MSIC registers + * @reg: array of registers to write + * @buf: values to write to each register + * @count: number of registers to write + * + * Function writes @count registers in @buf to MSIC. Writes are performed + * atomically wrt MSIC. Returns %0 in case of success and negative errno in + * case of failure. + * + * Function may sleep. + */ +int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count) +{ + if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT)) + return -EINVAL; + + return intel_scu_ipc_writev(reg, buf, count); +} +EXPORT_SYMBOL_GPL(intel_msic_bulk_write); + +/** + * intel_msic_irq_read - read a register from an MSIC interrupt tree + * @msic: MSIC instance + * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and + * %INTEL_MSIC_RESETIRQ2) + * @val: value of the register is placed here + * + * This function can be used by an MSIC subdevice interrupt handler to read + * a register value from the MSIC interrupt tree. In this way subdevice + * drivers don't have to map in the interrupt tree themselves but can just + * call this function instead. + * + * Function doesn't sleep and is callable from interrupt context. + * + * Returns %-EINVAL if @reg is outside of the allowed register region. + */ +int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val) +{ + if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2)) + return -EINVAL; + + *val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1)); + return 0; +} +EXPORT_SYMBOL_GPL(intel_msic_irq_read); + +static int __devinit intel_msic_init_devices(struct intel_msic *msic) +{ + struct platform_device *pdev = msic->pdev; + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + int ret, i; + + if (pdata->gpio) { + struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO]; + + cell->platform_data = pdata->gpio; + cell->pdata_size = sizeof(*pdata->gpio); + } + + if (pdata->ocd) { + unsigned gpio = pdata->ocd->gpio; + + ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio"); + if (ret) { + dev_err(&pdev->dev, "failed to register OCD GPIO\n"); + return ret; + } + + ret = gpio_to_irq(gpio); + if (ret < 0) { + dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n"); + gpio_free(gpio); + return ret; + } + + /* Update the IRQ number for the OCD */ + pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret; + } + + for (i = 0; i < ARRAY_SIZE(msic_devs); i++) { + if (!pdata->irq[i]) + continue; + + ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL, + pdata->irq[i]); + if (ret) + goto fail; + } + + ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs, + ARRAY_SIZE(msic_other_devs), NULL, 0); + if (ret) + goto fail; + + return 0; + +fail: + mfd_remove_devices(&pdev->dev); + if (pdata->ocd) + gpio_free(pdata->ocd->gpio); + + return ret; +} + +static void __devexit intel_msic_remove_devices(struct intel_msic *msic) +{ + struct platform_device *pdev = msic->pdev; + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + + mfd_remove_devices(&pdev->dev); + + if (pdata->ocd) + gpio_free(pdata->ocd->gpio); +} + +static int __devinit intel_msic_probe(struct platform_device *pdev) +{ + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + struct intel_msic *msic; + struct resource *res; + u8 id0, id1; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data passed\n"); + return -EINVAL; + } + + /* First validate that we have an MSIC in place */ + ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0); + if (ret) { + dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n"); + return -ENXIO; + } + + ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1); + if (ret) { + dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n"); + return -ENXIO; + } + + if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) { + dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1); + return -ENXIO; + } + + msic = kzalloc(sizeof(*msic), GFP_KERNEL); + if (!msic) + return -ENOMEM; + + msic->vendor = MSIC_VENDOR(id0); + msic->version = MSIC_VERSION(id0); + msic->pdev = pdev; + + /* + * Map in the MSIC interrupt tree area in SRAM. This is exposed to + * the clients via intel_msic_irq_read(). + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get SRAM iomem resource\n"); + ret = -ENODEV; + goto fail_free_msic; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + ret = -EBUSY; + goto fail_free_msic; + } + + msic->irq_base = ioremap_nocache(res->start, resource_size(res)); + if (!msic->irq_base) { + dev_err(&pdev->dev, "failed to map SRAM memory\n"); + ret = -ENOMEM; + goto fail_release_region; + } + + platform_set_drvdata(pdev, msic); + + ret = intel_msic_init_devices(msic); + if (ret) { + dev_err(&pdev->dev, "failed to initialize MSIC devices\n"); + goto fail_unmap_mem; + } + + dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n", + MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version), + msic->vendor); + + return 0; + +fail_unmap_mem: + iounmap(msic->irq_base); +fail_release_region: + release_mem_region(res->start, resource_size(res)); +fail_free_msic: + kfree(msic); + + return ret; +} + +static int __devexit intel_msic_remove(struct platform_device *pdev) +{ + struct intel_msic *msic = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + intel_msic_remove_devices(msic); + platform_set_drvdata(pdev, NULL); + iounmap(msic->irq_base); + release_mem_region(res->start, resource_size(res)); + kfree(msic); + + return 0; +} + +static struct platform_driver intel_msic_driver = { + .probe = intel_msic_probe, + .remove = __devexit_p(intel_msic_remove), + .driver = { + .name = "intel_msic", + .owner = THIS_MODULE, + }, +}; + +static int __init intel_msic_init(void) +{ + return platform_driver_register(&intel_msic_driver); +} +module_init(intel_msic_init); + +static void __exit intel_msic_exit(void) +{ + platform_driver_unregister(&intel_msic_driver); +} +module_exit(intel_msic_exit); + +MODULE_DESCRIPTION("Driver for Intel MSIC"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/intel_msic.h b/include/linux/mfd/intel_msic.h new file mode 100644 index 00000000000..439a7a617bc --- /dev/null +++ b/include/linux/mfd/intel_msic.h @@ -0,0 +1,456 @@ +/* + * include/linux/mfd/intel_msic.h - Core interface for Intel MSIC + * + * Copyright (C) 2011, Intel Corporation + * Author: Mika Westerberg + * + * 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. + */ + +#ifndef __LINUX_MFD_INTEL_MSIC_H__ +#define __LINUX_MFD_INTEL_MSIC_H__ + +/* ID */ +#define INTEL_MSIC_ID0 0x000 /* RO */ +#define INTEL_MSIC_ID1 0x001 /* RO */ + +/* IRQ */ +#define INTEL_MSIC_IRQLVL1 0x002 +#define INTEL_MSIC_ADC1INT 0x003 +#define INTEL_MSIC_CCINT 0x004 +#define INTEL_MSIC_PWRSRCINT 0x005 +#define INTEL_MSIC_PWRSRCINT1 0x006 +#define INTEL_MSIC_CHRINT 0x007 +#define INTEL_MSIC_CHRINT1 0x008 +#define INTEL_MSIC_RTCIRQ 0x009 +#define INTEL_MSIC_GPIO0LVIRQ 0x00a +#define INTEL_MSIC_GPIO1LVIRQ 0x00b +#define INTEL_MSIC_GPIOHVIRQ 0x00c +#define INTEL_MSIC_VRINT 0x00d +#define INTEL_MSIC_OCAUDIO 0x00e +#define INTEL_MSIC_ACCDET 0x00f +#define INTEL_MSIC_RESETIRQ1 0x010 +#define INTEL_MSIC_RESETIRQ2 0x011 +#define INTEL_MSIC_MADC1INT 0x012 +#define INTEL_MSIC_MCCINT 0x013 +#define INTEL_MSIC_MPWRSRCINT 0x014 +#define INTEL_MSIC_MPWRSRCINT1 0x015 +#define INTEL_MSIC_MCHRINT 0x016 +#define INTEL_MSIC_MCHRINT1 0x017 +#define INTEL_MSIC_RTCIRQMASK 0x018 +#define INTEL_MSIC_GPIO0LVIRQMASK 0x019 +#define INTEL_MSIC_GPIO1LVIRQMASK 0x01a +#define INTEL_MSIC_GPIOHVIRQMASK 0x01b +#define INTEL_MSIC_VRINTMASK 0x01c +#define INTEL_MSIC_OCAUDIOMASK 0x01d +#define INTEL_MSIC_ACCDETMASK 0x01e +#define INTEL_MSIC_RESETIRQ1MASK 0x01f +#define INTEL_MSIC_RESETIRQ2MASK 0x020 +#define INTEL_MSIC_IRQLVL1MSK 0x021 +#define INTEL_MSIC_PBCONFIG 0x03e +#define INTEL_MSIC_PBSTATUS 0x03f /* RO */ + +/* GPIO */ +#define INTEL_MSIC_GPIO0LV7CTLO 0x040 +#define INTEL_MSIC_GPIO0LV6CTLO 0x041 +#define INTEL_MSIC_GPIO0LV5CTLO 0x042 +#define INTEL_MSIC_GPIO0LV4CTLO 0x043 +#define INTEL_MSIC_GPIO0LV3CTLO 0x044 +#define INTEL_MSIC_GPIO0LV2CTLO 0x045 +#define INTEL_MSIC_GPIO0LV1CTLO 0x046 +#define INTEL_MSIC_GPIO0LV0CTLO 0x047 +#define INTEL_MSIC_GPIO1LV7CTLOS 0x048 +#define INTEL_MSIC_GPIO1LV6CTLO 0x049 +#define INTEL_MSIC_GPIO1LV5CTLO 0x04a +#define INTEL_MSIC_GPIO1LV4CTLO 0x04b +#define INTEL_MSIC_GPIO1LV3CTLO 0x04c +#define INTEL_MSIC_GPIO1LV2CTLO 0x04d +#define INTEL_MSIC_GPIO1LV1CTLO 0x04e +#define INTEL_MSIC_GPIO1LV0CTLO 0x04f +#define INTEL_MSIC_GPIO0LV7CTLI 0x050 +#define INTEL_MSIC_GPIO0LV6CTLI 0x051 +#define INTEL_MSIC_GPIO0LV5CTLI 0x052 +#define INTEL_MSIC_GPIO0LV4CTLI 0x053 +#define INTEL_MSIC_GPIO0LV3CTLI 0x054 +#define INTEL_MSIC_GPIO0LV2CTLI 0x055 +#define INTEL_MSIC_GPIO0LV1CTLI 0x056 +#define INTEL_MSIC_GPIO0LV0CTLI 0x057 +#define INTEL_MSIC_GPIO1LV7CTLIS 0x058 +#define INTEL_MSIC_GPIO1LV6CTLI 0x059 +#define INTEL_MSIC_GPIO1LV5CTLI 0x05a +#define INTEL_MSIC_GPIO1LV4CTLI 0x05b +#define INTEL_MSIC_GPIO1LV3CTLI 0x05c +#define INTEL_MSIC_GPIO1LV2CTLI 0x05d +#define INTEL_MSIC_GPIO1LV1CTLI 0x05e +#define INTEL_MSIC_GPIO1LV0CTLI 0x05f +#define INTEL_MSIC_PWM0CLKDIV1 0x061 +#define INTEL_MSIC_PWM0CLKDIV0 0x062 +#define INTEL_MSIC_PWM1CLKDIV1 0x063 +#define INTEL_MSIC_PWM1CLKDIV0 0x064 +#define INTEL_MSIC_PWM2CLKDIV1 0x065 +#define INTEL_MSIC_PWM2CLKDIV0 0x066 +#define INTEL_MSIC_PWM0DUTYCYCLE 0x067 +#define INTEL_MSIC_PWM1DUTYCYCLE 0x068 +#define INTEL_MSIC_PWM2DUTYCYCLE 0x069 +#define INTEL_MSIC_GPIO0HV3CTLO 0x06d +#define INTEL_MSIC_GPIO0HV2CTLO 0x06e +#define INTEL_MSIC_GPIO0HV1CTLO 0x06f +#define INTEL_MSIC_GPIO0HV0CTLO 0x070 +#define INTEL_MSIC_GPIO1HV3CTLO 0x071 +#define INTEL_MSIC_GPIO1HV2CTLO 0x072 +#define INTEL_MSIC_GPIO1HV1CTLO 0x073 +#define INTEL_MSIC_GPIO1HV0CTLO 0x074 +#define INTEL_MSIC_GPIO0HV3CTLI 0x075 +#define INTEL_MSIC_GPIO0HV2CTLI 0x076 +#define INTEL_MSIC_GPIO0HV1CTLI 0x077 +#define INTEL_MSIC_GPIO0HV0CTLI 0x078 +#define INTEL_MSIC_GPIO1HV3CTLI 0x079 +#define INTEL_MSIC_GPIO1HV2CTLI 0x07a +#define INTEL_MSIC_GPIO1HV1CTLI 0x07b +#define INTEL_MSIC_GPIO1HV0CTLI 0x07c + +/* SVID */ +#define INTEL_MSIC_SVIDCTRL0 0x080 +#define INTEL_MSIC_SVIDCTRL1 0x081 +#define INTEL_MSIC_SVIDCTRL2 0x082 +#define INTEL_MSIC_SVIDTXLASTPKT3 0x083 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT2 0x084 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT1 0x085 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT0 0x086 /* RO */ +#define INTEL_MSIC_SVIDPKTOUTBYTE3 0x087 +#define INTEL_MSIC_SVIDPKTOUTBYTE2 0x088 +#define INTEL_MSIC_SVIDPKTOUTBYTE1 0x089 +#define INTEL_MSIC_SVIDPKTOUTBYTE0 0x08a +#define INTEL_MSIC_SVIDRXVPDEBUG1 0x08b +#define INTEL_MSIC_SVIDRXVPDEBUG0 0x08c +#define INTEL_MSIC_SVIDRXLASTPKT3 0x08d /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT2 0x08e /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT1 0x08f /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT0 0x090 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS3 0x091 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS2 0x092 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS1 0x093 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS0 0x094 /* RO */ + +/* VREG */ +#define INTEL_MSIC_VCCLATCH 0x0c0 +#define INTEL_MSIC_VNNLATCH 0x0c1 +#define INTEL_MSIC_VCCCNT 0x0c2 +#define INTEL_MSIC_SMPSRAMP 0x0c3 +#define INTEL_MSIC_VNNCNT 0x0c4 +#define INTEL_MSIC_VNNAONCNT 0x0c5 +#define INTEL_MSIC_VCC122AONCNT 0x0c6 +#define INTEL_MSIC_V180AONCNT 0x0c7 +#define INTEL_MSIC_V500CNT 0x0c8 +#define INTEL_MSIC_VIHFCNT 0x0c9 +#define INTEL_MSIC_LDORAMP1 0x0ca +#define INTEL_MSIC_LDORAMP2 0x0cb +#define INTEL_MSIC_VCC108AONCNT 0x0cc +#define INTEL_MSIC_VCC108ASCNT 0x0cd +#define INTEL_MSIC_VCC108CNT 0x0ce +#define INTEL_MSIC_VCCA100ASCNT 0x0cf +#define INTEL_MSIC_VCCA100CNT 0x0d0 +#define INTEL_MSIC_VCC180AONCNT 0x0d1 +#define INTEL_MSIC_VCC180CNT 0x0d2 +#define INTEL_MSIC_VCC330CNT 0x0d3 +#define INTEL_MSIC_VUSB330CNT 0x0d4 +#define INTEL_MSIC_VCCSDIOCNT 0x0d5 +#define INTEL_MSIC_VPROG1CNT 0x0d6 +#define INTEL_MSIC_VPROG2CNT 0x0d7 +#define INTEL_MSIC_VEMMCSCNT 0x0d8 +#define INTEL_MSIC_VEMMC1CNT 0x0d9 +#define INTEL_MSIC_VEMMC2CNT 0x0da +#define INTEL_MSIC_VAUDACNT 0x0db +#define INTEL_MSIC_VHSPCNT 0x0dc +#define INTEL_MSIC_VHSNCNT 0x0dd +#define INTEL_MSIC_VHDMICNT 0x0de +#define INTEL_MSIC_VOTGCNT 0x0df +#define INTEL_MSIC_V1P35CNT 0x0e0 +#define INTEL_MSIC_V330AONCNT 0x0e1 + +/* RESET */ +#define INTEL_MSIC_CHIPCNTRL 0x100 /* WO */ +#define INTEL_MSIC_ERCONFIG 0x101 + +/* BURST */ +#define INTEL_MSIC_BATCURRENTLIMIT12 0x102 +#define INTEL_MSIC_BATTIMELIMIT12 0x103 +#define INTEL_MSIC_BATTIMELIMIT3 0x104 +#define INTEL_MSIC_BATTIMEDB 0x105 +#define INTEL_MSIC_BRSTCONFIGOUTPUTS 0x106 +#define INTEL_MSIC_BRSTCONFIGACTIONS 0x107 +#define INTEL_MSIC_BURSTCONTROLSTATUS 0x108 + +/* RTC */ +#define INTEL_MSIC_RTCB1 0x140 /* RO */ +#define INTEL_MSIC_RTCB2 0x141 /* RO */ +#define INTEL_MSIC_RTCB3 0x142 /* RO */ +#define INTEL_MSIC_RTCB4 0x143 /* RO */ +#define INTEL_MSIC_RTCOB1 0x144 +#define INTEL_MSIC_RTCOB2 0x145 +#define INTEL_MSIC_RTCOB3 0x146 +#define INTEL_MSIC_RTCOB4 0x147 +#define INTEL_MSIC_RTCAB1 0x148 +#define INTEL_MSIC_RTCAB2 0x149 +#define INTEL_MSIC_RTCAB3 0x14a +#define INTEL_MSIC_RTCAB4 0x14b +#define INTEL_MSIC_RTCWAB1 0x14c +#define INTEL_MSIC_RTCWAB2 0x14d +#define INTEL_MSIC_RTCWAB3 0x14e +#define INTEL_MSIC_RTCWAB4 0x14f +#define INTEL_MSIC_RTCSC1 0x150 +#define INTEL_MSIC_RTCSC2 0x151 +#define INTEL_MSIC_RTCSC3 0x152 +#define INTEL_MSIC_RTCSC4 0x153 +#define INTEL_MSIC_RTCSTATUS 0x154 /* RO */ +#define INTEL_MSIC_RTCCONFIG1 0x155 +#define INTEL_MSIC_RTCCONFIG2 0x156 + +/* CHARGER */ +#define INTEL_MSIC_BDTIMER 0x180 +#define INTEL_MSIC_BATTRMV 0x181 +#define INTEL_MSIC_VBUSDET 0x182 +#define INTEL_MSIC_VBUSDET1 0x183 +#define INTEL_MSIC_ADPHVDET 0x184 +#define INTEL_MSIC_ADPLVDET 0x185 +#define INTEL_MSIC_ADPDETDBDM 0x186 +#define INTEL_MSIC_LOWBATTDET 0x187 +#define INTEL_MSIC_CHRCTRL 0x188 +#define INTEL_MSIC_CHRCVOLTAGE 0x189 +#define INTEL_MSIC_CHRCCURRENT 0x18a +#define INTEL_MSIC_SPCHARGER 0x18b +#define INTEL_MSIC_CHRTTIME 0x18c +#define INTEL_MSIC_CHRCTRL1 0x18d +#define INTEL_MSIC_PWRSRCLMT 0x18e +#define INTEL_MSIC_CHRSTWDT 0x18f +#define INTEL_MSIC_WDTWRITE 0x190 /* WO */ +#define INTEL_MSIC_CHRSAFELMT 0x191 +#define INTEL_MSIC_SPWRSRCINT 0x192 /* RO */ +#define INTEL_MSIC_SPWRSRCINT1 0x193 /* RO */ +#define INTEL_MSIC_CHRLEDPWM 0x194 +#define INTEL_MSIC_CHRLEDCTRL 0x195 + +/* ADC */ +#define INTEL_MSIC_ADC1CNTL1 0x1c0 +#define INTEL_MSIC_ADC1CNTL2 0x1c1 +#define INTEL_MSIC_ADC1CNTL3 0x1c2 +#define INTEL_MSIC_ADC1OFFSETH 0x1c3 /* RO */ +#define INTEL_MSIC_ADC1OFFSETL 0x1c4 /* RO */ +#define INTEL_MSIC_ADC1ADDR0 0x1c5 +#define INTEL_MSIC_ADC1ADDR1 0x1c6 +#define INTEL_MSIC_ADC1ADDR2 0x1c7 +#define INTEL_MSIC_ADC1ADDR3 0x1c8 +#define INTEL_MSIC_ADC1ADDR4 0x1c9 +#define INTEL_MSIC_ADC1ADDR5 0x1ca +#define INTEL_MSIC_ADC1ADDR6 0x1cb +#define INTEL_MSIC_ADC1ADDR7 0x1cc +#define INTEL_MSIC_ADC1ADDR8 0x1cd +#define INTEL_MSIC_ADC1ADDR9 0x1ce +#define INTEL_MSIC_ADC1ADDR10 0x1cf +#define INTEL_MSIC_ADC1ADDR11 0x1d0 +#define INTEL_MSIC_ADC1ADDR12 0x1d1 +#define INTEL_MSIC_ADC1ADDR13 0x1d2 +#define INTEL_MSIC_ADC1ADDR14 0x1d3 +#define INTEL_MSIC_ADC1SNS0H 0x1d4 /* RO */ +#define INTEL_MSIC_ADC1SNS0L 0x1d5 /* RO */ +#define INTEL_MSIC_ADC1SNS1H 0x1d6 /* RO */ +#define INTEL_MSIC_ADC1SNS1L 0x1d7 /* RO */ +#define INTEL_MSIC_ADC1SNS2H 0x1d8 /* RO */ +#define INTEL_MSIC_ADC1SNS2L 0x1d9 /* RO */ +#define INTEL_MSIC_ADC1SNS3H 0x1da /* RO */ +#define INTEL_MSIC_ADC1SNS3L 0x1db /* RO */ +#define INTEL_MSIC_ADC1SNS4H 0x1dc /* RO */ +#define INTEL_MSIC_ADC1SNS4L 0x1dd /* RO */ +#define INTEL_MSIC_ADC1SNS5H 0x1de /* RO */ +#define INTEL_MSIC_ADC1SNS5L 0x1df /* RO */ +#define INTEL_MSIC_ADC1SNS6H 0x1e0 /* RO */ +#define INTEL_MSIC_ADC1SNS6L 0x1e1 /* RO */ +#define INTEL_MSIC_ADC1SNS7H 0x1e2 /* RO */ +#define INTEL_MSIC_ADC1SNS7L 0x1e3 /* RO */ +#define INTEL_MSIC_ADC1SNS8H 0x1e4 /* RO */ +#define INTEL_MSIC_ADC1SNS8L 0x1e5 /* RO */ +#define INTEL_MSIC_ADC1SNS9H 0x1e6 /* RO */ +#define INTEL_MSIC_ADC1SNS9L 0x1e7 /* RO */ +#define INTEL_MSIC_ADC1SNS10H 0x1e8 /* RO */ +#define INTEL_MSIC_ADC1SNS10L 0x1e9 /* RO */ +#define INTEL_MSIC_ADC1SNS11H 0x1ea /* RO */ +#define INTEL_MSIC_ADC1SNS11L 0x1eb /* RO */ +#define INTEL_MSIC_ADC1SNS12H 0x1ec /* RO */ +#define INTEL_MSIC_ADC1SNS12L 0x1ed /* RO */ +#define INTEL_MSIC_ADC1SNS13H 0x1ee /* RO */ +#define INTEL_MSIC_ADC1SNS13L 0x1ef /* RO */ +#define INTEL_MSIC_ADC1SNS14H 0x1f0 /* RO */ +#define INTEL_MSIC_ADC1SNS14L 0x1f1 /* RO */ +#define INTEL_MSIC_ADC1BV0H 0x1f2 /* RO */ +#define INTEL_MSIC_ADC1BV0L 0x1f3 /* RO */ +#define INTEL_MSIC_ADC1BV1H 0x1f4 /* RO */ +#define INTEL_MSIC_ADC1BV1L 0x1f5 /* RO */ +#define INTEL_MSIC_ADC1BV2H 0x1f6 /* RO */ +#define INTEL_MSIC_ADC1BV2L 0x1f7 /* RO */ +#define INTEL_MSIC_ADC1BV3H 0x1f8 /* RO */ +#define INTEL_MSIC_ADC1BV3L 0x1f9 /* RO */ +#define INTEL_MSIC_ADC1BI0H 0x1fa /* RO */ +#define INTEL_MSIC_ADC1BI0L 0x1fb /* RO */ +#define INTEL_MSIC_ADC1BI1H 0x1fc /* RO */ +#define INTEL_MSIC_ADC1BI1L 0x1fd /* RO */ +#define INTEL_MSIC_ADC1BI2H 0x1fe /* RO */ +#define INTEL_MSIC_ADC1BI2L 0x1ff /* RO */ +#define INTEL_MSIC_ADC1BI3H 0x200 /* RO */ +#define INTEL_MSIC_ADC1BI3L 0x201 /* RO */ +#define INTEL_MSIC_CCCNTL 0x202 +#define INTEL_MSIC_CCOFFSETH 0x203 /* RO */ +#define INTEL_MSIC_CCOFFSETL 0x204 /* RO */ +#define INTEL_MSIC_CCADCHA 0x205 /* RO */ +#define INTEL_MSIC_CCADCLA 0x206 /* RO */ + +/* AUDIO */ +#define INTEL_MSIC_AUDPLLCTRL 0x240 +#define INTEL_MSIC_DMICBUF0123 0x241 +#define INTEL_MSIC_DMICBUF45 0x242 +#define INTEL_MSIC_DMICGPO 0x244 +#define INTEL_MSIC_DMICMUX 0x245 +#define INTEL_MSIC_DMICCLK 0x246 +#define INTEL_MSIC_MICBIAS 0x247 +#define INTEL_MSIC_ADCCONFIG 0x248 +#define INTEL_MSIC_MICAMP1 0x249 +#define INTEL_MSIC_MICAMP2 0x24a +#define INTEL_MSIC_NOISEMUX 0x24b +#define INTEL_MSIC_AUDIOMUX12 0x24c +#define INTEL_MSIC_AUDIOMUX34 0x24d +#define INTEL_MSIC_AUDIOSINC 0x24e +#define INTEL_MSIC_AUDIOTXEN 0x24f +#define INTEL_MSIC_HSEPRXCTRL 0x250 +#define INTEL_MSIC_IHFRXCTRL 0x251 +#define INTEL_MSIC_VOICETXVOL 0x252 +#define INTEL_MSIC_SIDETONEVOL 0x253 +#define INTEL_MSIC_MUSICSHARVOL 0x254 +#define INTEL_MSIC_VOICETXCTRL 0x255 +#define INTEL_MSIC_HSMIXER 0x256 +#define INTEL_MSIC_DACCONFIG 0x257 +#define INTEL_MSIC_SOFTMUTE 0x258 +#define INTEL_MSIC_HSLVOLCTRL 0x259 +#define INTEL_MSIC_HSRVOLCTRL 0x25a +#define INTEL_MSIC_IHFLVOLCTRL 0x25b +#define INTEL_MSIC_IHFRVOLCTRL 0x25c +#define INTEL_MSIC_DRIVEREN 0x25d +#define INTEL_MSIC_LINEOUTCTRL 0x25e +#define INTEL_MSIC_VIB1CTRL1 0x25f +#define INTEL_MSIC_VIB1CTRL2 0x260 +#define INTEL_MSIC_VIB1CTRL3 0x261 +#define INTEL_MSIC_VIB1SPIPCM_1 0x262 +#define INTEL_MSIC_VIB1SPIPCM_2 0x263 +#define INTEL_MSIC_VIB1CTRL5 0x264 +#define INTEL_MSIC_VIB2CTRL1 0x265 +#define INTEL_MSIC_VIB2CTRL2 0x266 +#define INTEL_MSIC_VIB2CTRL3 0x267 +#define INTEL_MSIC_VIB2SPIPCM_1 0x268 +#define INTEL_MSIC_VIB2SPIPCM_2 0x269 +#define INTEL_MSIC_VIB2CTRL5 0x26a +#define INTEL_MSIC_BTNCTRL1 0x26b +#define INTEL_MSIC_BTNCTRL2 0x26c +#define INTEL_MSIC_PCM1TXSLOT01 0x26d +#define INTEL_MSIC_PCM1TXSLOT23 0x26e +#define INTEL_MSIC_PCM1TXSLOT45 0x26f +#define INTEL_MSIC_PCM1RXSLOT0123 0x270 +#define INTEL_MSIC_PCM1RXSLOT045 0x271 +#define INTEL_MSIC_PCM2TXSLOT01 0x272 +#define INTEL_MSIC_PCM2TXSLOT23 0x273 +#define INTEL_MSIC_PCM2TXSLOT45 0x274 +#define INTEL_MSIC_PCM2RXSLOT01 0x275 +#define INTEL_MSIC_PCM2RXSLOT23 0x276 +#define INTEL_MSIC_PCM2RXSLOT45 0x277 +#define INTEL_MSIC_PCM1CTRL1 0x278 +#define INTEL_MSIC_PCM1CTRL2 0x279 +#define INTEL_MSIC_PCM1CTRL3 0x27a +#define INTEL_MSIC_PCM2CTRL1 0x27b +#define INTEL_MSIC_PCM2CTRL2 0x27c + +/* HDMI */ +#define INTEL_MSIC_HDMIPUEN 0x280 +#define INTEL_MSIC_HDMISTATUS 0x281 /* RO */ + +/* Physical address of the start of the MSIC interrupt tree in SRAM */ +#define INTEL_MSIC_IRQ_PHYS_BASE 0xffff7fc0 + +/** + * struct intel_msic_gpio_pdata - platform data for the MSIC GPIO driver + * @gpio_base: base number for the GPIOs + */ +struct intel_msic_gpio_pdata { + unsigned gpio_base; +}; + +/** + * struct intel_msic_ocd_pdata - platform data for the MSIC OCD driver + * @gpio: GPIO number used for OCD interrupts + * + * The MSIC MFD driver converts @gpio into an IRQ number and passes it to + * the OCD driver as %IORESOURCE_IRQ. + */ +struct intel_msic_ocd_pdata { + unsigned gpio; +}; + +/* MSIC embedded blocks (subdevices) */ +enum intel_msic_block { + INTEL_MSIC_BLOCK_TOUCH, + INTEL_MSIC_BLOCK_ADC, + INTEL_MSIC_BLOCK_BATTERY, + INTEL_MSIC_BLOCK_GPIO, + INTEL_MSIC_BLOCK_AUDIO, + INTEL_MSIC_BLOCK_HDMI, + INTEL_MSIC_BLOCK_THERMAL, + INTEL_MSIC_BLOCK_POWER_BTN, + INTEL_MSIC_BLOCK_OCD, + + INTEL_MSIC_BLOCK_LAST, +}; + +/** + * struct intel_msic_platform_data - platform data for the MSIC driver + * @irq: array of interrupt numbers, one per device. If @irq is set to %0 + * for a given block, the corresponding platform device is not + * created. For devices which don't have an interrupt, use %0xff + * (this is same as in SFI spec). + * @gpio: platform data for the MSIC GPIO driver + * @ocd: platform data for the MSIC OCD driver + * + * Once the MSIC driver is initialized, the register interface is ready to + * use. All the platform devices for subdevices are created after the + * register interface is ready so that we can guarantee its availability to + * the subdevice drivers. + * + * Interrupt numbers are passed to the subdevices via %IORESOURCE_IRQ + * resources of the created platform device. + */ +struct intel_msic_platform_data { + int irq[INTEL_MSIC_BLOCK_LAST]; + struct intel_msic_gpio_pdata *gpio; + struct intel_msic_ocd_pdata *ocd; +}; + +struct intel_msic; + +extern int intel_msic_reg_read(unsigned short reg, u8 *val); +extern int intel_msic_reg_write(unsigned short reg, u8 val); +extern int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask); +extern int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count); +extern int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count); + +/* + * pdev_to_intel_msic - gets an MSIC instance from the platform device + * @pdev: platform device pointer + * + * The client drivers need to have pointer to the MSIC instance if they + * want to call intel_msic_irq_read(). This macro can be used for + * convenience to get the MSIC pointer from @pdev where needed. This is + * _only_ valid for devices which are managed by the MSIC. + */ +#define pdev_to_intel_msic(pdev) (dev_get_drvdata(pdev->dev.parent)) + +extern int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, + u8 *val); + +#endif /* __LINUX_MFD_INTEL_MSIC_H__ */ -- cgit v1.2.3-70-g09d2 From 3d5e2cabf11a65685e5067382ba4c4a76f18fcb7 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Thu, 22 Sep 2011 08:22:18 +0200 Subject: mfd: ab5500 chip register access The analog baseband chip ab5500 is a multi functional chip containing regulators, charging, gpio, USB and accessory detect. It also contain various multimedia functionalities like digital encoder and audio codec. The core driver added with this patch provides register access via i2c via PRCMU. Event handling implemented as irq_chip will come in future patches since it depends on PRCMU functionality not yet implemented. Signed-off-by: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/ab5500-core.c | 2312 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/ab5500/ab5500.h | 140 +++ include/linux/mfd/abx500.h | 4 +- 5 files changed, 2464 insertions(+), 2 deletions(-) create mode 100644 drivers/mfd/ab5500-core.c create mode 100644 include/linux/mfd/ab5500/ab5500.h (limited to 'drivers/mfd/Kconfig') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b01fbe27822..8594de8b86a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -563,6 +563,15 @@ config EZX_PCAP This enables the PCAP ASIC present on EZX Phones. This is needed for MMC, TouchScreen, Sound, USB, etc.. +config AB5500_CORE + bool "ST-Ericsson AB5500 Mixed Signal Power Management chip" + depends on ABX500_CORE && MFD_DB5500_PRCMU + select MFD_CORE + help + Select this option to enable access to AB5500 power management + chip. This connects to the db5500 chip via the I2C bus via PRCMU. + This chip embeds various other multimedia funtionalities as well. + config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7d53a7c530c..457fed8474c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o +obj-$(CONFIG_AB5500_CORE) += ab5500-core.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c new file mode 100644 index 00000000000..afec0f2ede4 --- /dev/null +++ b/drivers/mfd/ab5500-core.c @@ -0,0 +1,2312 @@ +/* + * Copyright (C) 2007-2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Low-level core for exclusive access to the AB5500 IC on the I2C bus + * and some basic chip-configuration. + * Author: Bengt Jonsson + * Author: Mattias Nilsson + * Author: Mattias Wallin + * Author: Rickard Andersson + * Author: Karl Komierowski + * Author: Bibek Basu + * + * TODO: Event handling with irq_chip. Waiting for PRCMU fw support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AB5500_NUM_EVENT_REG 23 +#define AB5500_IT_LATCH0_REG 0x40 +#define AB5500_IT_MASK0_REG 0x60 + +/* Read/write operation values. */ +#define AB5500_PERM_RD (0x01) +#define AB5500_PERM_WR (0x02) + +/* Read/write permissions. */ +#define AB5500_PERM_RO (AB5500_PERM_RD) +#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) + +#define AB5500_MASK_BASE (0x60) +#define AB5500_MASK_END (0x79) +#define AB5500_CHIP_ID (0x20) + +/** + * struct ab5500_bank + * @slave_addr: I2C slave_addr found in AB5500 specification + * @name: Documentation name of the bank. For reference + */ +struct ab5500_bank { + u8 slave_addr; + const char *name; +}; + +static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, + [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, + [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, + [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, + [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, + [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, + [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, + [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, + [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, + [AB5500_BANK_FG_BATTCOM_ACC] = { + AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, + [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, + [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, + [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, +}; + +/** + * struct ab5500_reg_range + * @first: the first address of the range + * @last: the last address of the range + * @perm: access permissions for the range + */ +struct ab5500_reg_range { + u8 first; + u8 last; + u8 perm; +}; + +/** + * struct ab5500_i2c_ranges + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_ranges { + u8 nranges; + u8 bankid; + const struct ab5500_reg_range *range; +}; + +/** + * struct ab5500_i2c_banks + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_banks { + u8 nbanks; + const struct ab5500_i2c_ranges *bank; +}; + +/* + * Permissible register ranges for reading and writing per device and bank. + * + * The ranges must be listed in increasing address order, and no overlaps are + * allowed. It is assumed that write permission implies read permission + * (i.e. only RO and RW permissions should be used). Ranges with write + * permission must not be split up. + */ + +#define NO_RANGE {.count = 0, .range = NULL,} +static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = { + [AB5500_DEVID_USB] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + }, + }, + [AB5500_DEVID_ADC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + }, + }, + [AB5500_DEVID_LEDS] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_VIDEO] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_REGULATORS] = { + .nbanks = 2, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_SIM] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_RTC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_CHARGER] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_FUELGAUGE] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_VIBRATOR] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_CODEC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_POWER] = { + .nbanks = 2, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_STARTUP, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x30, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + }, + }, + { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, +}; + +#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit)) + +/* I appologize for the resource names beeing a mix of upper case + * and lower case but I want them to be exact as the documentation */ +static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { + [AB5500_DEVID_LEDS] = { + .name = "ab5500-leds", + .id = AB5500_DEVID_LEDS, + }, + [AB5500_DEVID_POWER] = { + .name = "ab5500-power", + .id = AB5500_DEVID_POWER, + }, + [AB5500_DEVID_REGULATORS] = { + .name = "ab5500-regulator", + .id = AB5500_DEVID_REGULATORS, + }, + [AB5500_DEVID_SIM] = { + .name = "ab5500-sim", + .id = AB5500_DEVID_SIM, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "SIMOFF", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(2, 0), /*rising*/ + .end = AB5500_IRQ(2, 1), /*falling*/ + }, + }, + }, + [AB5500_DEVID_RTC] = { + .name = "ab5500-rtc", + .id = AB5500_DEVID_RTC, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "RTC_Alarm", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 7), + .end = AB5500_IRQ(1, 7), + } + }, + }, + [AB5500_DEVID_CHARGER] = { + .name = "ab5500-charger", + .id = AB5500_DEVID_CHARGER, + }, + [AB5500_DEVID_ADC] = { + .name = "ab5500-adc", + .id = AB5500_DEVID_ADC, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "TRIGGER-0", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 0), + .end = AB5500_IRQ(0, 0), + }, + { + .name = "TRIGGER-1", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 1), + .end = AB5500_IRQ(0, 1), + }, + { + .name = "TRIGGER-2", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 2), + .end = AB5500_IRQ(0, 2), + }, + { + .name = "TRIGGER-3", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 3), + .end = AB5500_IRQ(0, 3), + }, + { + .name = "TRIGGER-4", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 4), + .end = AB5500_IRQ(0, 4), + }, + { + .name = "TRIGGER-5", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 5), + .end = AB5500_IRQ(0, 5), + }, + { + .name = "TRIGGER-6", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 6), + .end = AB5500_IRQ(0, 6), + }, + { + .name = "TRIGGER-7", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 7), + .end = AB5500_IRQ(0, 7), + }, + { + .name = "TRIGGER-VBAT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 8), + .end = AB5500_IRQ(0, 8), + }, + { + .name = "TRIGGER-VBAT-TXON", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 9), + .end = AB5500_IRQ(0, 9), + }, + }, + }, + [AB5500_DEVID_FUELGAUGE] = { + .name = "ab5500-fuelgauge", + .id = AB5500_DEVID_FUELGAUGE, + .num_resources = 6, + .resources = (struct resource[]) { + { + .name = "Batt_attach", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 5), + .end = AB5500_IRQ(7, 5), + }, + { + .name = "Batt_removal", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 6), + .end = AB5500_IRQ(7, 6), + }, + { + .name = "UART_framing", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 7), + .end = AB5500_IRQ(7, 7), + }, + { + .name = "UART_overrun", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 0), + .end = AB5500_IRQ(8, 0), + }, + { + .name = "UART_Rdy_RX", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 1), + .end = AB5500_IRQ(8, 1), + }, + { + .name = "UART_Rdy_TX", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 2), + .end = AB5500_IRQ(8, 2), + }, + }, + }, + [AB5500_DEVID_VIBRATOR] = { + .name = "ab5500-vibrator", + .id = AB5500_DEVID_VIBRATOR, + }, + [AB5500_DEVID_CODEC] = { + .name = "ab5500-codec", + .id = AB5500_DEVID_CODEC, + .num_resources = 3, + .resources = (struct resource[]) { + { + .name = "audio_spkr1_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 5), + .end = AB5500_IRQ(9, 5), + }, + { + .name = "audio_plllocked", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 6), + .end = AB5500_IRQ(9, 6), + }, + { + .name = "audio_spkr2_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 4), + .end = AB5500_IRQ(17, 4), + }, + }, + }, + [AB5500_DEVID_USB] = { + .name = "ab5500-usb", + .id = AB5500_DEVID_USB, + .num_resources = 36, + .resources = (struct resource[]) { + { + .name = "Link_Update", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(22, 1), + .end = AB5500_IRQ(22, 1), + }, + { + .name = "DCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 3), + .end = AB5500_IRQ(8, 4), + }, + { + .name = "VBUS_R", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 5), + .end = AB5500_IRQ(8, 5), + }, + { + .name = "VBUS_F", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 6), + .end = AB5500_IRQ(8, 6), + }, + { + .name = "CHGstate_10_PCVBUSchg", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 7), + .end = AB5500_IRQ(8, 7), + }, + { + .name = "DCIOreverse_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 0), + .end = AB5500_IRQ(9, 0), + }, + { + .name = "USBCharDetDone", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 1), + .end = AB5500_IRQ(9, 1), + }, + { + .name = "DCIO_no_limit", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 2), + .end = AB5500_IRQ(9, 2), + }, + { + .name = "USB_suspend", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 3), + .end = AB5500_IRQ(9, 3), + }, + { + .name = "DCIOreverse_fwdcurrent", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 4), + .end = AB5500_IRQ(9, 4), + }, + { + .name = "Vbus_Imeasmax_change", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 5), + .end = AB5500_IRQ(9, 6), + }, + { + .name = "OVV", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 5), + .end = AB5500_IRQ(14, 5), + }, + { + .name = "USBcharging_NOTok", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 3), + .end = AB5500_IRQ(15, 3), + }, + { + .name = "usb_adp_sensoroff", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 6), + .end = AB5500_IRQ(15, 6), + }, + { + .name = "usb_adp_probeplug", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 7), + .end = AB5500_IRQ(15, 7), + }, + { + .name = "usb_adp_sinkerror", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 0), + .end = AB5500_IRQ(16, 6), + }, + { + .name = "usb_adp_sourceerror", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 1), + .end = AB5500_IRQ(16, 1), + }, + { + .name = "usb_idgnd_r", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 2), + .end = AB5500_IRQ(16, 2), + }, + { + .name = "usb_idgnd_f", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 3), + .end = AB5500_IRQ(16, 3), + }, + { + .name = "usb_iddetR1", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 4), + .end = AB5500_IRQ(16, 5), + }, + { + .name = "usb_iddetR2", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 6), + .end = AB5500_IRQ(16, 7), + }, + { + .name = "usb_iddetR3", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 0), + .end = AB5500_IRQ(17, 1), + }, + { + .name = "usb_iddetR4", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 2), + .end = AB5500_IRQ(17, 3), + }, + { + .name = "CharTempWindowOk", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 7), + .end = AB5500_IRQ(18, 0), + }, + { + .name = "USB_SprDetect", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 1), + .end = AB5500_IRQ(18, 1), + }, + { + .name = "usb_adp_probe_unplug", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 2), + .end = AB5500_IRQ(18, 2), + }, + { + .name = "VBUSChDrop", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 3), + .end = AB5500_IRQ(18, 4), + }, + { + .name = "dcio_char_rec_done", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 5), + .end = AB5500_IRQ(18, 5), + }, + { + .name = "Charging_stopped_by_temp", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 6), + .end = AB5500_IRQ(18, 6), + }, + { + .name = "CHGstate_11_SafeModeVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 1), + .end = AB5500_IRQ(21, 2), + }, + { + .name = "CHGstate_12_comletedVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 2), + .end = AB5500_IRQ(21, 2), + }, + { + .name = "CHGstate_13_completedVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 3), + .end = AB5500_IRQ(21, 3), + }, + { + .name = "CHGstate_14_FullChgDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 4), + .end = AB5500_IRQ(21, 4), + }, + { + .name = "CHGstate_15_SafeModeDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 5), + .end = AB5500_IRQ(21, 5), + }, + { + .name = "CHGstate_16_OFFsuspendDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 6), + .end = AB5500_IRQ(21, 6), + }, + { + .name = "CHGstate_17_completedDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 7), + .end = AB5500_IRQ(21, 7), + }, + }, + }, + [AB5500_DEVID_OTP] = { + .name = "ab5500-otp", + .id = AB5500_DEVID_OTP, + }, + [AB5500_DEVID_VIDEO] = { + .name = "ab5500-video", + .id = AB5500_DEVID_VIDEO, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "plugTVdet", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(22, 2), + .end = AB5500_IRQ(22, 2), + }, + }, + }, + [AB5500_DEVID_DBIECI] = { + .name = "ab5500-dbieci", + .id = AB5500_DEVID_DBIECI, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "COLL", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 0), + .end = AB5500_IRQ(14, 0), + }, + { + .name = "RESERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 1), + .end = AB5500_IRQ(14, 1), + }, + { + .name = "FRAERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 2), + .end = AB5500_IRQ(14, 2), + }, + { + .name = "COMERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 3), + .end = AB5500_IRQ(14, 3), + }, + { + .name = "BSI_indicator", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 4), + .end = AB5500_IRQ(14, 4), + }, + { + .name = "SPDSET", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 6), + .end = AB5500_IRQ(14, 6), + }, + { + .name = "DSENT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 7), + .end = AB5500_IRQ(14, 7), + }, + { + .name = "DREC", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 0), + .end = AB5500_IRQ(15, 0), + }, + { + .name = "ACCINT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 1), + .end = AB5500_IRQ(15, 1), + }, + { + .name = "NOPINT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 2), + .end = AB5500_IRQ(15, 2), + }, + }, + }, + [AB5500_DEVID_ONSWA] = { + .name = "ab5500-onswa", + .id = AB5500_DEVID_ONSWA, + .num_resources = 2, + .resources = (struct resource[]) { + { + .name = "ONSWAn_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 3), + .end = AB5500_IRQ(1, 3), + }, + { + .name = "ONSWAn_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 4), + .end = AB5500_IRQ(1, 4), + }, + }, + }, +}; + +/* + * Functionality for getting/setting register values. + */ +static int get_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, + u8 *value) +{ + int err; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1); + + mutex_unlock(&ab->access_mutex); + return err; +} + +static int get_register_page_interruptible(struct ab5500 *ab, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + int err; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + while (numregs) { + /* The hardware limit for get page is 4 */ + u8 curnum = min_t(u8, numregs, 4u); + + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + first_reg, regvals, curnum); + if (err) + goto out; + + numregs -= curnum; + first_reg += curnum; + regvals += curnum; + } + +out: + mutex_unlock(&ab->access_mutex); + return err; +} + +static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + int err = 0; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + if (bitmask) { + u8 buf; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + if (bitmask == 0xFF) /* No need to read in this case. */ + buf = bitvalues; + else { /* Read and modify the register value. */ + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + reg, &buf, 1); + if (err) + return err; + + buf = ((~bitmask & buf) | (bitmask & bitvalues)); + } + /* Write the new value. */ + err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg, + &buf, 1); + + mutex_unlock(&ab->access_mutex); + } + return err; +} + +static int +set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value) +{ + return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value); +} + +/* + * Read/write permission checking functions. + */ +static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank) +{ + u8 i; + + if (devid < AB5500_NUM_DEVICES) { + for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) { + if (ab5500_bank_ranges[devid].bank[i].bankid == bank) + return &ab5500_bank_ranges[devid].bank[i]; + } + } + return NULL; +} + +static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg) +{ + u8 i; /* range loop index */ + const struct ab5500_i2c_ranges *bankref; + + bankref = get_bankref(devid, bank); + if (bankref == NULL || last_reg < first_reg) + return false; + + for (i = 0; i < bankref->nranges; i++) { + if (first_reg < bankref->range[i].first) + break; + if ((last_reg <= bankref->range[i].last) && + (bankref->range[i].perm & AB5500_PERM_WR)) + return true; + } + return false; +} + +static bool reg_write_allowed(u8 devid, u8 bank, u8 reg) +{ + return page_write_allowed(devid, bank, reg, reg); +} + +static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg) +{ + u8 i; + const struct ab5500_i2c_ranges *bankref; + + bankref = get_bankref(devid, bank); + if (bankref == NULL || last_reg < first_reg) + return false; + + + /* Find the range (if it exists in the list) that includes first_reg. */ + for (i = 0; i < bankref->nranges; i++) { + if (first_reg < bankref->range[i].first) + return false; + if (first_reg <= bankref->range[i].last) + break; + } + /* Make sure that the entire range up to and including last_reg is + * readable. This may span several of the ranges in the list. + */ + while ((i < bankref->nranges) && + (bankref->range[i].perm & AB5500_PERM_RD)) { + if (last_reg <= bankref->range[i].last) + return true; + if ((++i >= bankref->nranges) || + (bankref->range[i].first != + (bankref->range[i - 1].last + 1))) { + break; + } + } + return false; +} + +static bool reg_read_allowed(u8 devid, u8 bank, u8 reg) +{ + return page_read_allowed(devid, bank, reg, reg); +} + + +/* + * The exported register access functionality. + */ +static int ab5500_get_chip_id(struct device *dev) +{ + struct ab5500 *ab = dev_get_drvdata(dev->parent); + + return (int)ab->chip_id; +} + +static int ab5500_mask_and_set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !reg_write_allowed(pdev->id, bank, reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return mask_and_set_register_interruptible(ab, bank, reg, + bitmask, bitvalues); +} + +static int ab5500_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 value) +{ + return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, + value); +} + +static int ab5500_get_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 *value) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !reg_read_allowed(pdev->id, bank, reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_interruptible(ab, bank, reg, value); +} + +static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !page_read_allowed(pdev->id, bank, + first_reg, (first_reg + numregs - 1))) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_page_interruptible(ab, bank, first_reg, regvals, + numregs); +} + +static int +ab5500_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct ab5500 *ab; + + ab = dev_get_drvdata(dev->parent); + if (!ab->startup_events_read) + return -EAGAIN; /* Try again later */ + + memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG); + return 0; +} + +static struct abx500_ops ab5500_ops = { + .get_chip_id = ab5500_get_chip_id, + .get_register = ab5500_get_register_interruptible, + .set_register = ab5500_set_register_interruptible, + .get_register_page = ab5500_get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = ab5500_mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab5500_event_registers_startup_state_get, + .startup_irq_enabled = NULL, +}; + +#ifdef CONFIG_DEBUG_FS +static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { + [AB5500_BANK_LED] = { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_ADC] = { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_RTC] = { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_STARTUP] = { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_DBI_ECI] = { + .bankid = AB5500_BANK_DBI_ECI, + .nranges = 3, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x07, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x10, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x13, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_CHG] = { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_FG_BATTCOM_ACC] = { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_USB] = { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_IT] = { + .bankid = AB5500_BANK_IT, + .nranges = 4, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x20, + .last = 0x36, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x40, + .last = 0x56, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x60, + .last = 0x76, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, + .nranges = 7, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x12, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x44, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x54, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x64, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x74, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 13, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0D, + .last = 0x0F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1C, + .last = 0x1C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1E, + .last = 0x1E, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x20, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x25, + .last = 0x25, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x28, + .last = 0x2A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x33, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x43, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x53, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x63, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x73, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIBRA] = { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_SIM_USBSIM] = { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VDENC] = { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, +}; +static int ab5500_registers_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + unsigned int i; + u8 bank = (u8)ab->debug_bank; + + seq_printf(s, "ab5500 register values:\n"); + for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { + seq_printf(s, " bank %u, %s (0x%x):\n", bank, + bankinfo[bank].name, + bankinfo[bank].slave_addr); + for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { + u8 reg; + int err; + + for (reg = ab5500_reg_ranges[bank].range[i].first; + reg <= ab5500_reg_ranges[bank].range[i].last; + reg++) { + u8 value; + + err = get_register_interruptible(ab, bank, reg, + &value); + if (err < 0) { + dev_err(ab->dev, "get_reg failed %d" + "bank 0x%x reg 0x%x\n", + err, bank, reg); + return err; + } + + err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", + bank, reg, value); + if (err < 0) { + dev_err(ab->dev, + "seq_printf overflow\n"); + /* + * Error is not returned here since + * the output is wanted in any case + */ + return 0; + } + } + } + } + return 0; +} + +static int ab5500_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_registers_print, inode->i_private); +} + +static const struct file_operations ab5500_registers_fops = { + .open = ab5500_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab5500_bank_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); + return 0; +} + +static int ab5500_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_bank_print, inode->i_private); +} + +static ssize_t ab5500_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB5500_NUM_BANKS) { + dev_err(ab->dev, + "debugfs error input > number of banks\n"); + return -EINVAL; + } + + ab->debug_bank = user_bank; + + return buf_size; +} + +static int ab5500_address_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab5500_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_address_print, inode->i_private); +} + +static ssize_t ab5500_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + ab->debug_address = user_address; + return buf_size; +} + +static int ab5500_val_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + int err; + u8 regvalue; + + err = get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) { + dev_err(ab->dev, "get_reg failed %d, bank 0x%x" + ", reg 0x%x\n", err, ab->debug_bank, + ab->debug_address); + return -EINVAL; + } + seq_printf(s, "0x%02X\n", regvalue); + + return 0; +} + +static int ab5500_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_val_print, inode->i_private); +} + +static ssize_t ab5500_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = mask_and_set_register_interruptible( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + + return buf_size; +} + +static const struct file_operations ab5500_bank_fops = { + .open = ab5500_bank_open, + .write = ab5500_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_address_fops = { + .open = ab5500_address_open, + .write = ab5500_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_val_fops = { + .open = ab5500_val_open, + .write = ab5500_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab5500_dir; +static struct dentry *ab5500_reg_file; +static struct dentry *ab5500_bank_file; +static struct dentry *ab5500_address_file; +static struct dentry *ab5500_val_file; + +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ + ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; + ab->debug_address = AB5500_CHIP_ID; + + ab5500_dir = debugfs_create_dir("ab5500", NULL); + if (!ab5500_dir) + goto exit_no_debugfs; + + ab5500_reg_file = debugfs_create_file("all-bank-registers", + S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); + if (!ab5500_reg_file) + goto exit_destroy_dir; + + ab5500_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); + if (!ab5500_bank_file) + goto exit_destroy_reg; + + ab5500_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); + if (!ab5500_address_file) + goto exit_destroy_bank; + + ab5500_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); + if (!ab5500_val_file) + goto exit_destroy_address; + + return; + +exit_destroy_address: + debugfs_remove(ab5500_address_file); +exit_destroy_bank: + debugfs_remove(ab5500_bank_file); +exit_destroy_reg: + debugfs_remove(ab5500_reg_file); +exit_destroy_dir: + debugfs_remove(ab5500_dir); +exit_no_debugfs: + dev_err(ab->dev, "failed to create debugfs entries.\n"); + return; +} + +static inline void ab5500_remove_debugfs(void) +{ + debugfs_remove(ab5500_val_file); + debugfs_remove(ab5500_address_file); + debugfs_remove(ab5500_bank_file); + debugfs_remove(ab5500_reg_file); + debugfs_remove(ab5500_dir); +} + +#else /* !CONFIG_DEBUG_FS */ +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ +} +static inline void ab5500_remove_debugfs(void) +{ +} +#endif + +/* + * ab5500_setup : Basic set-up, datastructure creation/destruction + * and I2C interface.This sets up a default config + * in the AB5500 chip so that it will work as expected. + * @ab : Pointer to ab5500 structure + * @settings : Pointer to struct abx500_init_settings + * @size : Size of init data + */ +static int __init ab5500_setup(struct ab5500 *ab, + struct abx500_init_settings *settings, unsigned int size) +{ + int err = 0; + int i; + + for (i = 0; i < size; i++) { + err = mask_and_set_register_interruptible(ab, + settings[i].bank, + settings[i].reg, + 0xFF, settings[i].setting); + if (err) + goto exit_no_setup; + + /* If event mask register update the event mask in ab5500 */ + if ((settings[i].bank == AB5500_BANK_IT) && + (AB5500_MASK_BASE <= settings[i].reg) && + (settings[i].reg <= AB5500_MASK_END)) { + ab->mask[settings[i].reg - AB5500_MASK_BASE] = + settings[i].setting; + } + } +exit_no_setup: + return err; +} + +struct ab_family_id { + u8 id; + char *name; +}; + +static const struct ab_family_id ids[] __initdata = { + /* AB5500 */ + { + .id = AB5500_1_0, + .name = "1.0" + }, + { + .id = AB5500_1_1, + .name = "1.1" + }, + /* Terminator */ + { + .id = 0x00, + } +}; + +static int __init ab5500_probe(struct platform_device *pdev) +{ + struct ab5500 *ab; + struct ab5500_platform_data *ab5500_plf_data = + pdev->dev.platform_data; + int err; + int i; + + ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL); + if (!ab) { + dev_err(&pdev->dev, + "could not allocate ab5500 device\n"); + return -ENOMEM; + } + + /* Initialize data structure */ + mutex_init(&ab->access_mutex); + mutex_init(&ab->irq_lock); + ab->dev = &pdev->dev; + + platform_set_drvdata(pdev, ab); + + /* Read chip ID register */ + err = get_register_interruptible(ab, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + AB5500_CHIP_ID, &ab->chip_id); + if (err) { + dev_err(&pdev->dev, "could not communicate with the analog " + "baseband chip\n"); + goto exit_no_detect; + } + + for (i = 0; ids[i].id != 0x0; i++) { + if (ids[i].id == ab->chip_id) { + snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, + "AB5500 %s", ids[i].name); + break; + } + } + if (ids[i].id == 0x0) { + dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n", + ab->chip_id); + dev_err(&pdev->dev, "driver not started!\n"); + goto exit_no_detect; + } + + /* Clear and mask all interrupts */ + for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) { + u8 latchreg = AB5500_IT_LATCH0_REG + i; + u8 maskreg = AB5500_IT_MASK0_REG + i; + u8 val; + + get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val); + set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff); + ab->mask[i] = ab->oldmask[i] = 0xff; + } + + err = abx500_register_ops(&pdev->dev, &ab5500_ops); + if (err) { + dev_err(&pdev->dev, "ab5500_register ops error\n"); + goto exit_no_detect; + } + + /* Set up and register the platform devices. */ + for (i = 0; i < AB5500_NUM_DEVICES; i++) { + ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i]; + ab5500_devs[i].pdata_size = + sizeof(ab5500_plf_data->dev_data[i]); + } + + err = mfd_add_devices(&pdev->dev, 0, ab5500_devs, + ARRAY_SIZE(ab5500_devs), NULL, + ab5500_plf_data->irq.base); + if (err) { + dev_err(&pdev->dev, "ab5500_mfd_add_device error\n"); + goto exit_no_detect; + } + + err = ab5500_setup(ab, ab5500_plf_data->init_settings, + ab5500_plf_data->init_settings_sz); + if (err) { + dev_err(&pdev->dev, "ab5500_setup error\n"); + goto exit_no_detect; + } + + ab5500_setup_debugfs(ab); + + dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]); + return 0; + +exit_no_detect: + kfree(ab); + return err; +} + +static int __exit ab5500_remove(struct platform_device *pdev) +{ + struct ab5500 *ab = platform_get_drvdata(pdev); + + ab5500_remove_debugfs(); + mfd_remove_devices(&pdev->dev); + kfree(ab); + return 0; +} + +static struct platform_driver ab5500_driver = { + .driver = { + .name = "ab5500-core", + .owner = THIS_MODULE, + }, + .remove = __exit_p(ab5500_remove), +}; + +static int __init ab5500_core_init(void) +{ + return platform_driver_probe(&ab5500_driver, ab5500_probe); +} + +static void __exit ab5500_core_exit(void) +{ + platform_driver_unregister(&ab5500_driver); +} + +subsys_initcall(ab5500_core_init); +module_exit(ab5500_core_exit); + +MODULE_AUTHOR("Mattias Wallin "); +MODULE_DESCRIPTION("AB5500 core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/ab5500/ab5500.h b/include/linux/mfd/ab5500/ab5500.h new file mode 100644 index 00000000000..a720051ae93 --- /dev/null +++ b/include/linux/mfd/ab5500/ab5500.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) ST-Ericsson 2011 + * + * License Terms: GNU General Public License v2 + */ +#ifndef MFD_AB5500_H +#define MFD_AB5500_H + +#include + +enum ab5500_devid { + AB5500_DEVID_ADC, + AB5500_DEVID_LEDS, + AB5500_DEVID_POWER, + AB5500_DEVID_REGULATORS, + AB5500_DEVID_SIM, + AB5500_DEVID_RTC, + AB5500_DEVID_CHARGER, + AB5500_DEVID_FUELGAUGE, + AB5500_DEVID_VIBRATOR, + AB5500_DEVID_CODEC, + AB5500_DEVID_USB, + AB5500_DEVID_OTP, + AB5500_DEVID_VIDEO, + AB5500_DEVID_DBIECI, + AB5500_DEVID_ONSWA, + AB5500_NUM_DEVICES, +}; + +enum ab5500_banks { + AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP = 0, + AB5500_BANK_VDDDIG_IO_I2C_CLK_TST = 1, + AB5500_BANK_VDENC = 2, + AB5500_BANK_SIM_USBSIM = 3, + AB5500_BANK_LED = 4, + AB5500_BANK_ADC = 5, + AB5500_BANK_RTC = 6, + AB5500_BANK_STARTUP = 7, + AB5500_BANK_DBI_ECI = 8, + AB5500_BANK_CHG = 9, + AB5500_BANK_FG_BATTCOM_ACC = 10, + AB5500_BANK_USB = 11, + AB5500_BANK_IT = 12, + AB5500_BANK_VIBRA = 13, + AB5500_BANK_AUDIO_HEADSETUSB = 14, + AB5500_NUM_BANKS = 15, +}; + +enum ab5500_banks_addr { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP = 0x4A, + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST = 0x4B, + AB5500_ADDR_VDENC = 0x06, + AB5500_ADDR_SIM_USBSIM = 0x04, + AB5500_ADDR_LED = 0x10, + AB5500_ADDR_ADC = 0x0A, + AB5500_ADDR_RTC = 0x0F, + AB5500_ADDR_STARTUP = 0x03, + AB5500_ADDR_DBI_ECI = 0x07, + AB5500_ADDR_CHG = 0x0B, + AB5500_ADDR_FG_BATTCOM_ACC = 0x0C, + AB5500_ADDR_USB = 0x05, + AB5500_ADDR_IT = 0x0E, + AB5500_ADDR_VIBRA = 0x02, + AB5500_ADDR_AUDIO_HEADSETUSB = 0x0D, +}; + +/* + * Interrupt register offsets + * Bank : 0x0E + */ +#define AB5500_IT_SOURCE0_REG 0x20 +#define AB5500_IT_SOURCE1_REG 0x21 +#define AB5500_IT_SOURCE2_REG 0x22 +#define AB5500_IT_SOURCE3_REG 0x23 +#define AB5500_IT_SOURCE4_REG 0x24 +#define AB5500_IT_SOURCE5_REG 0x25 +#define AB5500_IT_SOURCE6_REG 0x26 +#define AB5500_IT_SOURCE7_REG 0x27 +#define AB5500_IT_SOURCE8_REG 0x28 +#define AB5500_IT_SOURCE9_REG 0x29 +#define AB5500_IT_SOURCE10_REG 0x2A +#define AB5500_IT_SOURCE11_REG 0x2B +#define AB5500_IT_SOURCE12_REG 0x2C +#define AB5500_IT_SOURCE13_REG 0x2D +#define AB5500_IT_SOURCE14_REG 0x2E +#define AB5500_IT_SOURCE15_REG 0x2F +#define AB5500_IT_SOURCE16_REG 0x30 +#define AB5500_IT_SOURCE17_REG 0x31 +#define AB5500_IT_SOURCE18_REG 0x32 +#define AB5500_IT_SOURCE19_REG 0x33 +#define AB5500_IT_SOURCE20_REG 0x34 +#define AB5500_IT_SOURCE21_REG 0x35 +#define AB5500_IT_SOURCE22_REG 0x36 +#define AB5500_IT_SOURCE23_REG 0x37 + +#define AB5500_NUM_IRQ_REGS 23 + +/** + * struct ab5500 + * @access_mutex: lock out concurrent accesses to the AB registers + * @dev: a pointer to the device struct for this chip driver + * @ab5500_irq: the analog baseband irq + * @irq_base: the platform configuration irq base for subdevices + * @chip_name: name of this chip variant + * @chip_id: 8 bit chip ID for this chip variant + * @irq_lock: a lock to protect the mask + * @abb_events: a local bit mask of the prcmu wakeup events + * @event_mask: a local copy of the mask event registers + * @last_event_mask: a copy of the last event_mask written to hardware + * @startup_events: a copy of the first reading of the event registers + * @startup_events_read: whether the first events have been read + */ +struct ab5500 { + struct mutex access_mutex; + struct device *dev; + unsigned int ab5500_irq; + unsigned int irq_base; + char chip_name[32]; + u8 chip_id; + struct mutex irq_lock; + u32 abb_events; + u8 mask[AB5500_NUM_IRQ_REGS]; + u8 oldmask[AB5500_NUM_IRQ_REGS]; + u8 startup_events[AB5500_NUM_IRQ_REGS]; + bool startup_events_read; +#ifdef CONFIG_DEBUG_FS + unsigned int debug_bank; + unsigned int debug_address; +#endif +}; + +struct ab5500_platform_data { + struct {unsigned int base; unsigned int count; } irq; + void *dev_data[AB5500_NUM_DEVICES]; + struct abx500_init_settings *init_settings; + unsigned int init_settings_sz; + bool pm_power_off; +}; + +#endif /* MFD_AB5500_H */ diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 896b5e47f16..79ec2c7b5fa 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -31,8 +31,8 @@ #define AB3100_R2B 0xc8 #define AB3550_P1A 0x10 #define AB5500_1_0 0x20 -#define AB5500_2_0 0x21 -#define AB5500_2_1 0x22 +#define AB5500_1_1 0x21 +#define AB5500_2_0 0x24 /* AB8500 CIDs*/ #define AB8500_CUTEARLY 0x00 -- cgit v1.2.3-70-g09d2 From 94be70d4b279ba1b35119a3340833ffcc798c2e2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 Sep 2011 08:22:33 +0200 Subject: mfd: Break out ab5500 debugfs code This breaks the debugfs portions of the AB5500 driver into its own file. Split off a _raw function to access registers since we don't want to expose a generically named function globally. Move all required data structures to a shared ab5500-core.h file. Cc: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/ab5500-core.c | 907 +------------------------------------------ drivers/mfd/ab5500-core.h | 87 +++++ drivers/mfd/ab5500-debugfs.c | 806 ++++++++++++++++++++++++++++++++++++++ drivers/mfd/ab5500-debugfs.h | 22 ++ 6 files changed, 941 insertions(+), 890 deletions(-) create mode 100644 drivers/mfd/ab5500-core.h create mode 100644 drivers/mfd/ab5500-debugfs.c create mode 100644 drivers/mfd/ab5500-debugfs.h (limited to 'drivers/mfd/Kconfig') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8594de8b86a..f22bd2f0ecc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -572,6 +572,14 @@ config AB5500_CORE chip. This connects to the db5500 chip via the I2C bus via PRCMU. This chip embeds various other multimedia funtionalities as well. +config AB5500_DEBUG + bool "Enable debug info via debugfs" + depends on AB5500_CORE && DEBUG_FS + default y if DEBUG_FS + help + Select this option if you want debug information from the AB5500 + using the debug filesystem, debugfs. + config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 457fed8474c..7ed553d8157 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o obj-$(CONFIG_AB5500_CORE) += ab5500-core.o +obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c index afec0f2ede4..4175544b491 100644 --- a/drivers/mfd/ab5500-core.c +++ b/drivers/mfd/ab5500-core.c @@ -21,9 +21,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -33,87 +30,13 @@ #include #include +#include "ab5500-core.h" +#include "ab5500-debugfs.h" + #define AB5500_NUM_EVENT_REG 23 #define AB5500_IT_LATCH0_REG 0x40 #define AB5500_IT_MASK0_REG 0x60 -/* Read/write operation values. */ -#define AB5500_PERM_RD (0x01) -#define AB5500_PERM_WR (0x02) - -/* Read/write permissions. */ -#define AB5500_PERM_RO (AB5500_PERM_RD) -#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) - -#define AB5500_MASK_BASE (0x60) -#define AB5500_MASK_END (0x79) -#define AB5500_CHIP_ID (0x20) - -/** - * struct ab5500_bank - * @slave_addr: I2C slave_addr found in AB5500 specification - * @name: Documentation name of the bank. For reference - */ -struct ab5500_bank { - u8 slave_addr; - const char *name; -}; - -static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { - [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { - AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, - [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { - AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, - [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, - [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, - [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, - [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, - [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, - [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, - [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, - [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, - [AB5500_BANK_FG_BATTCOM_ACC] = { - AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, - [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, - [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, - [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, - [AB5500_BANK_AUDIO_HEADSETUSB] = { - AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, -}; - -/** - * struct ab5500_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab5500_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab5500_i2c_ranges - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab5500_i2c_ranges { - u8 nranges; - u8 bankid; - const struct ab5500_reg_range *range; -}; - -/** - * struct ab5500_i2c_banks - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab5500_i2c_banks { - u8 nbanks; - const struct ab5500_i2c_ranges *bank; -}; - /* * Permissible register ranges for reading and writing per device and bank. * @@ -1073,8 +996,9 @@ static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { /* * Functionality for getting/setting register values. */ -static int get_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, - u8 *value) +int ab5500_get_register_interruptible_raw(struct ab5500 *ab, + u8 bank, u8 reg, + u8 *value) { int err; @@ -1121,7 +1045,7 @@ out: return err; } -static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, +int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { int err = 0; @@ -1158,7 +1082,8 @@ static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, static int set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value) { - return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value); + return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg, + 0xff, value); } /* @@ -1261,7 +1186,7 @@ static int ab5500_mask_and_set_register_interruptible(struct device *dev, return -EINVAL; ab = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab, bank, reg, + return ab5500_mask_and_set_register_interruptible_raw(ab, bank, reg, bitmask, bitvalues); } @@ -1283,7 +1208,7 @@ static int ab5500_get_register_interruptible(struct device *dev, u8 bank, return -EINVAL; ab = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab, bank, reg, value); + return ab5500_get_register_interruptible_raw(ab, bank, reg, value); } static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, @@ -1327,806 +1252,6 @@ static struct abx500_ops ab5500_ops = { .startup_irq_enabled = NULL, }; -#ifdef CONFIG_DEBUG_FS -static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { - [AB5500_BANK_LED] = { - .bankid = AB5500_BANK_LED, - .nranges = 1, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x0C, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_ADC] = { - .bankid = AB5500_BANK_ADC, - .nranges = 6, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x1F, - .last = 0x22, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x23, - .last = 0x24, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x26, - .last = 0x2D, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x2F, - .last = 0x34, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x37, - .last = 0x57, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x58, - .last = 0x58, - .perm = AB5500_PERM_RO, - }, - }, - }, - [AB5500_BANK_RTC] = { - .bankid = AB5500_BANK_RTC, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x04, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x06, - .last = 0x0C, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_STARTUP] = { - .bankid = AB5500_BANK_STARTUP, - .nranges = 12, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x01, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1F, - .last = 0x1F, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x2E, - .last = 0x2E, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x2F, - .last = 0x30, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x50, - .last = 0x51, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x60, - .last = 0x61, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x66, - .last = 0x8A, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x8C, - .last = 0x96, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xAA, - .last = 0xB4, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xB7, - .last = 0xBF, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xC1, - .last = 0xCA, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xD3, - .last = 0xE0, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_DBI_ECI] = { - .bankid = AB5500_BANK_DBI_ECI, - .nranges = 3, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x07, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x10, - .last = 0x10, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x13, - .last = 0x13, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_CHG] = { - .bankid = AB5500_BANK_CHG, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x11, - .last = 0x11, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x12, - .last = 0x1B, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_FG_BATTCOM_ACC] = { - .bankid = AB5500_BANK_FG_BATTCOM_ACC, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x0B, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x0C, - .last = 0x10, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_USB] = { - .bankid = AB5500_BANK_USB, - .nranges = 12, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x01, - .last = 0x01, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x80, - .last = 0x83, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x87, - .last = 0x8A, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x8B, - .last = 0x8B, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x91, - .last = 0x92, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x93, - .last = 0x93, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x94, - .last = 0x94, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xA8, - .last = 0xB0, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xB2, - .last = 0xB2, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xB4, - .last = 0xBC, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xBF, - .last = 0xBF, - .perm = AB5500_PERM_RO, - }, - { - .first = 0xC1, - .last = 0xC5, - .perm = AB5500_PERM_RO, - }, - }, - }, - [AB5500_BANK_IT] = { - .bankid = AB5500_BANK_IT, - .nranges = 4, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x02, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x20, - .last = 0x36, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x40, - .last = 0x56, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x60, - .last = 0x76, - .perm = AB5500_PERM_RO, - }, - }, - }, - [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { - .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, - .nranges = 7, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x02, - .last = 0x02, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x12, - .last = 0x12, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x30, - .last = 0x34, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x40, - .last = 0x44, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x50, - .last = 0x54, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x60, - .last = 0x64, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x70, - .last = 0x74, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { - .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, - .nranges = 13, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x01, - .last = 0x01, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x02, - .last = 0x02, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x0D, - .last = 0x0F, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1C, - .last = 0x1C, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1E, - .last = 0x1E, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x20, - .last = 0x21, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x25, - .last = 0x25, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x28, - .last = 0x2A, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x30, - .last = 0x33, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x40, - .last = 0x43, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x50, - .last = 0x53, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x60, - .last = 0x63, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x70, - .last = 0x73, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_VIBRA] = { - .bankid = AB5500_BANK_VIBRA, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x10, - .last = 0x13, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xFE, - .last = 0xFE, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_AUDIO_HEADSETUSB] = { - .bankid = AB5500_BANK_AUDIO_HEADSETUSB, - .nranges = 2, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x48, - .perm = AB5500_PERM_RW, - }, - { - .first = 0xEB, - .last = 0xFB, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_SIM_USBSIM] = { - .bankid = AB5500_BANK_SIM_USBSIM, - .nranges = 1, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x13, - .last = 0x19, - .perm = AB5500_PERM_RW, - }, - }, - }, - [AB5500_BANK_VDENC] = { - .bankid = AB5500_BANK_VDENC, - .nranges = 12, - .range = (struct ab5500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x09, - .last = 0x09, - .perm = AB5500_PERM_RO, - }, - { - .first = 0x0A, - .last = 0x12, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x15, - .last = 0x19, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x1B, - .last = 0x21, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x27, - .last = 0x2C, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x41, - .last = 0x41, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x45, - .last = 0x5B, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x5D, - .last = 0x5D, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x69, - .last = 0x69, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x6C, - .last = 0x6D, - .perm = AB5500_PERM_RW, - }, - { - .first = 0x80, - .last = 0x81, - .perm = AB5500_PERM_RW, - }, - }, - }, -}; -static int ab5500_registers_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - unsigned int i; - u8 bank = (u8)ab->debug_bank; - - seq_printf(s, "ab5500 register values:\n"); - for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { - seq_printf(s, " bank %u, %s (0x%x):\n", bank, - bankinfo[bank].name, - bankinfo[bank].slave_addr); - for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { - u8 reg; - int err; - - for (reg = ab5500_reg_ranges[bank].range[i].first; - reg <= ab5500_reg_ranges[bank].range[i].last; - reg++) { - u8 value; - - err = get_register_interruptible(ab, bank, reg, - &value); - if (err < 0) { - dev_err(ab->dev, "get_reg failed %d" - "bank 0x%x reg 0x%x\n", - err, bank, reg); - return err; - } - - err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", - bank, reg, value); - if (err < 0) { - dev_err(ab->dev, - "seq_printf overflow\n"); - /* - * Error is not returned here since - * the output is wanted in any case - */ - return 0; - } - } - } - } - return 0; -} - -static int ab5500_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_registers_print, inode->i_private); -} - -static const struct file_operations ab5500_registers_fops = { - .open = ab5500_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab5500_bank_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - - seq_printf(s, "%d\n", ab->debug_bank); - return 0; -} - -static int ab5500_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_bank_print, inode->i_private); -} - -static ssize_t ab5500_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_bank; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf) - 1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_bank); - if (err) - return -EINVAL; - - if (user_bank >= AB5500_NUM_BANKS) { - dev_err(ab->dev, - "debugfs error input > number of banks\n"); - return -EINVAL; - } - - ab->debug_bank = user_bank; - - return buf_size; -} - -static int ab5500_address_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - - seq_printf(s, "0x%02X\n", ab->debug_address); - return 0; -} - -static int ab5500_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_address_print, inode->i_private); -} - -static ssize_t ab5500_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_address; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf) - 1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_address); - if (err) - return -EINVAL; - if (user_address > 0xff) { - dev_err(ab->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - ab->debug_address = user_address; - return buf_size; -} - -static int ab5500_val_print(struct seq_file *s, void *p) -{ - struct ab5500 *ab = s->private; - int err; - u8 regvalue; - - err = get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) { - dev_err(ab->dev, "get_reg failed %d, bank 0x%x" - ", reg 0x%x\n", err, ab->debug_bank, - ab->debug_address); - return -EINVAL; - } - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab5500_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab5500_val_print, inode->i_private); -} - -static ssize_t ab5500_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_val; - int err; - u8 regvalue; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_val); - if (err) - return -EINVAL; - if (user_val > 0xff) { - dev_err(ab->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = mask_and_set_register_interruptible( - ab, (u8)ab->debug_bank, - (u8)ab->debug_address, 0xFF, (u8)user_val); - if (err) - return -EINVAL; - - get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - - return buf_size; -} - -static const struct file_operations ab5500_bank_fops = { - .open = ab5500_bank_open, - .write = ab5500_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab5500_address_fops = { - .open = ab5500_address_open, - .write = ab5500_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab5500_val_fops = { - .open = ab5500_val_open, - .write = ab5500_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static struct dentry *ab5500_dir; -static struct dentry *ab5500_reg_file; -static struct dentry *ab5500_bank_file; -static struct dentry *ab5500_address_file; -static struct dentry *ab5500_val_file; - -static inline void ab5500_setup_debugfs(struct ab5500 *ab) -{ - ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; - ab->debug_address = AB5500_CHIP_ID; - - ab5500_dir = debugfs_create_dir("ab5500", NULL); - if (!ab5500_dir) - goto exit_no_debugfs; - - ab5500_reg_file = debugfs_create_file("all-bank-registers", - S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); - if (!ab5500_reg_file) - goto exit_destroy_dir; - - ab5500_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); - if (!ab5500_bank_file) - goto exit_destroy_reg; - - ab5500_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); - if (!ab5500_address_file) - goto exit_destroy_bank; - - ab5500_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); - if (!ab5500_val_file) - goto exit_destroy_address; - - return; - -exit_destroy_address: - debugfs_remove(ab5500_address_file); -exit_destroy_bank: - debugfs_remove(ab5500_bank_file); -exit_destroy_reg: - debugfs_remove(ab5500_reg_file); -exit_destroy_dir: - debugfs_remove(ab5500_dir); -exit_no_debugfs: - dev_err(ab->dev, "failed to create debugfs entries.\n"); - return; -} - -static inline void ab5500_remove_debugfs(void) -{ - debugfs_remove(ab5500_val_file); - debugfs_remove(ab5500_address_file); - debugfs_remove(ab5500_bank_file); - debugfs_remove(ab5500_reg_file); - debugfs_remove(ab5500_dir); -} - -#else /* !CONFIG_DEBUG_FS */ -static inline void ab5500_setup_debugfs(struct ab5500 *ab) -{ -} -static inline void ab5500_remove_debugfs(void) -{ -} -#endif - /* * ab5500_setup : Basic set-up, datastructure creation/destruction * and I2C interface.This sets up a default config @@ -2142,7 +1267,7 @@ static int __init ab5500_setup(struct ab5500 *ab, int i; for (i = 0; i < size; i++) { - err = mask_and_set_register_interruptible(ab, + err = ab5500_mask_and_set_register_interruptible_raw(ab, settings[i].bank, settings[i].reg, 0xFF, settings[i].setting); @@ -2205,8 +1330,9 @@ static int __init ab5500_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ab); /* Read chip ID register */ - err = get_register_interruptible(ab, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, - AB5500_CHIP_ID, &ab->chip_id); + err = ab5500_get_register_interruptible_raw(ab, + AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + AB5500_CHIP_ID, &ab->chip_id); if (err) { dev_err(&pdev->dev, "could not communicate with the analog " "baseband chip\n"); @@ -2233,7 +1359,8 @@ static int __init ab5500_probe(struct platform_device *pdev) u8 maskreg = AB5500_IT_MASK0_REG + i; u8 val; - get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val); + ab5500_get_register_interruptible_raw(ab, AB5500_BANK_IT, + latchreg, &val); set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff); ab->mask[i] = ab->oldmask[i] = 0xff; } diff --git a/drivers/mfd/ab5500-core.h b/drivers/mfd/ab5500-core.h new file mode 100644 index 00000000000..63b30b17e4f --- /dev/null +++ b/drivers/mfd/ab5500-core.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Shared definitions and data structures for the AB5500 MFD driver + */ + +/* Read/write operation values. */ +#define AB5500_PERM_RD (0x01) +#define AB5500_PERM_WR (0x02) + +/* Read/write permissions. */ +#define AB5500_PERM_RO (AB5500_PERM_RD) +#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) + +#define AB5500_MASK_BASE (0x60) +#define AB5500_MASK_END (0x79) +#define AB5500_CHIP_ID (0x20) + +/** + * struct ab5500_reg_range + * @first: the first address of the range + * @last: the last address of the range + * @perm: access permissions for the range + */ +struct ab5500_reg_range { + u8 first; + u8 last; + u8 perm; +}; + +/** + * struct ab5500_i2c_ranges + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_ranges { + u8 nranges; + u8 bankid; + const struct ab5500_reg_range *range; +}; + +/** + * struct ab5500_i2c_banks + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_banks { + u8 nbanks; + const struct ab5500_i2c_ranges *bank; +}; + +/** + * struct ab5500_bank + * @slave_addr: I2C slave_addr found in AB5500 specification + * @name: Documentation name of the bank. For reference + */ +struct ab5500_bank { + u8 slave_addr; + const char *name; +}; + +static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, + [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, + [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, + [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, + [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, + [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, + [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, + [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, + [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, + [AB5500_BANK_FG_BATTCOM_ACC] = { + AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, + [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, + [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, + [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, +}; + +int ab5500_get_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg, + u8 *value); +int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues); diff --git a/drivers/mfd/ab5500-debugfs.c b/drivers/mfd/ab5500-debugfs.c new file mode 100644 index 00000000000..6be1fe6b5f9 --- /dev/null +++ b/drivers/mfd/ab5500-debugfs.c @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Debugfs support for the AB5500 MFD driver + */ + +#include +#include +#include +#include +#include + +#include "ab5500-core.h" +#include "ab5500-debugfs.h" + +static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { + [AB5500_BANK_LED] = { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_ADC] = { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_RTC] = { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_STARTUP] = { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_DBI_ECI] = { + .bankid = AB5500_BANK_DBI_ECI, + .nranges = 3, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x07, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x10, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x13, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_CHG] = { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_FG_BATTCOM_ACC] = { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_USB] = { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_IT] = { + .bankid = AB5500_BANK_IT, + .nranges = 4, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x20, + .last = 0x36, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x40, + .last = 0x56, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x60, + .last = 0x76, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, + .nranges = 7, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x12, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x44, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x54, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x64, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x74, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 13, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0D, + .last = 0x0F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1C, + .last = 0x1C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1E, + .last = 0x1E, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x20, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x25, + .last = 0x25, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x28, + .last = 0x2A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x33, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x43, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x53, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x63, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x73, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIBRA] = { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_SIM_USBSIM] = { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VDENC] = { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, +}; + +static int ab5500_registers_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + unsigned int i; + u8 bank = (u8)ab->debug_bank; + + seq_printf(s, "ab5500 register values:\n"); + for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { + seq_printf(s, " bank %u, %s (0x%x):\n", bank, + bankinfo[bank].name, + bankinfo[bank].slave_addr); + for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { + u8 reg; + int err; + + for (reg = ab5500_reg_ranges[bank].range[i].first; + reg <= ab5500_reg_ranges[bank].range[i].last; + reg++) { + u8 value; + + err = ab5500_get_register_interruptible_raw(ab, + bank, reg, + &value); + if (err < 0) { + dev_err(ab->dev, "get_reg failed %d" + "bank 0x%x reg 0x%x\n", + err, bank, reg); + return err; + } + + err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", + bank, reg, value); + if (err < 0) { + dev_err(ab->dev, + "seq_printf overflow\n"); + /* + * Error is not returned here since + * the output is wanted in any case + */ + return 0; + } + } + } + } + return 0; +} + +static int ab5500_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_registers_print, inode->i_private); +} + +static const struct file_operations ab5500_registers_fops = { + .open = ab5500_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab5500_bank_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); + return 0; +} + +static int ab5500_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_bank_print, inode->i_private); +} + +static ssize_t ab5500_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB5500_NUM_BANKS) { + dev_err(ab->dev, + "debugfs error input > number of banks\n"); + return -EINVAL; + } + + ab->debug_bank = user_bank; + + return buf_size; +} + +static int ab5500_address_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab5500_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_address_print, inode->i_private); +} + +static ssize_t ab5500_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + ab->debug_address = user_address; + return buf_size; +} + +static int ab5500_val_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + int err; + u8 regvalue; + + err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) { + dev_err(ab->dev, "get_reg failed %d, bank 0x%x" + ", reg 0x%x\n", err, ab->debug_bank, + ab->debug_address); + return -EINVAL; + } + seq_printf(s, "0x%02X\n", regvalue); + + return 0; +} + +static int ab5500_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_val_print, inode->i_private); +} + +static ssize_t ab5500_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = ab5500_mask_and_set_register_interruptible_raw( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + + return buf_size; +} + +static const struct file_operations ab5500_bank_fops = { + .open = ab5500_bank_open, + .write = ab5500_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_address_fops = { + .open = ab5500_address_open, + .write = ab5500_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_val_fops = { + .open = ab5500_val_open, + .write = ab5500_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab5500_dir; +static struct dentry *ab5500_reg_file; +static struct dentry *ab5500_bank_file; +static struct dentry *ab5500_address_file; +static struct dentry *ab5500_val_file; + +void __init ab5500_setup_debugfs(struct ab5500 *ab) +{ + ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; + ab->debug_address = AB5500_CHIP_ID; + + ab5500_dir = debugfs_create_dir("ab5500", NULL); + if (!ab5500_dir) + goto exit_no_debugfs; + + ab5500_reg_file = debugfs_create_file("all-bank-registers", + S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); + if (!ab5500_reg_file) + goto exit_destroy_dir; + + ab5500_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); + if (!ab5500_bank_file) + goto exit_destroy_reg; + + ab5500_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); + if (!ab5500_address_file) + goto exit_destroy_bank; + + ab5500_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); + if (!ab5500_val_file) + goto exit_destroy_address; + + return; + +exit_destroy_address: + debugfs_remove(ab5500_address_file); +exit_destroy_bank: + debugfs_remove(ab5500_bank_file); +exit_destroy_reg: + debugfs_remove(ab5500_reg_file); +exit_destroy_dir: + debugfs_remove(ab5500_dir); +exit_no_debugfs: + dev_err(ab->dev, "failed to create debugfs entries.\n"); + return; +} + +void __exit ab5500_remove_debugfs(void) +{ + debugfs_remove(ab5500_val_file); + debugfs_remove(ab5500_address_file); + debugfs_remove(ab5500_bank_file); + debugfs_remove(ab5500_reg_file); + debugfs_remove(ab5500_dir); +} diff --git a/drivers/mfd/ab5500-debugfs.h b/drivers/mfd/ab5500-debugfs.h new file mode 100644 index 00000000000..7330a9b6afa --- /dev/null +++ b/drivers/mfd/ab5500-debugfs.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Debugfs interface to the AB5500 core driver + */ + +#ifdef CONFIG_DEBUG_FS + +void ab5500_setup_debugfs(struct ab5500 *ab); +void ab5500_remove_debugfs(void); + +#else /* !CONFIG_DEBUG_FS */ + +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ +} + +static inline void ab5500_remove_debugfs(void) +{ +} + +#endif -- cgit v1.2.3-70-g09d2 From 8959e74399c798b45c0f5d477972b927c28f8dc9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Sep 2011 11:45:30 +0200 Subject: mfd: Delete ab3550 driver The AB3550 never passed the prototype stage. Instead it was used as a precursor to AB5500 for testing basic building blocks used in that chip, since they had large similarities. Since AB3550 will not see the light of day in product form and since the prototypes are no longer used, let's delete the driver and any references to it. Cc: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- arch/arm/mach-u300/i2c.c | 51 +- arch/arm/mach-u300/include/mach/irqs.h | 7 - drivers/mfd/Kconfig | 14 - drivers/mfd/Makefile | 1 - drivers/mfd/ab3550-core.c | 1380 -------------------------------- include/linux/mfd/abx500.h | 48 +- 6 files changed, 2 insertions(+), 1499 deletions(-) delete mode 100644 drivers/mfd/ab3550-core.c (limited to 'drivers/mfd/Kconfig') diff --git a/arch/arm/mach-u300/i2c.c b/arch/arm/mach-u300/i2c.c index f0394baa11f..5140deeddf7 100644 --- a/arch/arm/mach-u300/i2c.c +++ b/arch/arm/mach-u300/i2c.c @@ -256,57 +256,8 @@ static struct ab3100_platform_data ab3100_plf_data = { }; #endif -#ifdef CONFIG_AB3550_CORE -static struct abx500_init_settings ab3550_init_settings[] = { - { - .bank = 0, - .reg = AB3550_IMR1, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR2, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR3, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR4, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR5, - /* The two most significant bits are not used */ - .setting = 0x3f - }, -}; - -static struct ab3550_platform_data ab3550_plf_data = { - .irq = { - .base = IRQ_AB3550_BASE, - .count = (IRQ_AB3550_END - IRQ_AB3550_BASE + 1), - }, - .dev_data = { - }, - .init_settings = ab3550_init_settings, - .init_settings_sz = ARRAY_SIZE(ab3550_init_settings), -}; -#endif - static struct i2c_board_info __initdata bus0_i2c_board_info[] = { -#if defined(CONFIG_AB3550_CORE) - { - .type = "ab3550", - .addr = 0x4A, - .irq = IRQ_U300_IRQ0_EXT, - .platform_data = &ab3550_plf_data, - }, -#elif defined(CONFIG_AB3100_CORE) +#ifdef CONFIG_AB3100_CORE { .type = "ab3100", .addr = 0x48, diff --git a/arch/arm/mach-u300/include/mach/irqs.h b/arch/arm/mach-u300/include/mach/irqs.h index 09b1b28fa8f..a6867b12773 100644 --- a/arch/arm/mach-u300/include/mach/irqs.h +++ b/arch/arm/mach-u300/include/mach/irqs.h @@ -109,13 +109,6 @@ #define U300_NR_IRQS 48 #endif -#ifdef CONFIG_AB3550_CORE -#define IRQ_AB3550_BASE (U300_NR_IRQS) -#define IRQ_AB3550_END (IRQ_AB3550_BASE + 37) - -#define NR_IRQS (IRQ_AB3550_END + 1) -#else #define NR_IRQS U300_NR_IRQS -#endif #endif diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f22bd2f0ecc..74d4893a8f2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -616,20 +616,6 @@ config AB8500_GPADC help AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage -config AB3550_CORE - bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions" - select MFD_CORE - depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE - help - Select this to enable the AB3550 Mixed Signal IC core - functionality. This connects to a AB3550 on the I2C bus - and expose a number of symbols needed for dependent devices - to read and write registers and subscribe to events from - this multi-functional IC. This is needed to use other features - of the AB3550 such as battery-backed RTC, charging control, - LEDs, vibrator, system power and temperature, power management - and ALSA sound. - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7ed553d8157..b2292eb7524 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -79,7 +79,6 @@ 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_AB3550_CORE) += ab3550-core.o obj-$(CONFIG_AB5500_CORE) += ab5500-core.o obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c deleted file mode 100644 index 882ea7192d8..00000000000 --- a/drivers/mfd/ab3550-core.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * Copyright (C) 2007-2010 ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - * Low-level core for exclusive access to the AB3550 IC on the I2C bus - * and some basic chip-configuration. - * Author: Bengt Jonsson - * Author: Mattias Nilsson - * Author: Mattias Wallin - * Author: Rickard Andersson - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AB3550_NAME_STRING "ab3550" -#define AB3550_ID_FORMAT_STRING "AB3550 %s" -#define AB3550_NUM_BANKS 2 -#define AB3550_NUM_EVENT_REG 5 - -/* These are the only registers inside AB3550 used in this main file */ - -/* Chip ID register */ -#define AB3550_CID_REG 0x20 - -/* Interrupt event registers */ -#define AB3550_EVENT_BANK 0 -#define AB3550_EVENT_REG 0x22 - -/* Read/write operation values. */ -#define AB3550_PERM_RD (0x01) -#define AB3550_PERM_WR (0x02) - -/* Read/write permissions. */ -#define AB3550_PERM_RO (AB3550_PERM_RD) -#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR) - -/** - * struct ab3550 - * @access_mutex: lock out concurrent accesses to the AB registers - * @i2c_client: I2C client for this chip - * @chip_name: name of this chip variant - * @chip_id: 8 bit chip ID for this chip variant - * @mask_work: a worker for writing to mask registers - * @event_lock: a lock to protect the event_mask - * @event_mask: a local copy of the mask event registers - * @startup_events: a copy of the first reading of the event registers - * @startup_events_read: whether the first events have been read - */ -struct ab3550 { - struct mutex access_mutex; - struct i2c_client *i2c_client[AB3550_NUM_BANKS]; - char chip_name[32]; - u8 chip_id; - struct work_struct mask_work; - spinlock_t event_lock; - u8 event_mask[AB3550_NUM_EVENT_REG]; - u8 startup_events[AB3550_NUM_EVENT_REG]; - bool startup_events_read; -#ifdef CONFIG_DEBUG_FS - unsigned int debug_bank; - unsigned int debug_address; -#endif -}; - -/** - * struct ab3550_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab3550_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab3550_reg_ranges - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab3550_reg_ranges { - u8 count; - const struct ab3550_reg_range *range; -}; - -/* - * Permissible register ranges for reading and writing per device and bank. - * - * The ranges must be listed in increasing address order, and no overlaps are - * allowed. It is assumed that write permission implies read permission - * (i.e. only RO and RW permissions should be used). Ranges with write - * permission must not be split up. - */ - -#define NO_RANGE {.count = 0, .range = NULL,} - -static struct -ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = { - [AB3550_DEVID_DAC] = { - NO_RANGE, - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0xb0, - .last = 0xba, - .perm = AB3550_PERM_RW, - }, - { - .first = 0xbc, - .last = 0xc3, - .perm = AB3550_PERM_RW, - }, - }, - }, - }, - [AB3550_DEVID_LEDS] = { - NO_RANGE, - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x5a, - .last = 0x88, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x8a, - .last = 0xad, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_POWER] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_REGULATORS] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x69, - .last = 0xa3, - .perm = AB3550_PERM_RW, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x14, - .last = 0x16, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_SIM] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x14, - .last = 0x17, - .perm = AB3550_PERM_RW, - }, - } - - }, - }, - [AB3550_DEVID_UART] = { - NO_RANGE, - NO_RANGE, - }, - [AB3550_DEVID_RTC] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0c, - .perm = AB3550_PERM_RW, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_CHARGER] = { - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x10, - .last = 0x1a, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_ADC] = { - NO_RANGE, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x20, - .last = 0x56, - .perm = AB3550_PERM_RW, - }, - - } - }, - }, - [AB3550_DEVID_FUELGAUGE] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_VIBRATOR] = { - NO_RANGE, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x10, - .last = 0x13, - .perm = AB3550_PERM_RW, - }, - - } - }, - }, - [AB3550_DEVID_CODEC] = { - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x31, - .last = 0x63, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x65, - .last = 0x68, - .perm = AB3550_PERM_RW, - }, - } - }, - NO_RANGE, - }, -}; - -static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = { - [AB3550_DEVID_DAC] = { - .name = "ab3550-dac", - .id = AB3550_DEVID_DAC, - .num_resources = 0, - }, - [AB3550_DEVID_LEDS] = { - .name = "ab3550-leds", - .id = AB3550_DEVID_LEDS, - }, - [AB3550_DEVID_POWER] = { - .name = "ab3550-power", - .id = AB3550_DEVID_POWER, - }, - [AB3550_DEVID_REGULATORS] = { - .name = "ab3550-regulators", - .id = AB3550_DEVID_REGULATORS, - }, - [AB3550_DEVID_SIM] = { - .name = "ab3550-sim", - .id = AB3550_DEVID_SIM, - }, - [AB3550_DEVID_UART] = { - .name = "ab3550-uart", - .id = AB3550_DEVID_UART, - }, - [AB3550_DEVID_RTC] = { - .name = "ab3550-rtc", - .id = AB3550_DEVID_RTC, - }, - [AB3550_DEVID_CHARGER] = { - .name = "ab3550-charger", - .id = AB3550_DEVID_CHARGER, - }, - [AB3550_DEVID_ADC] = { - .name = "ab3550-adc", - .id = AB3550_DEVID_ADC, - .num_resources = 10, - .resources = (struct resource[]) { - { - .name = "TRIGGER-0", - .flags = IORESOURCE_IRQ, - .start = 16, - .end = 16, - }, - { - .name = "TRIGGER-1", - .flags = IORESOURCE_IRQ, - .start = 17, - .end = 17, - }, - { - .name = "TRIGGER-2", - .flags = IORESOURCE_IRQ, - .start = 18, - .end = 18, - }, - { - .name = "TRIGGER-3", - .flags = IORESOURCE_IRQ, - .start = 19, - .end = 19, - }, - { - .name = "TRIGGER-4", - .flags = IORESOURCE_IRQ, - .start = 20, - .end = 20, - }, - { - .name = "TRIGGER-5", - .flags = IORESOURCE_IRQ, - .start = 21, - .end = 21, - }, - { - .name = "TRIGGER-6", - .flags = IORESOURCE_IRQ, - .start = 22, - .end = 22, - }, - { - .name = "TRIGGER-7", - .flags = IORESOURCE_IRQ, - .start = 23, - .end = 23, - }, - { - .name = "TRIGGER-VBAT-TXON", - .flags = IORESOURCE_IRQ, - .start = 13, - .end = 13, - }, - { - .name = "TRIGGER-VBAT", - .flags = IORESOURCE_IRQ, - .start = 12, - .end = 12, - }, - }, - }, - [AB3550_DEVID_FUELGAUGE] = { - .name = "ab3550-fuelgauge", - .id = AB3550_DEVID_FUELGAUGE, - }, - [AB3550_DEVID_VIBRATOR] = { - .name = "ab3550-vibrator", - .id = AB3550_DEVID_VIBRATOR, - }, - [AB3550_DEVID_CODEC] = { - .name = "ab3550-codec", - .id = AB3550_DEVID_CODEC, - }, -}; - -/* - * I2C transactions with error messages. - */ -static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data, - u8 count) -{ - int err; - - err = i2c_master_send(ab->i2c_client[bank], data, count); - if (err < 0) { - dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err); - return err; - } - return 0; -} - -static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data, - u8 count) -{ - int err; - - err = i2c_master_recv(ab->i2c_client[bank], data, count); - if (err < 0) { - dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err); - return err; - } - return 0; -} - -/* - * Functionality for getting/setting register values. - */ -static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg, - u8 *value) -{ - int err; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - err = ab3550_i2c_master_send(ab, bank, ®, 1); - if (!err) - err = ab3550_i2c_master_recv(ab, bank, value, 1); - - mutex_unlock(&ab->access_mutex); - return err; -} - -static int get_register_page_interruptible(struct ab3550 *ab, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) -{ - int err; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - err = ab3550_i2c_master_send(ab, bank, &first_reg, 1); - if (!err) - err = ab3550_i2c_master_recv(ab, bank, regvals, numregs); - - mutex_unlock(&ab->access_mutex); - return err; -} - -static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) -{ - int err = 0; - - if (likely(bitmask)) { - u8 reg_bits[2] = {reg, 0}; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - if (bitmask == 0xFF) /* No need to read in this case. */ - reg_bits[1] = bitvalues; - else { /* Read and modify the register value. */ - u8 bits; - - err = ab3550_i2c_master_send(ab, bank, ®, 1); - if (err) - goto unlock_and_return; - err = ab3550_i2c_master_recv(ab, bank, &bits, 1); - if (err) - goto unlock_and_return; - reg_bits[1] = ((~bitmask & bits) | - (bitmask & bitvalues)); - } - /* Write the new value. */ - err = ab3550_i2c_master_send(ab, bank, reg_bits, 2); -unlock_and_return: - mutex_unlock(&ab->access_mutex); - } - return err; -} - -/* - * Read/write permission checking functions. - */ -static bool page_write_allowed(const struct ab3550_reg_ranges *ranges, - u8 first_reg, u8 last_reg) -{ - u8 i; - - if (last_reg < first_reg) - return false; - - for (i = 0; i < ranges->count; i++) { - if (first_reg < ranges->range[i].first) - break; - if ((last_reg <= ranges->range[i].last) && - (ranges->range[i].perm & AB3550_PERM_WR)) - return true; - } - return false; -} - -static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) -{ - return page_write_allowed(ranges, reg, reg); -} - -static bool page_read_allowed(const struct ab3550_reg_ranges *ranges, - u8 first_reg, u8 last_reg) -{ - u8 i; - - if (last_reg < first_reg) - return false; - /* Find the range (if it exists in the list) that includes first_reg. */ - for (i = 0; i < ranges->count; i++) { - if (first_reg < ranges->range[i].first) - return false; - if (first_reg <= ranges->range[i].last) - break; - } - /* Make sure that the entire range up to and including last_reg is - * readable. This may span several of the ranges in the list. - */ - while ((i < ranges->count) && - (ranges->range[i].perm & AB3550_PERM_RD)) { - if (last_reg <= ranges->range[i].last) - return true; - if ((++i >= ranges->count) || - (ranges->range[i].first != - (ranges->range[i - 1].last + 1))) { - break; - } - } - return false; -} - -static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) -{ - return page_read_allowed(ranges, reg, reg); -} - -/* - * The register access functionality. - */ -static int ab3550_get_chip_id(struct device *dev) -{ - struct ab3550 *ab = dev_get_drvdata(dev->parent); - return (int)ab->chip_id; -} - -static int ab3550_mask_and_set_register_interruptible(struct device *dev, - u8 bank, u8 reg, u8 bitmask, u8 bitvalues) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab, bank, reg, - bitmask, bitvalues); -} - -static int ab3550_set_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 value) -{ - return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, - value); -} - -static int ab3550_get_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 *value) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab, bank, reg, value); -} - -static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank], - first_reg, (first_reg + numregs - 1))) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return get_register_page_interruptible(ab, bank, first_reg, regvals, - numregs); -} - -static int ab3550_event_registers_startup_state_get(struct device *dev, - u8 *event) -{ - struct ab3550 *ab; - - ab = dev_get_drvdata(dev->parent); - if (!ab->startup_events_read) - return -EAGAIN; /* Try again later */ - - memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG); - return 0; -} - -static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) -{ - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - bool val; - - ab = irq_get_chip_data(irq); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq -= plf_data->irq.base; - val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0); - - return val; -} - -static struct abx500_ops ab3550_ops = { - .get_chip_id = ab3550_get_chip_id, - .get_register = ab3550_get_register_interruptible, - .set_register = ab3550_set_register_interruptible, - .get_register_page = ab3550_get_register_page_interruptible, - .set_register_page = NULL, - .mask_and_set_register = ab3550_mask_and_set_register_interruptible, - .event_registers_startup_state_get = - ab3550_event_registers_startup_state_get, - .startup_irq_enabled = ab3550_startup_irq_enabled, -}; - -static irqreturn_t ab3550_irq_handler(int irq, void *data) -{ - struct ab3550 *ab = data; - int err; - unsigned int i; - u8 e[AB3550_NUM_EVENT_REG]; - u8 *events; - unsigned long flags; - - events = (ab->startup_events_read ? e : ab->startup_events); - - err = get_register_page_interruptible(ab, AB3550_EVENT_BANK, - AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG); - if (err) - goto err_event_rd; - - if (!ab->startup_events_read) { - dev_info(&ab->i2c_client[0]->dev, - "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n", - ab->startup_events[0], ab->startup_events[1], - ab->startup_events[2], ab->startup_events[3], - ab->startup_events[4]); - ab->startup_events_read = true; - goto out; - } - - /* The two highest bits in event[4] are not used. */ - events[4] &= 0x3f; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) - events[i] &= ~ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { - u8 bit; - u8 event_reg; - - dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n", - i, events[i]); - - event_reg = events[i]; - for (bit = 0; event_reg; bit++, event_reg /= 2) { - if (event_reg % 2) { - unsigned int irq; - struct ab3550_platform_data *plf_data; - - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = plf_data->irq.base + (i * 8) + bit; - handle_nested_irq(irq); - } - } - } -out: - return IRQ_HANDLED; - -err_event_rd: - dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n"); - return IRQ_HANDLED; -} - -#ifdef CONFIG_DEBUG_FS -static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = { - { - .count = 6, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - }, - { - .first = 0x10, - .last = 0x1a, - }, - { - .first = 0x1e, - .last = 0x4f, - }, - { - .first = 0x51, - .last = 0x63, - }, - { - .first = 0x65, - .last = 0xa3, - }, - { - .first = 0xa5, - .last = 0xa8, - }, - } - }, - { - .count = 8, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - }, - { - .first = 0x10, - .last = 0x17, - }, - { - .first = 0x1a, - .last = 0x1c, - }, - { - .first = 0x20, - .last = 0x56, - }, - { - .first = 0x5a, - .last = 0x88, - }, - { - .first = 0x8a, - .last = 0xad, - }, - { - .first = 0xb0, - .last = 0xba, - }, - { - .first = 0xbc, - .last = 0xc3, - }, - } - }, -}; - -static int ab3550_registers_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - int bank; - - seq_printf(s, AB3550_NAME_STRING " register values:\n"); - - for (bank = 0; bank < AB3550_NUM_BANKS; bank++) { - unsigned int i; - - seq_printf(s, " bank %d:\n", bank); - for (i = 0; i < debug_ranges[bank].count; i++) { - u8 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - - get_register_interruptible(ab, bank, reg, - &value); - seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank, - reg, value); - } - } - } - return 0; -} - -static int ab3550_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_registers_print, inode->i_private); -} - -static const struct file_operations ab3550_registers_fops = { - .open = ab3550_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab3550_bank_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - - seq_printf(s, "%d\n", ab->debug_bank); - return 0; -} - -static int ab3550_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_bank_print, inode->i_private); -} - -static ssize_t ab3550_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_bank; - int err; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_bank); - if (err) - return err; - - if (user_bank >= AB3550_NUM_BANKS) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > number of banks\n"); - return -EINVAL; - } - - ab->debug_bank = user_bank; - - return count; -} - -static int ab3550_address_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - - seq_printf(s, "0x%02X\n", ab->debug_address); - return 0; -} - -static int ab3550_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_address_print, inode->i_private); -} - -static ssize_t ab3550_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_address; - int err; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_address); - if (err) - return err; - - if (user_address > 0xff) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - ab->debug_address = user_address; - return count; -} - -static int ab3550_val_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - int err; - u8 regvalue; - - err = get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab3550_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_val_print, inode->i_private); -} - -static ssize_t ab3550_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - u8 regvalue; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val > 0xff) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = mask_and_set_register_interruptible( - ab, (u8)ab->debug_bank, - (u8)ab->debug_address, 0xFF, (u8)user_val); - if (err) - return -EINVAL; - - get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - - return count; -} - -static const struct file_operations ab3550_bank_fops = { - .open = ab3550_bank_open, - .write = ab3550_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab3550_address_fops = { - .open = ab3550_address_open, - .write = ab3550_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab3550_val_fops = { - .open = ab3550_val_open, - .write = ab3550_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static struct dentry *ab3550_dir; -static struct dentry *ab3550_reg_file; -static struct dentry *ab3550_bank_file; -static struct dentry *ab3550_address_file; -static struct dentry *ab3550_val_file; - -static inline void ab3550_setup_debugfs(struct ab3550 *ab) -{ - ab->debug_bank = 0; - ab->debug_address = 0x00; - - ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL); - if (!ab3550_dir) - goto exit_no_debugfs; - - ab3550_reg_file = debugfs_create_file("all-registers", - S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops); - if (!ab3550_reg_file) - goto exit_destroy_dir; - - ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops); - if (!ab3550_bank_file) - goto exit_destroy_reg; - - ab3550_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops); - if (!ab3550_address_file) - goto exit_destroy_bank; - - ab3550_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); - if (!ab3550_val_file) - goto exit_destroy_address; - - return; - -exit_destroy_address: - debugfs_remove(ab3550_address_file); -exit_destroy_bank: - debugfs_remove(ab3550_bank_file); -exit_destroy_reg: - debugfs_remove(ab3550_reg_file); -exit_destroy_dir: - debugfs_remove(ab3550_dir); -exit_no_debugfs: - dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n"); - return; -} - -static inline void ab3550_remove_debugfs(void) -{ - debugfs_remove(ab3550_val_file); - debugfs_remove(ab3550_address_file); - debugfs_remove(ab3550_bank_file); - debugfs_remove(ab3550_reg_file); - debugfs_remove(ab3550_dir); -} - -#else /* !CONFIG_DEBUG_FS */ -static inline void ab3550_setup_debugfs(struct ab3550 *ab) -{ -} -static inline void ab3550_remove_debugfs(void) -{ -} -#endif - -/* - * Basic set-up, datastructure creation/destruction and I2C interface. - * This sets up a default config in the AB3550 chip so that it - * will work as expected. - */ -static int __devinit ab3550_setup(struct ab3550 *ab) -{ - int err = 0; - int i; - struct ab3550_platform_data *plf_data; - struct abx500_init_settings *settings; - - plf_data = ab->i2c_client[0]->dev.platform_data; - settings = plf_data->init_settings; - - for (i = 0; i < plf_data->init_settings_sz; i++) { - err = mask_and_set_register_interruptible(ab, - settings[i].bank, - settings[i].reg, - 0xFF, settings[i].setting); - if (err) - goto exit_no_setup; - - /* If event mask register update the event mask in ab3550 */ - if ((settings[i].bank == 0) && - (AB3550_IMR1 <= settings[i].reg) && - (settings[i].reg <= AB3550_IMR5)) { - ab->event_mask[settings[i].reg - AB3550_IMR1] = - settings[i].setting; - } - } -exit_no_setup: - return err; -} - -static void ab3550_mask_work(struct work_struct *work) -{ - struct ab3550 *ab = container_of(work, struct ab3550, mask_work); - int i; - unsigned long flags; - u8 mask[AB3550_NUM_EVENT_REG]; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) - mask[i] = ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { - int err; - - err = mask_and_set_register_interruptible(ab, 0, - (AB3550_IMR1 + i), ~0, mask[i]); - if (err) - dev_err(&ab->i2c_client[0]->dev, - "ab3550_mask_work failed 0x%x,0x%x\n", - (AB3550_IMR1 + i), mask[i]); - } -} - -static void ab3550_mask(struct irq_data *data) -{ - unsigned long flags; - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - int irq; - - ab = irq_data_get_irq_chip_data(data); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = data->irq - plf_data->irq.base; - - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] |= BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); - - schedule_work(&ab->mask_work); -} - -static void ab3550_unmask(struct irq_data *data) -{ - unsigned long flags; - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - int irq; - - ab = irq_data_get_irq_chip_data(data); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = data->irq - plf_data->irq.base; - - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] &= ~BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); - - schedule_work(&ab->mask_work); -} - -static void noop(struct irq_data *data) -{ -} - -static struct irq_chip ab3550_irq_chip = { - .name = "ab3550-core", /* Keep the same name as the request */ - .irq_disable = ab3550_mask, /* No default to mask in chip.c */ - .irq_ack = noop, - .irq_mask = ab3550_mask, - .irq_unmask = ab3550_unmask, -}; - -struct ab_family_id { - u8 id; - char *name; -}; - -static const struct ab_family_id ids[] __devinitconst = { - /* AB3550 */ - { - .id = AB3550_P1A, - .name = "P1A" - }, - /* Terminator */ - { - .id = 0x00, - } -}; - -static int __devinit ab3550_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ab3550 *ab; - struct ab3550_platform_data *ab3550_plf_data = - client->dev.platform_data; - int err; - int i; - int num_i2c_clients = 0; - - ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL); - if (!ab) { - dev_err(&client->dev, - "could not allocate " AB3550_NAME_STRING " device\n"); - return -ENOMEM; - } - - /* Initialize data structure */ - mutex_init(&ab->access_mutex); - spin_lock_init(&ab->event_lock); - ab->i2c_client[0] = client; - - i2c_set_clientdata(client, ab); - - /* Read chip ID register */ - err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id); - if (err) { - dev_err(&client->dev, "could not communicate with the analog " - "baseband chip\n"); - goto exit_no_detect; - } - - for (i = 0; ids[i].id != 0x0; i++) { - if (ids[i].id == ab->chip_id) { - snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, - AB3550_ID_FORMAT_STRING, ids[i].name); - break; - } - } - - if (ids[i].id == 0x0) { - dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n", - ab->chip_id); - dev_err(&client->dev, "driver not started!\n"); - goto exit_no_detect; - } - - dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]); - - /* Attach other dummy I2C clients. */ - while (++num_i2c_clients < AB3550_NUM_BANKS) { - ab->i2c_client[num_i2c_clients] = - i2c_new_dummy(client->adapter, - (client->addr + num_i2c_clients)); - if (!ab->i2c_client[num_i2c_clients]) { - err = -ENOMEM; - goto exit_no_dummy_client; - } - strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name, - sizeof(ab->i2c_client[num_i2c_clients]->name)); - } - - err = ab3550_setup(ab); - if (err) - goto exit_no_setup; - - INIT_WORK(&ab->mask_work, ab3550_mask_work); - - for (i = 0; i < ab3550_plf_data->irq.count; i++) { - unsigned int irq; - - irq = ab3550_plf_data->irq.base + i; - irq_set_chip_data(irq, ab); - irq_set_chip_and_handler(irq, &ab3550_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler, - IRQF_ONESHOT, "ab3550-core", ab); - /* This real unpredictable IRQ is of course sampled for entropy */ - rand_initialize_irq(client->irq); - - if (err) - goto exit_no_irq; - - err = abx500_register_ops(&client->dev, &ab3550_ops); - if (err) - goto exit_no_ops; - - /* Set up and register the platform devices. */ - for (i = 0; i < AB3550_NUM_DEVICES; i++) { - ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; - ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i]; - } - - err = mfd_add_devices(&client->dev, 0, ab3550_devs, - ARRAY_SIZE(ab3550_devs), NULL, - ab3550_plf_data->irq.base); - - ab3550_setup_debugfs(ab); - - return 0; - -exit_no_ops: -exit_no_irq: -exit_no_setup: -exit_no_dummy_client: - /* Unregister the dummy i2c clients. */ - while (--num_i2c_clients) - i2c_unregister_device(ab->i2c_client[num_i2c_clients]); -exit_no_detect: - kfree(ab); - return err; -} - -static int __devexit ab3550_remove(struct i2c_client *client) -{ - struct ab3550 *ab = i2c_get_clientdata(client); - int num_i2c_clients = AB3550_NUM_BANKS; - - mfd_remove_devices(&client->dev); - ab3550_remove_debugfs(); - - while (--num_i2c_clients) - i2c_unregister_device(ab->i2c_client[num_i2c_clients]); - - /* - * At this point, all subscribers should have unregistered - * their notifiers so deactivate IRQ - */ - free_irq(client->irq, ab); - kfree(ab); - return 0; -} - -static const struct i2c_device_id ab3550_id[] = { - {AB3550_NAME_STRING, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, ab3550_id); - -static struct i2c_driver ab3550_driver = { - .driver = { - .name = AB3550_NAME_STRING, - .owner = THIS_MODULE, - }, - .id_table = ab3550_id, - .probe = ab3550_probe, - .remove = __devexit_p(ab3550_remove), -}; - -static int __init ab3550_i2c_init(void) -{ - return i2c_add_driver(&ab3550_driver); -} - -static void __exit ab3550_i2c_exit(void) -{ - i2c_del_driver(&ab3550_driver); -} - -subsys_initcall(ab3550_i2c_init); -module_exit(ab3550_i2c_exit); - -MODULE_AUTHOR("Mattias Wallin "); -MODULE_DESCRIPTION("AB3550 core driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 79ec2c7b5fa..6d096e8b774 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -6,7 +6,7 @@ * * ABX500 core access functions. * The abx500 interface is used for the Analog Baseband chip - * ab3100, ab3550, ab5500, and ab8500. + * ab3100, ab5500, and ab8500. * * Author: Mattias Wallin * Author: Mattias Nilsson @@ -29,7 +29,6 @@ #define AB3100_P1G 0xc6 #define AB3100_R2A 0xc7 #define AB3100_R2B 0xc8 -#define AB3550_P1A 0x10 #define AB5500_1_0 0x20 #define AB5500_1_1 0x21 #define AB5500_2_0 0x24 @@ -143,39 +142,6 @@ int ab3100_event_register(struct ab3100 *ab3100, int ab3100_event_unregister(struct ab3100 *ab3100, struct notifier_block *nb); -/* 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. @@ -186,18 +152,6 @@ struct abx500_init_settings { 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, -- cgit v1.2.3-70-g09d2