diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-24 15:59:01 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-24 15:59:01 +0100 |
commit | a6a1d6485e77f28c11cdf943a3ed2a3fd83ac727 (patch) | |
tree | d9b1948c2c07509f9fab16cd1444de15457c08f4 | |
parent | Merge branch 'for-linus' of git://git.open-osd.org/linux-open-osd (diff) | |
parent | mfd: Push byte swaps out of wm8994 bulk read path (diff) | |
download | linux-a6a1d6485e77f28c11cdf943a3ed2a3fd83ac727.tar.xz linux-a6a1d6485e77f28c11cdf943a3ed2a3fd83ac727.zip |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (90 commits)
mfd: Push byte swaps out of wm8994 bulk read path
mfd: Rename ab8500 gpadc header
mfd: Constify WM8994 write path
mfd: Push byte swap out of WM8994 bulk I/O
mfd: Avoid copying data in WM8994 I2C write
mfd: Remove copy from WM831x I2C write function
mfd: Staticise WM8994 PM ops
regulator: Add a subdriver for TI TPS6105x regulator portions v2
mfd: Add a core driver for TI TPS61050/TPS61052 chips v2
gpio: Add Tunnel Creek support to sch_gpio
mfd: Add Tunnel Creek support to lpc_sch
pci_ids: Add Intel Tunnel Creek LPC Bridge device ID.
regulator: MAX8997/8966 support
mfd: Add WM8994 bulk register write operation
mfd: Append additional read write on 88pm860x
mfd: Adopt mfd_data in 88pm860x input driver
mfd: Adopt mfd_data in 88pm860x regulator
mfd: Adopt mfd_data in 88pm860x led
mfd: Adopt mfd_data in 88pm860x backlight
mfd: Fix MAX8997 Kconfig entry typos
...
110 files changed, 6059 insertions, 785 deletions
diff --git a/Documentation/hwmon/twl4030-madc-hwmon b/Documentation/hwmon/twl4030-madc-hwmon new file mode 100644 index 000000000000..ef7984317cec --- /dev/null +++ b/Documentation/hwmon/twl4030-madc-hwmon @@ -0,0 +1,45 @@ +Kernel driver twl4030-madc +========================= + +Supported chips: + * Texas Instruments TWL4030 + Prefix: 'twl4030-madc' + + +Authors: + J Keerthy <j-keerthy@ti.com> + +Description +----------- + +The Texas Instruments TWL4030 is a Power Management and Audio Circuit. Among +other things it contains a 10-bit A/D converter MADC. The converter has 16 +channels which can be used in different modes. + + +See this table for the meaning of the different channels + +Channel Signal +------------------------------------------ +0 Battery type(BTYPE) +1 BCI: Battery temperature (BTEMP) +2 GP analog input +3 GP analog input +4 GP analog input +5 GP analog input +6 GP analog input +7 GP analog input +8 BCI: VBUS voltage(VBUS) +9 Backup Battery voltage (VBKP) +10 BCI: Battery charger current (ICHG) +11 BCI: Battery charger voltage (VCHG) +12 BCI: Main battery voltage (VBAT) +13 Reserved +14 Reserved +15 VRUSB Supply/Speaker left/Speaker right polarization level + + +The Sysfs nodes will represent the voltage in the units of mV, +the temperature channel shows the converted temperature in +degree celcius. The Battery charging current channel represents +battery charging current in mA. diff --git a/arch/arm/mach-imx/mach-mx27_3ds.c b/arch/arm/mach-imx/mach-mx27_3ds.c index 614b3c00c4a0..6e1accf93f81 100644 --- a/arch/arm/mach-imx/mach-mx27_3ds.c +++ b/arch/arm/mach-imx/mach-mx27_3ds.c @@ -232,10 +232,13 @@ static struct mc13xxx_regulator_init_data mx27_3ds_regulators[] = { }; /* MC13783 */ -static struct mc13xxx_platform_data mc13783_pdata __initdata = { - .regulators = mx27_3ds_regulators, - .num_regulators = ARRAY_SIZE(mx27_3ds_regulators), - .flags = MC13XXX_USE_REGULATOR, +static struct mc13xxx_platform_data mc13783_pdata = { + .regulators = { + .regulators = mx27_3ds_regulators, + .num_regulators = ARRAY_SIZE(mx27_3ds_regulators), + + }, + .flags = MC13783_USE_REGULATOR, }; /* SPI */ diff --git a/arch/arm/mach-imx/mach-pcm038.c b/arch/arm/mach-imx/mach-pcm038.c index 38c77084b615..4cbce6d0fef1 100644 --- a/arch/arm/mach-imx/mach-pcm038.c +++ b/arch/arm/mach-imx/mach-pcm038.c @@ -263,10 +263,12 @@ static struct mc13xxx_regulator_init_data pcm038_regulators[] = { }; static struct mc13xxx_platform_data pcm038_pmic = { - .regulators = pcm038_regulators, - .num_regulators = ARRAY_SIZE(pcm038_regulators), - .flags = MC13XXX_USE_ADC | MC13XXX_USE_REGULATOR | - MC13XXX_USE_TOUCHSCREEN, + .regulators = { + .regulators = pcm038_regulators, + .num_regulators = ARRAY_SIZE(pcm038_regulators), + }, + .flags = MC13783_USE_ADC | MC13783_USE_REGULATOR | + MC13783_USE_TOUCHSCREEN, }; static struct spi_board_info pcm038_spi_board_info[] __initdata = { diff --git a/arch/arm/mach-mx3/mach-mx31_3ds.c b/arch/arm/mach-mx3/mach-mx31_3ds.c index 544d3e414f58..034be624d35c 100644 --- a/arch/arm/mach-mx3/mach-mx31_3ds.c +++ b/arch/arm/mach-mx3/mach-mx31_3ds.c @@ -488,10 +488,12 @@ static struct mc13xxx_regulator_init_data mx31_3ds_regulators[] = { }; /* MC13783 */ -static struct mc13xxx_platform_data mc13783_pdata __initdata = { - .regulators = mx31_3ds_regulators, - .num_regulators = ARRAY_SIZE(mx31_3ds_regulators), - .flags = MC13XXX_USE_REGULATOR | MC13XXX_USE_TOUCHSCREEN +static struct mc13xxx_platform_data mc13783_pdata = { + .regulators = { + .regulators = mx31_3ds_regulators, + .num_regulators = ARRAY_SIZE(mx31_3ds_regulators), + }, + .flags = MC13783_USE_REGULATOR | MC13783_USE_TOUCHSCREEN, }; /* SPI */ diff --git a/arch/arm/mach-mx3/mach-mx31moboard.c b/arch/arm/mach-mx3/mach-mx31moboard.c index 6f3692bccb8a..3a021b01161d 100644 --- a/arch/arm/mach-mx3/mach-mx31moboard.c +++ b/arch/arm/mach-mx3/mach-mx31moboard.c @@ -268,8 +268,10 @@ static struct mc13783_leds_platform_data moboard_leds = { }; static struct mc13xxx_platform_data moboard_pmic = { - .regulators = moboard_regulators, - .num_regulators = ARRAY_SIZE(moboard_regulators), + .regulators = { + .regulators = moboard_regulators, + .num_regulators = ARRAY_SIZE(moboard_regulators), + }, .leds = &moboard_leds, .flags = MC13XXX_USE_REGULATOR | MC13XXX_USE_RTC | MC13XXX_USE_ADC | MC13XXX_USE_LED, diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 62c78de1aa51..56702c5e577f 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -550,6 +550,12 @@ static struct regulator_init_data sdp4430_vusb = { }, }; +static struct regulator_init_data sdp4430_clk32kg = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + static struct twl4030_platform_data sdp4430_twldata = { .irq_base = TWL6030_IRQ_BASE, .irq_end = TWL6030_IRQ_END, @@ -565,6 +571,7 @@ static struct twl4030_platform_data sdp4430_twldata = { .vaux1 = &sdp4430_vaux1, .vaux2 = &sdp4430_vaux2, .vaux3 = &sdp4430_vaux3, + .clk32kg = &sdp4430_clk32kg, .usb = &omap4_usbphy_data }; diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c index 209cffbee4e0..c936c6d7ded0 100644 --- a/arch/arm/mach-omap2/board-omap4panda.c +++ b/arch/arm/mach-omap2/board-omap4panda.c @@ -411,6 +411,12 @@ static struct regulator_init_data omap4_panda_vusb = { }, }; +static struct regulator_init_data omap4_panda_clk32kg = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + static struct twl4030_platform_data omap4_panda_twldata = { .irq_base = TWL6030_IRQ_BASE, .irq_end = TWL6030_IRQ_END, @@ -426,6 +432,7 @@ static struct twl4030_platform_data omap4_panda_twldata = { .vaux1 = &omap4_panda_vaux1, .vaux2 = &omap4_panda_vaux2, .vaux3 = &omap4_panda_vaux3, + .clk32kg = &omap4_panda_clk32kg, .usb = &omap4_usbphy_data, }; diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c index 127775696d6c..99513642a0e6 100644 --- a/arch/x86/platform/olpc/olpc-xo1.c +++ b/arch/x86/platform/olpc/olpc-xo1.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/mfd/core.h> #include <asm/io.h> #include <asm/olpc.h> @@ -56,25 +57,24 @@ static void xo1_power_off(void) static int __devinit olpc_xo1_probe(struct platform_device *pdev) { struct resource *res; + int err; /* don't run on non-XOs */ if (!machine_is_olpc()) return -ENODEV; + err = mfd_cell_enable(pdev); + if (err) + return err; + res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) { dev_err(&pdev->dev, "can't fetch device resource info\n"); return -EIO; } - - if (!request_region(res->start, resource_size(res), DRV_NAME)) { - dev_err(&pdev->dev, "can't request region\n"); - return -EIO; - } - - if (strcmp(pdev->name, "cs5535-pms") == 0) + if (strcmp(pdev->name, "olpc-xo1-pms") == 0) pms_base = res->start; - else if (strcmp(pdev->name, "cs5535-acpi") == 0) + else if (strcmp(pdev->name, "olpc-xo1-ac-acpi") == 0) acpi_base = res->start; /* If we have both addresses, we can override the poweroff hook */ @@ -88,14 +88,11 @@ static int __devinit olpc_xo1_probe(struct platform_device *pdev) static int __devexit olpc_xo1_remove(struct platform_device *pdev) { - struct resource *r; - - r = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(r->start, resource_size(r)); + mfd_cell_disable(pdev); - if (strcmp(pdev->name, "cs5535-pms") == 0) + if (strcmp(pdev->name, "olpc-xo1-pms") == 0) pms_base = 0; - else if (strcmp(pdev->name, "cs5535-acpi") == 0) + else if (strcmp(pdev->name, "olpc-xo1-acpi") == 0) acpi_base = 0; pm_power_off = NULL; @@ -104,7 +101,7 @@ static int __devexit olpc_xo1_remove(struct platform_device *pdev) static struct platform_driver cs5535_pms_drv = { .driver = { - .name = "cs5535-pms", + .name = "olpc-xo1-pms", .owner = THIS_MODULE, }, .probe = olpc_xo1_probe, @@ -113,7 +110,7 @@ static struct platform_driver cs5535_pms_drv = { static struct platform_driver cs5535_acpi_drv = { .driver = { - .name = "cs5535-acpi", + .name = "olpc-xo1-acpi", .owner = THIS_MODULE, }, .probe = olpc_xo1_probe, @@ -124,26 +121,27 @@ static int __init olpc_xo1_init(void) { int r; - r = platform_driver_register(&cs5535_pms_drv); + r = mfd_shared_platform_driver_register(&cs5535_pms_drv, "cs5535-pms"); if (r) return r; - r = platform_driver_register(&cs5535_acpi_drv); + r = mfd_shared_platform_driver_register(&cs5535_acpi_drv, + "cs5535-acpi"); if (r) - platform_driver_unregister(&cs5535_pms_drv); + mfd_shared_platform_driver_unregister(&cs5535_pms_drv); return r; } static void __exit olpc_xo1_exit(void) { - platform_driver_unregister(&cs5535_acpi_drv); - platform_driver_unregister(&cs5535_pms_drv); + mfd_shared_platform_driver_unregister(&cs5535_acpi_drv); + mfd_shared_platform_driver_unregister(&cs5535_pms_drv); } MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:olpc-xo1"); +MODULE_ALIAS("platform:cs5535-pms"); module_init(olpc_xo1_init); module_exit(olpc_xo1_exit); diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index f69f90a61873..d2c75feff7df 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -27,6 +27,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/timb_dma.h> @@ -684,7 +685,7 @@ static irqreturn_t td_irq(int irq, void *devid) static int __devinit td_probe(struct platform_device *pdev) { - struct timb_dma_platform_data *pdata = pdev->dev.platform_data; + struct timb_dma_platform_data *pdata = mfd_get_data(pdev); struct timb_dma *td; struct resource *iomem; int irq; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b46442d7d66e..d8d0cda2641d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -100,18 +100,21 @@ config GPIO_VR41XX Say yes here to support the NEC VR4100 series General-purpose I/O Uint config GPIO_SCH - tristate "Intel SCH GPIO" + tristate "Intel SCH/TunnelCreek GPIO" depends on GPIOLIB && PCI && X86 select MFD_CORE select LPC_SCH help - Say yes here to support GPIO interface on Intel Poulsbo SCH. + Say yes here to support GPIO interface on Intel Poulsbo SCH + or Intel Tunnel Creek processor. The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are powered by the core power rail and are turned off during sleep modes (S3 and higher). The remaining four GPIOs are powered by the Intel SCH suspend power supply. These GPIOs remain active during S3. The suspend powered GPIOs can be used to wake the system from the Suspend-to-RAM state. + The Intel Tunnel Creek processor has 5 GPIOs powered by the + core power rail and 9 from suspend power supply. This driver can also be built as a module. If so, the module will be called sch-gpio. diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c index 813ac077e5d7..2514fb075f4a 100644 --- a/drivers/gpio/janz-ttl.c +++ b/drivers/gpio/janz-ttl.c @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/io.h> #include <linux/gpio.h> #include <linux/slab.h> @@ -149,7 +150,7 @@ static int __devinit ttl_probe(struct platform_device *pdev) struct resource *res; int ret; - pdata = pdev->dev.platform_data; + pdata = mfd_get_data(pdev); if (!pdata) { dev_err(dev, "no platform data\n"); ret = -ENXIO; diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c index 897e0577e65e..a9bda881935a 100644 --- a/drivers/gpio/rdc321x-gpio.c +++ b/drivers/gpio/rdc321x-gpio.c @@ -27,6 +27,7 @@ #include <linux/pci.h> #include <linux/gpio.h> #include <linux/mfd/rdc321x.h> +#include <linux/mfd/core.h> #include <linux/slab.h> struct rdc321x_gpio { @@ -135,7 +136,7 @@ static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) struct rdc321x_gpio *rdc321x_gpio_dev; struct rdc321x_gpio_pdata *pdata; - pdata = platform_get_drvdata(pdev); + pdata = mfd_get_data(pdev); if (!pdata) { dev_err(&pdev->dev, "no platform data supplied\n"); return -ENODEV; diff --git a/drivers/gpio/sch_gpio.c b/drivers/gpio/sch_gpio.c index 583521352c16..56060421cdff 100644 --- a/drivers/gpio/sch_gpio.c +++ b/drivers/gpio/sch_gpio.c @@ -25,6 +25,7 @@ #include <linux/errno.h> #include <linux/acpi.h> #include <linux/platform_device.h> +#include <linux/pci_ids.h> #include <linux/gpio.h> @@ -187,7 +188,11 @@ static struct gpio_chip sch_gpio_resume = { static int __devinit sch_gpio_probe(struct platform_device *pdev) { struct resource *res; - int err; + int err, id; + + id = pdev->id; + if (!id) + return -ENODEV; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -198,12 +203,40 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev) gpio_ba = res->start; - sch_gpio_core.base = 0; - sch_gpio_core.ngpio = 10; - sch_gpio_core.dev = &pdev->dev; + switch (id) { + case PCI_DEVICE_ID_INTEL_SCH_LPC: + sch_gpio_core.base = 0; + sch_gpio_core.ngpio = 10; + + sch_gpio_resume.base = 10; + sch_gpio_resume.ngpio = 4; + + /* + * GPIO[6:0] enabled by default + * GPIO7 is configured by the CMC as SLPIOVR + * Enable GPIO[9:8] core powered gpios explicitly + */ + outb(0x3, gpio_ba + CGEN + 1); + /* + * SUS_GPIO[2:0] enabled by default + * Enable SUS_GPIO3 resume powered gpio explicitly + */ + outb(0x8, gpio_ba + RGEN); + break; + + case PCI_DEVICE_ID_INTEL_ITC_LPC: + sch_gpio_core.base = 0; + sch_gpio_core.ngpio = 5; + + sch_gpio_resume.base = 5; + sch_gpio_resume.ngpio = 9; + break; + + default: + return -ENODEV; + } - sch_gpio_resume.base = 10; - sch_gpio_resume.ngpio = 4; + sch_gpio_core.dev = &pdev->dev; sch_gpio_resume.dev = &pdev->dev; err = gpiochip_add(&sch_gpio_core); @@ -214,18 +247,6 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev) if (err < 0) goto err_sch_gpio_resume; - /* - * GPIO[6:0] enabled by default - * GPIO7 is configured by the CMC as SLPIOVR - * Enable GPIO[9:8] core powered gpios explicitly - */ - outb(0x3, gpio_ba + CGEN + 1); - /* - * SUS_GPIO[2:0] enabled by default - * Enable SUS_GPIO3 resume powered gpio explicitly - */ - outb(0x8, gpio_ba + RGEN); - return 0; err_sch_gpio_resume: diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c index 58c8f30352dd..ffcd815b8b8b 100644 --- a/drivers/gpio/timbgpio.c +++ b/drivers/gpio/timbgpio.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/gpio.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/timb_gpio.h> @@ -228,7 +229,7 @@ static int __devinit timbgpio_probe(struct platform_device *pdev) struct gpio_chip *gc; struct timbgpio *tgpio; struct resource *iomem; - struct timbgpio_platform_data *pdata = pdev->dev.platform_data; + struct timbgpio_platform_data *pdata = mfd_get_data(pdev); int irq = platform_get_irq(pdev, 0); if (!pdata || pdata->nr_pins > 32) { @@ -319,14 +320,13 @@ err_mem: static int __devexit timbgpio_remove(struct platform_device *pdev) { int err; - struct timbgpio_platform_data *pdata = pdev->dev.platform_data; struct timbgpio *tgpio = platform_get_drvdata(pdev); struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); if (irq >= 0 && tgpio->irq_base > 0) { int i; - for (i = 0; i < pdata->nr_pins; i++) { + for (i = 0; i < tgpio->gpio.ngpio; i++) { set_irq_chip(tgpio->irq_base + i, NULL); set_irq_chip_data(tgpio->irq_base + i, NULL); } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e4bd13b3cd8b..81131eda5544 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1047,6 +1047,16 @@ config SENSORS_TMP421 This driver can also be built as a module. If so, the module will be called tmp421. +config SENSORS_TWL4030_MADC + tristate "Texas Instruments TWL4030 MADC Hwmon" + depends on TWL4030_MADC + help + If you say yes here you get hwmon support for triton + TWL4030-MADC. + + This driver can also be built as a module. If so it will be called + twl4030-madc-hwmon. + config SENSORS_VIA_CPUTEMP tristate "VIA CPU temperature sensor" depends on X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 54ca5939d028..967d0ea9447f 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -104,6 +104,7 @@ obj-$(CONFIG_SENSORS_THMC50) += thmc50.o obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o +obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VT1211) += vt1211.o diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c index 1c8b3d9e2051..fea292d43407 100644 --- a/drivers/hwmon/jz4740-hwmon.c +++ b/drivers/hwmon/jz4740-hwmon.c @@ -32,7 +32,7 @@ struct jz4740_hwmon { int irq; - struct mfd_cell *cell; + const struct mfd_cell *cell; struct device *hwmon; struct completion read_completion; @@ -112,7 +112,7 @@ static int __devinit jz4740_hwmon_probe(struct platform_device *pdev) return -ENOMEM; } - hwmon->cell = pdev->dev.platform_data; + hwmon->cell = mfd_get_cell(pdev); hwmon->irq = platform_get_irq(pdev, 0); if (hwmon->irq < 0) { diff --git a/drivers/hwmon/twl4030-madc-hwmon.c b/drivers/hwmon/twl4030-madc-hwmon.c new file mode 100644 index 000000000000..97e22bef85ab --- /dev/null +++ b/drivers/hwmon/twl4030-madc-hwmon.c @@ -0,0 +1,157 @@ +/* + * + * TWL4030 MADC Hwmon driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. User can ask for the conversion on a + * particular channel using the sysfs nodes. + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/i2c/twl.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030-madc.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/stddef.h> +#include <linux/sysfs.h> +#include <linux/err.h> +#include <linux/types.h> + +/* + * sysfs hook function + */ +static ssize_t madc_read(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct twl4030_madc_request req; + long val; + + req.channels = (1 << attr->index); + req.method = TWL4030_MADC_SW2; + req.func_cb = NULL; + val = twl4030_madc_conversion(&req); + if (val < 0) + return val; + + return sprintf(buf, "%d\n", req.rbuf[attr->index]); +} + +/* sysfs nodes to read individual channels from user side */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, madc_read, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, madc_read, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, madc_read, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, madc_read, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, madc_read, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, madc_read, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, madc_read, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, madc_read, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, madc_read, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, madc_read, NULL, 9); +static SENSOR_DEVICE_ATTR(curr10_input, S_IRUGO, madc_read, NULL, 10); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, madc_read, NULL, 11); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, madc_read, NULL, 12); +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, madc_read, NULL, 15); + +static struct attribute *twl4030_madc_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_curr10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group twl4030_madc_group = { + .attrs = twl4030_madc_attributes, +}; + +static int __devinit twl4030_madc_hwmon_probe(struct platform_device *pdev) +{ + int ret; + int status; + struct device *hwmon; + + ret = sysfs_create_group(&pdev->dev.kobj, &twl4030_madc_group); + if (ret) + goto err_sysfs; + hwmon = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon)) { + dev_err(&pdev->dev, "hwmon_device_register failed.\n"); + status = PTR_ERR(hwmon); + goto err_reg; + } + + return 0; + +err_reg: + sysfs_remove_group(&pdev->dev.kobj, &twl4030_madc_group); +err_sysfs: + + return ret; +} + +static int __devexit twl4030_madc_hwmon_remove(struct platform_device *pdev) +{ + hwmon_device_unregister(&pdev->dev); + sysfs_remove_group(&pdev->dev.kobj, &twl4030_madc_group); + + return 0; +} + +static struct platform_driver twl4030_madc_hwmon_driver = { + .probe = twl4030_madc_hwmon_probe, + .remove = __exit_p(twl4030_madc_hwmon_remove), + .driver = { + .name = "twl4030_madc_hwmon", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_madc_hwmon_init(void) +{ + return platform_driver_register(&twl4030_madc_hwmon_driver); +} + +module_init(twl4030_madc_hwmon_init); + +static void __exit twl4030_madc_hwmon_exit(void) +{ + platform_driver_unregister(&twl4030_madc_hwmon_driver); +} + +module_exit(twl4030_madc_hwmon_exit); + +MODULE_DESCRIPTION("TWL4030 ADC Hwmon driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("twl4030_madc_hwmon"); diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 1b46a9d9f907..fee1a2613861 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -49,6 +49,7 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/wait.h> @@ -305,7 +306,7 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) return -EIO; } - pdata = pdev->dev.platform_data; + pdata = mfd_get_data(pdev); if (pdata) { i2c->regstep = pdata->regstep; i2c->clock_khz = pdata->clock_khz; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index a9c419e075a5..9fbd7e6fe32e 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -34,6 +34,7 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/wait.h> @@ -704,7 +705,7 @@ static int __devinit xiic_i2c_probe(struct platform_device *pdev) if (irq < 0) goto resource_missing; - pdata = (struct xiic_i2c_platform_data *) pdev->dev.platform_data; + pdata = mfd_get_data(pdev); if (!pdata) return -EINVAL; diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c index 4cc82826ea6b..3dca3c14510e 100644 --- a/drivers/input/misc/88pm860x_onkey.c +++ b/drivers/input/misc/88pm860x_onkey.c @@ -74,7 +74,7 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) info->chip = chip; info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; info->dev = &pdev->dev; - info->irq = irq + chip->irq_base; + info->irq = irq; info->idev = input_allocate_device(); if (!info->idev) { diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 014dd4ad0d4f..6a11694e3fc7 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -29,6 +29,7 @@ #include <linux/workqueue.h> #include <linux/i2c/twl.h> #include <linux/mfd/twl4030-codec.h> +#include <linux/mfd/core.h> #include <linux/input.h> #include <linux/slab.h> @@ -196,7 +197,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, static int __devinit twl4030_vibra_probe(struct platform_device *pdev) { - struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_vibra_data *pdata = mfd_get_data(pdev); struct vibra_info *info; int ret; diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index e672b44ee172..416def84d045 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -17,6 +17,7 @@ #include <linux/leds.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> #define LED_PWM_SHIFT (3) @@ -118,7 +119,8 @@ static void pm860x_led_work(struct work_struct *work) struct pm860x_led *led; struct pm860x_chip *chip; - int mask; + unsigned char buf[3]; + int mask, ret; led = container_of(work, struct pm860x_led, work); chip = led->chip; @@ -128,16 +130,27 @@ static void pm860x_led_work(struct work_struct *work) pm860x_set_bits(led->i2c, __led_off(led->port), LED_CURRENT_MASK, led->iset); } + pm860x_set_bits(led->i2c, __blink_off(led->port), + LED_BLINK_MASK, LED_ON_CONTINUOUS); mask = __blink_ctl_mask(led->port); pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask); - } else if (led->brightness == 0) { - pm860x_set_bits(led->i2c, __led_off(led->port), - LED_CURRENT_MASK, 0); - mask = __blink_ctl_mask(led->port); - pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0); } pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK, led->brightness); + + if (led->brightness == 0) { + pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf); + ret = buf[0] & LED_PWM_MASK; + ret |= buf[1] & LED_PWM_MASK; + ret |= buf[2] & LED_PWM_MASK; + if (ret == 0) { + /* unset current since no led is lighting */ + pm860x_set_bits(led->i2c, __led_off(led->port), + LED_CURRENT_MASK, 0); + mask = __blink_ctl_mask(led->port); + pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0); + } + } led->current_brightness = led->brightness; dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", __led_off(led->port), led->brightness); @@ -153,31 +166,12 @@ static void pm860x_led_set(struct led_classdev *cdev, schedule_work(&data->work); } -static int __check_device(struct pm860x_led_pdata *pdata, char *name) -{ - struct pm860x_led_pdata *p = pdata; - int ret = -EINVAL; - - while (p && p->id) { - if ((p->id != PM8606_ID_LED) || (p->flags < 0)) - break; - - if (!strncmp(name, pm860x_led_name[p->flags], - MFD_NAME_SIZE)) { - ret = (int)p->flags; - break; - } - p++; - } - return ret; -} - static int pm860x_led_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct pm860x_platform_data *pm860x_pdata; struct pm860x_led_pdata *pdata; struct pm860x_led *data; + struct mfd_cell *cell; struct resource *res; int ret; @@ -187,10 +181,11 @@ static int pm860x_led_probe(struct platform_device *pdev) return -EINVAL; } - if (pdev->dev.parent->platform_data) { - pm860x_pdata = pdev->dev.parent->platform_data; - pdata = pm860x_pdata->led; - } else { + cell = pdev->dev.platform_data; + if (cell == NULL) + return -ENODEV; + pdata = cell->mfd_data; + if (pdata == NULL) { dev_err(&pdev->dev, "No platform data!\n"); return -EINVAL; } @@ -198,12 +193,12 @@ static int pm860x_led_probe(struct platform_device *pdev) data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL); if (data == NULL) return -ENOMEM; - strncpy(data->name, res->name, MFD_NAME_SIZE); + strncpy(data->name, res->name, MFD_NAME_SIZE - 1); dev_set_drvdata(&pdev->dev, data); data->chip = chip; data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; data->iset = pdata->iset; - data->port = __check_device(pdata, data->name); + data->port = pdata->flags; if (data->port < 0) { dev_err(&pdev->dev, "check device failed\n"); kfree(data); @@ -221,6 +216,7 @@ static int pm860x_led_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); goto out; } + pm860x_led_set(&data->cdev, 0); return 0; out: kfree(data); diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index f05bb08d0f09..06a5bb484707 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -22,6 +22,7 @@ #include <linux/leds.h> #include <linux/workqueue.h> #include <linux/mfd/mc13783.h> +#include <linux/mfd/core.h> #include <linux/slab.h> struct mc13783_led { @@ -183,7 +184,7 @@ static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current) static int __devinit mc13783_leds_prepare(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev); struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); int ret = 0; int reg = 0; @@ -264,7 +265,7 @@ out: static int __devinit mc13783_led_probe(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev); struct mc13783_led_platform_data *led_cur; struct mc13783_led *led, *led_dat; int ret, i; @@ -351,7 +352,7 @@ err_free: static int __devexit mc13783_led_remove(struct platform_device *pdev) { - struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mc13783_leds_platform_data *pdata = mfd_get_data(pdev); struct mc13783_led *led = platform_get_drvdata(pdev); struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent); int i; diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index a185610b376b..1e3a8dd820a4 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -21,6 +21,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/i2c.h> @@ -148,7 +149,7 @@ static const struct v4l2_file_operations timbradio_fops = { static int __devinit timbradio_probe(struct platform_device *pdev) { - struct timb_radio_platform_data *pdata = pdev->dev.platform_data; + struct timb_radio_platform_data *pdata = mfd_get_data(pdev); struct timbradio *tr; int err; diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 7ecc8e657663..4698eb00b59f 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -2138,7 +2138,7 @@ static int wl1273_fm_radio_remove(struct platform_device *pdev) static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) { - struct wl1273_core **core = pdev->dev.platform_data; + struct wl1273_core **core = mfd_get_data(pdev); struct wl1273_device *radio; struct v4l2_ctrl *ctrl; int r = 0; diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c index fc611ebeb82c..84d4c7c83435 100644 --- a/drivers/media/video/timblogiw.c +++ b/drivers/media/video/timblogiw.c @@ -24,6 +24,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dmaengine.h> +#include <linux/mfd/core.h> #include <linux/scatterlist.h> #include <linux/interrupt.h> #include <linux/list.h> @@ -790,7 +791,7 @@ static int __devinit timblogiw_probe(struct platform_device *pdev) { int err; struct timblogiw *lw = NULL; - struct timb_video_platform_data *pdata = pdev->dev.platform_data; + struct timb_video_platform_data *pdata = mfd_get_data(pdev); if (!pdata) { dev_err(&pdev->dev, "No platform data\n"); diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 793300c554b4..9c511c1604a5 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -17,230 +17,138 @@ #include <linux/platform_device.h> #include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> +#include <linux/regulator/machine.h> #define INT_STATUS_NUM 3 -char pm860x_backlight_name[][MFD_NAME_SIZE] = { - "backlight-0", - "backlight-1", - "backlight-2", +static struct resource bk_resources[] __initdata = { + {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,}, + {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,}, + {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,}, }; -EXPORT_SYMBOL(pm860x_backlight_name); - -char pm860x_led_name[][MFD_NAME_SIZE] = { - "led0-red", - "led0-green", - "led0-blue", - "led1-red", - "led1-green", - "led1-blue", -}; -EXPORT_SYMBOL(pm860x_led_name); - -#define PM8606_BACKLIGHT_RESOURCE(_i, _x) \ -{ \ - .name = pm860x_backlight_name[_i], \ - .start = PM8606_##_x, \ - .end = PM8606_##_x, \ - .flags = IORESOURCE_IO, \ -} -static struct resource backlight_resources[] = { - PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT1, WLED1A), - PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT2, WLED2A), - PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT3, WLED3A), +static struct resource led_resources[] __initdata = { + {PM8606_LED1_RED, PM8606_LED1_RED, "led0-red", IORESOURCE_IO,}, + {PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,}, + {PM8606_LED1_BLUE, PM8606_LED1_BLUE, "led0-blue", IORESOURCE_IO,}, + {PM8606_LED2_RED, PM8606_LED2_RED, "led1-red", IORESOURCE_IO,}, + {PM8606_LED2_GREEN, PM8606_LED2_GREEN, "led1-green", IORESOURCE_IO,}, + {PM8606_LED2_BLUE, PM8606_LED2_BLUE, "led1-blue", IORESOURCE_IO,}, }; -#define PM8606_BACKLIGHT_DEVS(_i) \ -{ \ - .name = "88pm860x-backlight", \ - .num_resources = 1, \ - .resources = &backlight_resources[_i], \ - .id = _i, \ -} - -static struct mfd_cell backlight_devs[] = { - PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT1), - PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT2), - PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT3), +static struct resource regulator_resources[] __initdata = { + {PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,}, + {PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,}, + {PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,}, + {PM8607_ID_LDO1, PM8607_ID_LDO1, "ldo-01", IORESOURCE_IO,}, + {PM8607_ID_LDO2, PM8607_ID_LDO2, "ldo-02", IORESOURCE_IO,}, + {PM8607_ID_LDO3, PM8607_ID_LDO3, "ldo-03", IORESOURCE_IO,}, + {PM8607_ID_LDO4, PM8607_ID_LDO4, "ldo-04", IORESOURCE_IO,}, + {PM8607_ID_LDO5, PM8607_ID_LDO5, "ldo-05", IORESOURCE_IO,}, + {PM8607_ID_LDO6, PM8607_ID_LDO6, "ldo-06", IORESOURCE_IO,}, + {PM8607_ID_LDO7, PM8607_ID_LDO7, "ldo-07", IORESOURCE_IO,}, + {PM8607_ID_LDO8, PM8607_ID_LDO8, "ldo-08", IORESOURCE_IO,}, + {PM8607_ID_LDO9, PM8607_ID_LDO9, "ldo-09", IORESOURCE_IO,}, + {PM8607_ID_LDO10, PM8607_ID_LDO10, "ldo-10", IORESOURCE_IO,}, + {PM8607_ID_LDO11, PM8607_ID_LDO11, "ldo-11", IORESOURCE_IO,}, + {PM8607_ID_LDO12, PM8607_ID_LDO12, "ldo-12", IORESOURCE_IO,}, + {PM8607_ID_LDO13, PM8607_ID_LDO13, "ldo-13", IORESOURCE_IO,}, + {PM8607_ID_LDO14, PM8607_ID_LDO14, "ldo-14", IORESOURCE_IO,}, + {PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,}, }; -#define PM8606_LED_RESOURCE(_i, _x) \ -{ \ - .name = pm860x_led_name[_i], \ - .start = PM8606_##_x, \ - .end = PM8606_##_x, \ - .flags = IORESOURCE_IO, \ -} - -static struct resource led_resources[] = { - PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B), - PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C), - PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D), - PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B), - PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C), - PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D), +static struct resource touch_resources[] __initdata = { + {PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,}, }; -#define PM8606_LED_DEVS(_i) \ -{ \ - .name = "88pm860x-led", \ - .num_resources = 1, \ - .resources = &led_resources[_i], \ - .id = _i, \ -} - -static struct mfd_cell led_devs[] = { - PM8606_LED_DEVS(PM8606_LED1_RED), - PM8606_LED_DEVS(PM8606_LED1_GREEN), - PM8606_LED_DEVS(PM8606_LED1_BLUE), - PM8606_LED_DEVS(PM8606_LED2_RED), - PM8606_LED_DEVS(PM8606_LED2_GREEN), - PM8606_LED_DEVS(PM8606_LED2_BLUE), +static struct resource onkey_resources[] __initdata = { + {PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,}, }; -static struct resource touch_resources[] = { - { - .start = PM8607_IRQ_PEN, - .end = PM8607_IRQ_PEN, - .flags = IORESOURCE_IRQ, - }, +static struct resource codec_resources[] __initdata = { + /* Headset microphone insertion or removal */ + {PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,}, + /* Hook-switch press or release */ + {PM8607_IRQ_HOOK, PM8607_IRQ_HOOK, "hook", IORESOURCE_IRQ,}, + /* Headset insertion or removal */ + {PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,}, + /* Audio short */ + {PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,}, }; -static struct mfd_cell touch_devs[] = { - { - .name = "88pm860x-touch", - .num_resources = 1, - .resources = &touch_resources[0], - }, +static struct resource battery_resources[] __initdata = { + {PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,}, + {PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,}, }; -#define PM8607_REG_RESOURCE(_start, _end) \ -{ \ - .start = PM8607_##_start, \ - .end = PM8607_##_end, \ - .flags = IORESOURCE_IO, \ -} - -static struct resource power_supply_resources[] = { - { - .name = "88pm860x-power", - .start = PM8607_IRQ_CHG, - .end = PM8607_IRQ_CHG, - .flags = IORESOURCE_IRQ, - }, +static struct resource charger_resources[] __initdata = { + {PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,}, + {PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,}, + {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,}, + {PM8607_IRQ_GPADC1, PM8607_IRQ_GPADC1, "battery temperature", IORESOURCE_IRQ,}, + {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,}, + {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, }; -static struct mfd_cell power_devs[] = { - { - .name = "88pm860x-power", - .num_resources = 1, - .resources = &power_supply_resources[0], - .id = -1, - }, +static struct mfd_cell bk_devs[] __initdata = { + {"88pm860x-backlight", 0,}, + {"88pm860x-backlight", 1,}, + {"88pm860x-backlight", 2,}, }; -static struct resource onkey_resources[] = { - { - .name = "88pm860x-onkey", - .start = PM8607_IRQ_ONKEY, - .end = PM8607_IRQ_ONKEY, - .flags = IORESOURCE_IRQ, - }, +static struct mfd_cell led_devs[] __initdata = { + {"88pm860x-led", 0,}, + {"88pm860x-led", 1,}, + {"88pm860x-led", 2,}, + {"88pm860x-led", 3,}, + {"88pm860x-led", 4,}, + {"88pm860x-led", 5,}, }; -static struct mfd_cell onkey_devs[] = { - { - .name = "88pm860x-onkey", - .num_resources = 1, - .resources = &onkey_resources[0], - .id = -1, - }, +static struct mfd_cell regulator_devs[] __initdata = { + {"88pm860x-regulator", 0,}, + {"88pm860x-regulator", 1,}, + {"88pm860x-regulator", 2,}, + {"88pm860x-regulator", 3,}, + {"88pm860x-regulator", 4,}, + {"88pm860x-regulator", 5,}, + {"88pm860x-regulator", 6,}, + {"88pm860x-regulator", 7,}, + {"88pm860x-regulator", 8,}, + {"88pm860x-regulator", 9,}, + {"88pm860x-regulator", 10,}, + {"88pm860x-regulator", 11,}, + {"88pm860x-regulator", 12,}, + {"88pm860x-regulator", 13,}, + {"88pm860x-regulator", 14,}, + {"88pm860x-regulator", 15,}, + {"88pm860x-regulator", 16,}, + {"88pm860x-regulator", 17,}, }; -static struct resource codec_resources[] = { - { - /* Headset microphone insertion or removal */ - .name = "micin", - .start = PM8607_IRQ_MICIN, - .end = PM8607_IRQ_MICIN, - .flags = IORESOURCE_IRQ, - }, { - /* Hook-switch press or release */ - .name = "hook", - .start = PM8607_IRQ_HOOK, - .end = PM8607_IRQ_HOOK, - .flags = IORESOURCE_IRQ, - }, { - /* Headset insertion or removal */ - .name = "headset", - .start = PM8607_IRQ_HEADSET, - .end = PM8607_IRQ_HEADSET, - .flags = IORESOURCE_IRQ, - }, { - /* Audio short */ - .name = "audio-short", - .start = PM8607_IRQ_AUDIO_SHORT, - .end = PM8607_IRQ_AUDIO_SHORT, - .flags = IORESOURCE_IRQ, - }, +static struct mfd_cell touch_devs[] __initdata = { + {"88pm860x-touch", -1,}, }; -static struct mfd_cell codec_devs[] = { - { - .name = "88pm860x-codec", - .num_resources = ARRAY_SIZE(codec_resources), - .resources = &codec_resources[0], - .id = -1, - }, +static struct mfd_cell onkey_devs[] __initdata = { + {"88pm860x-onkey", -1,}, }; -static struct resource regulator_resources[] = { - PM8607_REG_RESOURCE(BUCK1, BUCK1), - PM8607_REG_RESOURCE(BUCK2, BUCK2), - PM8607_REG_RESOURCE(BUCK3, BUCK3), - PM8607_REG_RESOURCE(LDO1, LDO1), - PM8607_REG_RESOURCE(LDO2, LDO2), - PM8607_REG_RESOURCE(LDO3, LDO3), - PM8607_REG_RESOURCE(LDO4, LDO4), - PM8607_REG_RESOURCE(LDO5, LDO5), - PM8607_REG_RESOURCE(LDO6, LDO6), - PM8607_REG_RESOURCE(LDO7, LDO7), - PM8607_REG_RESOURCE(LDO8, LDO8), - PM8607_REG_RESOURCE(LDO9, LDO9), - PM8607_REG_RESOURCE(LDO10, LDO10), - PM8607_REG_RESOURCE(LDO12, LDO12), - PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET), - PM8607_REG_RESOURCE(LDO14, LDO14), +static struct mfd_cell codec_devs[] __initdata = { + {"88pm860x-codec", -1,}, }; -#define PM8607_REG_DEVS(_id) \ -{ \ - .name = "88pm860x-regulator", \ - .num_resources = 1, \ - .resources = ®ulator_resources[PM8607_ID_##_id], \ - .id = PM8607_ID_##_id, \ -} - -static struct mfd_cell regulator_devs[] = { - PM8607_REG_DEVS(BUCK1), - PM8607_REG_DEVS(BUCK2), - PM8607_REG_DEVS(BUCK3), - PM8607_REG_DEVS(LDO1), - PM8607_REG_DEVS(LDO2), - PM8607_REG_DEVS(LDO3), - PM8607_REG_DEVS(LDO4), - PM8607_REG_DEVS(LDO5), - PM8607_REG_DEVS(LDO6), - PM8607_REG_DEVS(LDO7), - PM8607_REG_DEVS(LDO8), - PM8607_REG_DEVS(LDO9), - PM8607_REG_DEVS(LDO10), - PM8607_REG_DEVS(LDO12), - PM8607_REG_DEVS(LDO13), - PM8607_REG_DEVS(LDO14), +static struct mfd_cell power_devs[] = { + {"88pm860x-battery", -1,}, + {"88pm860x-charger", -1,}, }; +static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)]; +static struct pm860x_led_pdata led_pdata[ARRAY_SIZE(led_devs)]; +static struct regulator_init_data regulator_pdata[ARRAY_SIZE(regulator_devs)]; +static struct pm860x_touch_pdata touch_pdata; +static struct pm860x_power_pdata power_pdata; + struct pm860x_irq_data { int reg; int mask_reg; @@ -595,37 +503,212 @@ static void device_irq_exit(struct pm860x_chip *chip) free_irq(chip->core_irq, chip); } -static void __devinit device_8606_init(struct pm860x_chip *chip, - struct i2c_client *i2c, - struct pm860x_platform_data *pdata) +static void __devinit device_bk_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) { int ret; + int i, j, id; + + if ((pdata == NULL) || (pdata->backlight == NULL)) + return; + + if (pdata->num_backlights > ARRAY_SIZE(bk_devs)) + pdata->num_backlights = ARRAY_SIZE(bk_devs); + + for (i = 0; i < pdata->num_backlights; i++) { + memcpy(&bk_pdata[i], &pdata->backlight[i], + sizeof(struct pm860x_backlight_pdata)); + bk_devs[i].mfd_data = &bk_pdata[i]; + + for (j = 0; j < ARRAY_SIZE(bk_devs); j++) { + id = bk_resources[j].start; + if (bk_pdata[i].flags != id) + continue; + + bk_devs[i].num_resources = 1; + bk_devs[i].resources = &bk_resources[j]; + ret = mfd_add_devices(chip->dev, 0, + &bk_devs[i], 1, + &bk_resources[j], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add " + "backlight subdev\n"); + return; + } + } + } +} - if (pdata && pdata->backlight) { - ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0], - ARRAY_SIZE(backlight_devs), - &backlight_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add backlight " - "subdev\n"); - goto out_dev; +static void __devinit device_led_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + int i, j, id; + + if ((pdata == NULL) || (pdata->led == NULL)) + return; + + if (pdata->num_leds > ARRAY_SIZE(led_devs)) + pdata->num_leds = ARRAY_SIZE(led_devs); + + for (i = 0; i < pdata->num_leds; i++) { + memcpy(&led_pdata[i], &pdata->led[i], + sizeof(struct pm860x_led_pdata)); + led_devs[i].mfd_data = &led_pdata[i]; + + for (j = 0; j < ARRAY_SIZE(led_devs); j++) { + id = led_resources[j].start; + if (led_pdata[i].flags != id) + continue; + + led_devs[i].num_resources = 1; + led_devs[i].resources = &led_resources[j], + ret = mfd_add_devices(chip->dev, 0, + &led_devs[i], 1, + &led_resources[j], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add " + "led subdev\n"); + return; + } } } +} - if (pdata && pdata->led) { - ret = mfd_add_devices(chip->dev, 0, &led_devs[0], - ARRAY_SIZE(led_devs), - &led_resources[0], 0); +static void __devinit device_regulator_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + struct regulator_init_data *initdata; + int ret; + int i, j; + + if ((pdata == NULL) || (pdata->regulator == NULL)) + return; + + if (pdata->num_regulators > ARRAY_SIZE(regulator_devs)) + pdata->num_regulators = ARRAY_SIZE(regulator_devs); + + for (i = 0, j = -1; i < pdata->num_regulators; i++) { + initdata = &pdata->regulator[i]; + if (strstr(initdata->constraints.name, "BUCK")) { + sscanf(initdata->constraints.name, "BUCK%d", &j); + /* BUCK1 ~ BUCK3 */ + if ((j < 1) || (j > 3)) { + dev_err(chip->dev, "Failed to add constraint " + "(%s)\n", initdata->constraints.name); + goto out; + } + j = (j - 1) + PM8607_ID_BUCK1; + } + if (strstr(initdata->constraints.name, "LDO")) { + sscanf(initdata->constraints.name, "LDO%d", &j); + /* LDO1 ~ LDO15 */ + if ((j < 1) || (j > 15)) { + dev_err(chip->dev, "Failed to add constraint " + "(%s)\n", initdata->constraints.name); + goto out; + } + j = (j - 1) + PM8607_ID_LDO1; + } + if (j == -1) { + dev_err(chip->dev, "Failed to add constraint (%s)\n", + initdata->constraints.name); + goto out; + } + memcpy(®ulator_pdata[i], &pdata->regulator[i], + sizeof(struct regulator_init_data)); + regulator_devs[i].mfd_data = ®ulator_pdata[i]; + regulator_devs[i].num_resources = 1; + regulator_devs[i].resources = ®ulator_resources[j]; + + ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[i], 1, + ®ulator_resources[j], 0); if (ret < 0) { - dev_err(chip->dev, "Failed to add led " - "subdev\n"); - goto out_dev; + dev_err(chip->dev, "Failed to add regulator subdev\n"); + goto out; } } +out: return; -out_dev: - mfd_remove_devices(chip->dev); - device_irq_exit(chip); +} + +static void __devinit device_touch_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + if ((pdata == NULL) || (pdata->touch == NULL)) + return; + + memcpy(&touch_pdata, pdata->touch, sizeof(struct pm860x_touch_pdata)); + touch_devs[0].mfd_data = &touch_pdata; + touch_devs[0].num_resources = ARRAY_SIZE(touch_resources); + touch_devs[0].resources = &touch_resources[0]; + ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], + ARRAY_SIZE(touch_devs), &touch_resources[0], + chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add touch subdev\n"); +} + +static void __devinit device_power_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + if ((pdata == NULL) || (pdata->power == NULL)) + return; + + memcpy(&power_pdata, pdata->power, sizeof(struct pm860x_power_pdata)); + power_devs[0].mfd_data = &power_pdata; + power_devs[0].num_resources = ARRAY_SIZE(battery_resources); + power_devs[0].resources = &battery_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1, + &battery_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add battery subdev\n"); + + power_devs[1].mfd_data = &power_pdata; + power_devs[1].num_resources = ARRAY_SIZE(charger_resources); + power_devs[1].resources = &charger_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1, + &charger_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add charger subdev\n"); +} + +static void __devinit device_onkey_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources); + onkey_devs[0].resources = &onkey_resources[0], + ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), &onkey_resources[0], + chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add onkey subdev\n"); +} + +static void __devinit device_codec_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + codec_devs[0].num_resources = ARRAY_SIZE(codec_resources); + codec_devs[0].resources = &codec_resources[0], + ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], + ARRAY_SIZE(codec_devs), &codec_resources[0], 0); + if (ret < 0) + dev_err(chip->dev, "Failed to add codec subdev\n"); } static void __devinit device_8607_init(struct pm860x_chip *chip, @@ -683,55 +766,11 @@ static void __devinit device_8607_init(struct pm860x_chip *chip, if (ret < 0) goto out; - ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], - ARRAY_SIZE(regulator_devs), - ®ulator_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add regulator subdev\n"); - goto out_dev; - } - - if (pdata && pdata->touch) { - ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], - ARRAY_SIZE(touch_devs), - &touch_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add touch " - "subdev\n"); - goto out_dev; - } - } - - if (pdata && pdata->power) { - ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), - &power_supply_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add power supply " - "subdev\n"); - goto out_dev; - } - } - - ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], - ARRAY_SIZE(onkey_devs), - &onkey_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add onkey subdev\n"); - goto out_dev; - } - - ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], - ARRAY_SIZE(codec_devs), - &codec_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add codec subdev\n"); - goto out_dev; - } - return; -out_dev: - mfd_remove_devices(chip->dev); - device_irq_exit(chip); + device_regulator_init(chip, i2c, pdata); + device_onkey_init(chip, i2c, pdata); + device_touch_init(chip, i2c, pdata); + device_power_init(chip, i2c, pdata); + device_codec_init(chip, i2c, pdata); out: return; } @@ -743,7 +782,8 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, switch (chip->id) { case CHIP_PM8606: - device_8606_init(chip, chip->client, pdata); + device_bk_init(chip, chip->client, pdata); + device_led_init(chip, chip->client, pdata); break; case CHIP_PM8607: device_8607_init(chip, chip->client, pdata); @@ -753,7 +793,8 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, if (chip->companion) { switch (chip->id) { case CHIP_PM8607: - device_8606_init(chip, chip->companion, pdata); + device_bk_init(chip, chip->companion, pdata); + device_led_init(chip, chip->companion, pdata); break; case CHIP_PM8606: device_8607_init(chip, chip->companion, pdata); diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c index bc02e6b21608..e017dc88622a 100644 --- a/drivers/mfd/88pm860x-i2c.c +++ b/drivers/mfd/88pm860x-i2c.c @@ -126,6 +126,109 @@ out: } EXPORT_SYMBOL(pm860x_set_bits); +int pm860x_page_reg_read(struct i2c_client *i2c, int reg) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero = 0; + unsigned char data; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_read_device(i2c, reg, 1, &data); + if (ret >= 0) + ret = (int)data; + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_reg_read); + +int pm860x_page_reg_write(struct i2c_client *i2c, int reg, + unsigned char data) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_write_device(i2c, reg, 1, &data); + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_reg_write); + +int pm860x_page_bulk_read(struct i2c_client *i2c, int reg, + int count, unsigned char *buf) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero = 0; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_read_device(i2c, reg, count, buf); + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_bulk_read); + +int pm860x_page_bulk_write(struct i2c_client *i2c, int reg, + int count, unsigned char *buf) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero = 0; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_write_device(i2c, reg, count, buf); + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_bulk_write); + +int pm860x_page_set_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero; + unsigned char value; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_read_device(i2c, reg, 1, &value); + if (ret < 0) + goto out; + value &= ~mask; + value |= data; + ret = pm860x_write_device(i2c, reg, 1, &value); +out: + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_set_bits); static const struct i2c_device_id pm860x_id_table[] = { { "88PM860x", 0 }, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fdca643249e1..8d7d098f7a03 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -129,6 +129,17 @@ config UCB1400_CORE To compile this driver as a module, choose M here: the module will be called ucb1400_core. +config TPS6105X + tristate "TPS61050/61052 Boost Converters" + depends on I2C + select REGULATOR + select REGULATOR_FIXED_VOLTAGE + help + This option enables a driver for the TP61050/TPS61052 + high-power "white LED driver". This boost converter is + sometimes used for other things than white LEDs, and + also contains a GPIO pin. + config TPS65010 tristate "TPS6501x Power Management chips" depends on I2C && GPIOLIB @@ -178,6 +189,16 @@ config TWL4030_CORE high speed USB OTG transceiver, an audio codec (on most versions) and many other features. +config TWL4030_MADC + tristate "Texas Instruments TWL4030 MADC" + depends on TWL4030_CORE + help + This driver provides support for triton TWL4030-MADC. The + driver supports both RT and SW conversion methods. + + This driver can be built as a module. If so it will be + named twl4030-madc + config TWL4030_POWER bool "Support power resources on TWL4030 family chips" depends on TWL4030_CORE && ARM @@ -304,6 +325,18 @@ config MFD_MAX8925 accessing the device, additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX8997 + bool "Maxim Semiconductor MAX8997/8966 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + help + Say yes here to support for Maxim Semiconductor MAX8998/8966. + This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, + MUIC controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX8998 bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -534,6 +567,13 @@ config AB8500_DEBUG Select this option if you want debug information using the debug filesystem, debugfs. +config AB8500_GPADC + bool "AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y + 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 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f0e25cad762e..47f5709f3828 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -33,11 +33,13 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o +obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o @@ -61,6 +63,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o +obj-$(CONFIG_MFD_MAX8997) += max8997.o obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o pcf50633-objs := pcf50633-core.o pcf50633-irq.o @@ -71,9 +74,10 @@ 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_AB8500_CORE) += ab8500-core.o +obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o +obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 4193af5f2743..a751927047ac 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -613,7 +613,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_get_priv.ab3100 = ab3100; ab3100_get_priv.mode = false; ab3100_get_reg_file = debugfs_create_file("get_reg", - S_IWUGO, ab3100_dir, &ab3100_get_priv, + S_IWUSR, ab3100_dir, &ab3100_get_priv, &ab3100_get_set_reg_fops); if (!ab3100_get_reg_file) { err = -ENOMEM; @@ -623,7 +623,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_set_priv.ab3100 = ab3100; ab3100_set_priv.mode = true; ab3100_set_reg_file = debugfs_create_file("set_reg", - S_IWUGO, ab3100_dir, &ab3100_set_priv, + S_IWUSR, ab3100_dir, &ab3100_set_priv, &ab3100_get_set_reg_fops); if (!ab3100_set_reg_file) { err = -ENOMEM; @@ -949,10 +949,8 @@ static int __devinit ab3100_probe(struct i2c_client *client, goto exit_no_ops; /* Set up and register the platform devices. */ - for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) { - ab3100_devs[i].platform_data = ab3100_plf_data; - ab3100_devs[i].data_size = sizeof(struct ab3100_platform_data); - } + for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) + ab3100_devs[i].mfd_data = ab3100_plf_data; err = mfd_add_devices(&client->dev, 0, ab3100_devs, ARRAY_SIZE(ab3100_devs), NULL, 0); diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index 5fbca346b998..c12d04285226 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1053,17 +1053,17 @@ static inline void ab3550_setup_debugfs(struct ab3550 *ab) goto exit_destroy_dir; ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops); + (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_IWUGO), ab3550_dir, ab, &ab3550_address_fops); + (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_IWUGO), ab3550_dir, ab, &ab3550_val_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); if (!ab3550_val_file) goto exit_destroy_address; @@ -1320,10 +1320,8 @@ static int __init ab3550_probe(struct i2c_client *client, 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].data_size = ab3550_plf_data->dev_data_sz[i]; - } + for (i = 0; i < AB3550_NUM_DEVICES; i++) + ab3550_devs[i].mfd_data = ab3550_plf_data->dev_data[i]; err = mfd_add_devices(&client->dev, 0, ab3550_devs, ARRAY_SIZE(ab3550_devs), NULL, diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index b6887014d687..6e185b272d00 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -4,7 +4,7 @@ * License Terms: GNU General Public License v2 * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> * Author: Rabin Vincent <rabin.vincent@stericsson.com> - * Changes: Mattias Wallin <mattias.wallin@stericsson.com> + * Author: Mattias Wallin <mattias.wallin@stericsson.com> */ #include <linux/kernel.h> @@ -90,6 +90,7 @@ #define AB8500_IT_MASK24_REG 0x57 #define AB8500_REV_REG 0x80 +#define AB8500_SWITCH_OFF_STATUS 0x00 /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt @@ -652,10 +653,38 @@ static ssize_t show_chip_id(struct device *dev, return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL); } +/* + * ab8500 has switched off due to (SWITCH_OFF_STATUS): + * 0x01 Swoff bit programming + * 0x02 Thermal protection activation + * 0x04 Vbat lower then BattOk falling threshold + * 0x08 Watchdog expired + * 0x10 Non presence of 32kHz clock + * 0x20 Battery level lower than power on reset threshold + * 0x40 Power on key 1 pressed longer than 10 seconds + * 0x80 DB8500 thermal shutdown + */ +static ssize_t show_switch_off_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + u8 value; + struct ab8500 *ab8500; + + ab8500 = dev_get_drvdata(dev); + ret = get_register_interruptible(ab8500, AB8500_RTC, + AB8500_SWITCH_OFF_STATUS, &value); + if (ret < 0) + return ret; + return sprintf(buf, "%#x\n", value); +} + static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); +static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); static struct attribute *ab8500_sysfs_entries[] = { &dev_attr_chip_id.attr, + &dev_attr_switch_off_status.attr, NULL, }; @@ -686,9 +715,10 @@ int __devinit ab8500_init(struct ab8500 *ab8500) * 0x10 - Cut 1.0 * 0x11 - Cut 1.1 * 0x20 - Cut 2.0 + * 0x30 - Cut 3.0 */ - if (value == 0x0 || value == 0x10 || value == 0x11 || value == 0x20) { - ab8500->revision = value; + if (value == 0x0 || value == 0x10 || value == 0x11 || value == 0x20 || + value == 0x30) { dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); } else { dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); @@ -696,6 +726,24 @@ int __devinit ab8500_init(struct ab8500 *ab8500) } ab8500->chip_id = value; + /* + * ab8500 has switched off due to (SWITCH_OFF_STATUS): + * 0x01 Swoff bit programming + * 0x02 Thermal protection activation + * 0x04 Vbat lower then BattOk falling threshold + * 0x08 Watchdog expired + * 0x10 Non presence of 32kHz clock + * 0x20 Battery level lower than power on reset threshold + * 0x40 Power on key 1 pressed longer than 10 seconds + * 0x80 DB8500 thermal shutdown + */ + + ret = get_register_interruptible(ab8500, AB8500_RTC, + AB8500_SWITCH_OFF_STATUS, &value); + if (ret < 0) + return ret; + dev_info(ab8500->dev, "switch off status: %#x", value); + if (plat && plat->init) plat->init(ab8500); @@ -764,6 +812,6 @@ int __devexit ab8500_exit(struct ab8500 *ab8500) return 0; } -MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent"); +MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent"); MODULE_DESCRIPTION("AB8500 MFD core"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 3c1541ae7223..64748e42ac03 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -585,18 +585,18 @@ static int __devinit ab8500_debug_probe(struct platform_device *plf) goto exit_destroy_dir; ab8500_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops); + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops); if (!ab8500_bank_file) goto exit_destroy_reg; ab8500_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_address_fops); if (!ab8500_address_file) goto exit_destroy_bank; ab8500_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops); + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops); if (!ab8500_val_file) goto exit_destroy_address; diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c new file mode 100644 index 000000000000..bc93b2e8230c --- /dev/null +++ b/drivers/mfd/ab8500-gpadc.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Arun R Murthy <arun.murthy@stericsson.com> + * Author: Daniel Willerud <daniel.willerud@stericsson.com> + * Author: Johan Palsson <johan.palsson@stericsson.com> + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/mfd/ab8500.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/ab8500/gpadc.h> + +/* + * GPADC register offsets + * Bank : 0x0A + */ +#define AB8500_GPADC_CTRL1_REG 0x00 +#define AB8500_GPADC_CTRL2_REG 0x01 +#define AB8500_GPADC_CTRL3_REG 0x02 +#define AB8500_GPADC_AUTO_TIMER_REG 0x03 +#define AB8500_GPADC_STAT_REG 0x04 +#define AB8500_GPADC_MANDATAL_REG 0x05 +#define AB8500_GPADC_MANDATAH_REG 0x06 +#define AB8500_GPADC_AUTODATAL_REG 0x07 +#define AB8500_GPADC_AUTODATAH_REG 0x08 +#define AB8500_GPADC_MUX_CTRL_REG 0x09 + +/* + * OTP register offsets + * Bank : 0x15 + */ +#define AB8500_GPADC_CAL_1 0x0F +#define AB8500_GPADC_CAL_2 0x10 +#define AB8500_GPADC_CAL_3 0x11 +#define AB8500_GPADC_CAL_4 0x12 +#define AB8500_GPADC_CAL_5 0x13 +#define AB8500_GPADC_CAL_6 0x14 +#define AB8500_GPADC_CAL_7 0x15 + +/* gpadc constants */ +#define EN_VINTCORE12 0x04 +#define EN_VTVOUT 0x02 +#define EN_GPADC 0x01 +#define DIS_GPADC 0x00 +#define SW_AVG_16 0x60 +#define ADC_SW_CONV 0x04 +#define EN_ICHAR 0x80 +#define EN_BUF 0x40 +#define DIS_ZERO 0x00 +#define GPADC_BUSY 0x01 + +/* GPADC constants from AB8500 spec, UM0836 */ +#define ADC_RESOLUTION 1024 +#define ADC_CH_BTEMP_MIN 0 +#define ADC_CH_BTEMP_MAX 1350 +#define ADC_CH_DIETEMP_MIN 0 +#define ADC_CH_DIETEMP_MAX 1350 +#define ADC_CH_CHG_V_MIN 0 +#define ADC_CH_CHG_V_MAX 20030 +#define ADC_CH_ACCDET2_MIN 0 +#define ADC_CH_ACCDET2_MAX 2500 +#define ADC_CH_VBAT_MIN 2300 +#define ADC_CH_VBAT_MAX 4800 +#define ADC_CH_CHG_I_MIN 0 +#define ADC_CH_CHG_I_MAX 1500 +#define ADC_CH_BKBAT_MIN 0 +#define ADC_CH_BKBAT_MAX 3200 + +/* This is used to not lose precision when dividing to get gain and offset */ +#define CALIB_SCALE 1000 + +enum cal_channels { + ADC_INPUT_VMAIN = 0, + ADC_INPUT_BTEMP, + ADC_INPUT_VBAT, + NBR_CAL_INPUTS, +}; + +/** + * struct adc_cal_data - Table for storing gain and offset for the calibrated + * ADC channels + * @gain: Gain of the ADC channel + * @offset: Offset of the ADC channel + */ +struct adc_cal_data { + u64 gain; + u64 offset; +}; + +/** + * struct ab8500_gpadc - AB8500 GPADC device information + * @dev: pointer to the struct device + * @node: a list of AB8500 GPADCs, hence prepared for + reentrance + * @ab8500_gpadc_complete: pointer to the struct completion, to indicate + * the completion of gpadc conversion + * @ab8500_gpadc_lock: structure of type mutex + * @regu: pointer to the struct regulator + * @irq: interrupt number that is used by gpadc + * @cal_data array of ADC calibration data structs + */ +struct ab8500_gpadc { + struct device *dev; + struct list_head node; + struct completion ab8500_gpadc_complete; + struct mutex ab8500_gpadc_lock; + struct regulator *regu; + int irq; + struct adc_cal_data cal_data[NBR_CAL_INPUTS]; +}; + +static LIST_HEAD(ab8500_gpadc_list); + +/** + * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC + * (i.e. the first GPADC in the instance list) + */ +struct ab8500_gpadc *ab8500_gpadc_get(char *name) +{ + struct ab8500_gpadc *gpadc; + + list_for_each_entry(gpadc, &ab8500_gpadc_list, node) { + if (!strcmp(name, dev_name(gpadc->dev))) + return gpadc; + } + + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(ab8500_gpadc_get); + +static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input, + int ad_value) +{ + int res; + + switch (input) { + case MAIN_CHARGER_V: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) { + res = ADC_CH_CHG_V_MIN + (ADC_CH_CHG_V_MAX - + ADC_CH_CHG_V_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VMAIN].gain + + gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE; + break; + + case BAT_CTRL: + case BTEMP_BALL: + case ACC_DETECT1: + case ADC_AUX1: + case ADC_AUX2: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_BTEMP].gain) { + res = ADC_CH_BTEMP_MIN + (ADC_CH_BTEMP_MAX - + ADC_CH_BTEMP_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_BTEMP].gain + + gpadc->cal_data[ADC_INPUT_BTEMP].offset) / CALIB_SCALE; + break; + + case MAIN_BAT_V: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) { + res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX - + ADC_CH_VBAT_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VBAT].gain + + gpadc->cal_data[ADC_INPUT_VBAT].offset) / CALIB_SCALE; + break; + + case DIE_TEMP: + res = ADC_CH_DIETEMP_MIN + + (ADC_CH_DIETEMP_MAX - ADC_CH_DIETEMP_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case ACC_DETECT2: + res = ADC_CH_ACCDET2_MIN + + (ADC_CH_ACCDET2_MAX - ADC_CH_ACCDET2_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case VBUS_V: + res = ADC_CH_CHG_V_MIN + + (ADC_CH_CHG_V_MAX - ADC_CH_CHG_V_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case MAIN_CHARGER_C: + case USB_CHARGER_C: + res = ADC_CH_CHG_I_MIN + + (ADC_CH_CHG_I_MAX - ADC_CH_CHG_I_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case BK_BAT_V: + res = ADC_CH_BKBAT_MIN + + (ADC_CH_BKBAT_MAX - ADC_CH_BKBAT_MIN) * ad_value / + ADC_RESOLUTION; + break; + + default: + dev_err(gpadc->dev, + "unknown channel, not possible to convert\n"); + res = -EINVAL; + break; + + } + return res; +} + +/** + * ab8500_gpadc_convert() - gpadc conversion + * @input: analog input to be converted to digital data + * + * This function converts the selected analog i/p to digital + * data. + */ +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) +{ + int ret; + u16 data = 0; + int looplimit = 0; + u8 val, low_data, high_data; + + if (!gpadc) + return -ENODEV; + + mutex_lock(&gpadc->ab8500_gpadc_lock); + /* Enable VTVout LDO this is required for GPADC */ + regulator_enable(gpadc->regu); + + /* Check if ADC is not busy, lock and proceed */ + do { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); + if (ret < 0) + goto out; + if (!(val & GPADC_BUSY)) + break; + msleep(10); + } while (++looplimit < 10); + if (looplimit >= 10 && (val & GPADC_BUSY)) { + dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); + ret = -EINVAL; + goto out; + } + + /* Enable GPADC */ + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n"); + goto out; + } + /* Select the input source and set average samples to 16 */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set avg samples failed\n"); + goto out; + } + /* + * Enable ADC, buffering, select rising edge and enable ADC path + * charging current sense if it needed + */ + switch (input) { + case MAIN_CHARGER_C: + case USB_CHARGER_C: + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | EN_ICHAR, + EN_BUF | EN_ICHAR); + break; + default: + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); + break; + } + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: select falling edge failed\n"); + goto out; + } + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conversion failed\n"); + goto out; + } + /* wait for completion of conversion */ + if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) { + dev_err(gpadc->dev, + "timeout: didnt recieve GPADC conversion interrupt\n"); + ret = -EINVAL; + goto out; + } + + /* Read the converted RAW data */ + ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_MANDATAL_REG, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_MANDATAH_REG, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read high data failed\n"); + goto out; + } + + data = (high_data << 8) | low_data; + /* Disable GPADC */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, DIS_GPADC); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); + goto out; + } + /* Disable VTVout LDO this is required for GPADC */ + regulator_disable(gpadc->regu); + mutex_unlock(&gpadc->ab8500_gpadc_lock); + ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data); + return ret; + +out: + /* + * It has shown to be needed to turn off the GPADC if an error occurs, + * otherwise we might have problem when waiting for the busy bit in the + * GPADC status register to go low. In V1.1 there wait_for_completion + * seems to timeout when waiting for an interrupt.. Not seen in V2.0 + */ + (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, DIS_GPADC); + regulator_disable(gpadc->regu); + mutex_unlock(&gpadc->ab8500_gpadc_lock); + dev_err(gpadc->dev, + "gpadc_conversion: Failed to AD convert channel %d\n", input); + return ret; +} +EXPORT_SYMBOL(ab8500_gpadc_convert); + +/** + * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion + * @irq: irq number + * @data: pointer to the data passed during request irq + * + * This is a interrupt service routine for s/w gpadc conversion completion. + * Notifies the gpadc completion is completed and the converted raw value + * can be read from the registers. + * Returns IRQ status(IRQ_HANDLED) + */ +static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc) +{ + struct ab8500_gpadc *gpadc = _gpadc; + + complete(&gpadc->ab8500_gpadc_complete); + + return IRQ_HANDLED; +} + +static int otp_cal_regs[] = { + AB8500_GPADC_CAL_1, + AB8500_GPADC_CAL_2, + AB8500_GPADC_CAL_3, + AB8500_GPADC_CAL_4, + AB8500_GPADC_CAL_5, + AB8500_GPADC_CAL_6, + AB8500_GPADC_CAL_7, +}; + +static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) +{ + int i; + int ret[ARRAY_SIZE(otp_cal_regs)]; + u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; + + int vmain_high, vmain_low; + int btemp_high, btemp_low; + int vbat_high, vbat_low; + + /* First we read all OTP registers and store the error code */ + for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { + ret[i] = abx500_get_register_interruptible(gpadc->dev, + AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); + if (ret[i] < 0) + dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", + __func__, otp_cal_regs[i]); + } + + /* + * The ADC calibration data is stored in OTP registers. + * The layout of the calibration data is outlined below and a more + * detailed description can be found in UM0836 + * + * vm_h/l = vmain_high/low + * bt_h/l = btemp_high/low + * vb_h/l = vbat_high/low + * + * Data bits: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h9 | vm_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * + * Ideal output ADC codes corresponding to injected input voltages + * during manufacturing is: + * + * vmain_high: Vin = 19500mV / ADC ideal code = 997 + * vmain_low: Vin = 315mV / ADC ideal code = 16 + * btemp_high: Vin = 1300mV / ADC ideal code = 985 + * btemp_low: Vin = 21mV / ADC ideal code = 16 + * vbat_high: Vin = 4700mV / ADC ideal code = 982 + * vbat_low: Vin = 2380mV / ADC ideal code = 33 + */ + + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 - + (CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; + } + + /* Calculate gain and offset for BTEMP if all reads succeeded */ + if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { + btemp_high = (((gpadc_cal[2] & 0x01) << 9) | + (gpadc_cal[3] << 1) | + ((gpadc_cal[4] & 0x80) >> 7)); + + btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + + gpadc->cal_data[ADC_INPUT_BTEMP].gain = + CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); + + gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 - + (CALIB_SCALE * (1300 - 21) / + (btemp_high - btemp_low)) * btemp_high; + } else { + gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0; + } + + /* Calculate gain and offset for VBAT if all reads succeeded */ + if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { + vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); + vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + + gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE * + (4700 - 2380) / (vbat_high - vbat_low); + + gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 - + (CALIB_SCALE * (4700 - 2380) / + (vbat_high - vbat_low)) * vbat_high; + } else { + gpadc->cal_data[ADC_INPUT_VBAT].gain = 0; + } + + dev_dbg(gpadc->dev, "VMAIN gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_VMAIN].gain, + gpadc->cal_data[ADC_INPUT_VMAIN].offset); + + dev_dbg(gpadc->dev, "BTEMP gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_BTEMP].gain, + gpadc->cal_data[ADC_INPUT_BTEMP].offset); + + dev_dbg(gpadc->dev, "VBAT gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_VBAT].gain, + gpadc->cal_data[ADC_INPUT_VBAT].offset); +} + +static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct ab8500_gpadc *gpadc; + + gpadc = kzalloc(sizeof(struct ab8500_gpadc), GFP_KERNEL); + if (!gpadc) { + dev_err(&pdev->dev, "Error: No memory\n"); + return -ENOMEM; + } + + gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq < 0) { + dev_err(gpadc->dev, "failed to get platform irq-%d\n", + gpadc->irq); + ret = gpadc->irq; + goto fail; + } + + gpadc->dev = &pdev->dev; + mutex_init(&gpadc->ab8500_gpadc_lock); + + /* Initialize completion used to notify completion of conversion */ + init_completion(&gpadc->ab8500_gpadc_complete); + + /* Register interrupt - SwAdcComplete */ + ret = request_threaded_irq(gpadc->irq, NULL, + ab8500_bm_gpswadcconvend_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc); + if (ret < 0) { + dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", + gpadc->irq); + goto fail; + } + + /* VTVout LDO used to power up ab8500-GPADC */ + gpadc->regu = regulator_get(&pdev->dev, "vddadc"); + if (IS_ERR(gpadc->regu)) { + ret = PTR_ERR(gpadc->regu); + dev_err(gpadc->dev, "failed to get vtvout LDO\n"); + goto fail_irq; + } + ab8500_gpadc_read_calibration_data(gpadc); + list_add_tail(&gpadc->node, &ab8500_gpadc_list); + dev_dbg(gpadc->dev, "probe success\n"); + return 0; +fail_irq: + free_irq(gpadc->irq, gpadc); +fail: + kfree(gpadc); + gpadc = NULL; + return ret; +} + +static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) +{ + struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); + + /* remove this gpadc entry from the list */ + list_del(&gpadc->node); + /* remove interrupt - completion of Sw ADC conversion */ + free_irq(gpadc->irq, gpadc); + /* disable VTVout LDO that is being used by GPADC */ + regulator_put(gpadc->regu); + kfree(gpadc); + gpadc = NULL; + return 0; +} + +static struct platform_driver ab8500_gpadc_driver = { + .probe = ab8500_gpadc_probe, + .remove = __devexit_p(ab8500_gpadc_remove), + .driver = { + .name = "ab8500-gpadc", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_gpadc_init(void) +{ + return platform_driver_register(&ab8500_gpadc_driver); +} + +static void __exit ab8500_gpadc_exit(void) +{ + platform_driver_unregister(&ab8500_gpadc_driver); +} + +subsys_initcall_sync(ab8500_gpadc_init); +module_exit(ab8500_gpadc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson"); +MODULE_ALIAS("platform:ab8500_gpadc"); +MODULE_DESCRIPTION("AB8500 GPADC driver"); diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c new file mode 100644 index 000000000000..392185965b39 --- /dev/null +++ b/drivers/mfd/ab8500-sysctrl.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson. + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/mfd/ab8500.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/ab8500/sysctrl.h> + +static struct device *sysctrl_dev; + +static inline bool valid_bank(u8 bank) +{ + return ((bank == AB8500_SYS_CTRL1_BLOCK) || + (bank == AB8500_SYS_CTRL2_BLOCK)); +} + +int ab8500_sysctrl_read(u16 reg, u8 *value) +{ + u8 bank; + + if (sysctrl_dev == NULL) + return -EAGAIN; + + bank = (reg >> 8); + if (!valid_bank(bank)) + return -EINVAL; + + return abx500_get_register_interruptible(sysctrl_dev, bank, + (u8)(reg & 0xFF), value); +} + +int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) +{ + u8 bank; + + if (sysctrl_dev == NULL) + return -EAGAIN; + + bank = (reg >> 8); + if (!valid_bank(bank)) + return -EINVAL; + + return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank, + (u8)(reg & 0xFF), mask, value); +} + +static int __devinit ab8500_sysctrl_probe(struct platform_device *pdev) +{ + sysctrl_dev = &pdev->dev; + return 0; +} + +static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev) +{ + sysctrl_dev = NULL; + return 0; +} + +static struct platform_driver ab8500_sysctrl_driver = { + .driver = { + .name = "ab8500-sysctrl", + .owner = THIS_MODULE, + }, + .probe = ab8500_sysctrl_probe, + .remove = __devexit_p(ab8500_sysctrl_remove), +}; + +static int __init ab8500_sysctrl_init(void) +{ + return platform_driver_register(&ab8500_sysctrl_driver); +} +subsys_initcall(ab8500_sysctrl_init); + +MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com"); +MODULE_DESCRIPTION("AB8500 system control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 3122139b4300..f1d88483112c 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -321,27 +321,27 @@ static int __devexit adp5520_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int adp5520_suspend(struct i2c_client *client, - pm_message_t state) +static int adp5520_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct adp5520_chip *chip = dev_get_drvdata(&client->dev); adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); return 0; } -static int adp5520_resume(struct i2c_client *client) +static int adp5520_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct adp5520_chip *chip = dev_get_drvdata(&client->dev); adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); return 0; } -#else -#define adp5520_suspend NULL -#define adp5520_resume NULL #endif +static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume); + static const struct i2c_device_id adp5520_id[] = { { "pmic-adp5520", ID_ADP5520 }, { "pmic-adp5501", ID_ADP5501 }, @@ -353,11 +353,10 @@ static struct i2c_driver adp5520_driver = { .driver = { .name = "adp5520", .owner = THIS_MODULE, + .pm = &adp5520_pm, }, .probe = adp5520_probe, .remove = __devexit_p(adp5520_remove), - .suspend = adp5520_suspend, - .resume = adp5520_resume, .id_table = adp5520_id, }; diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index c45e6305b26f..0241f08fc00d 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -682,7 +682,7 @@ static struct mfd_cell asic3_cell_ds1wm = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .driver_data = &ds1wm_pdata, + .mfd_data = &ds1wm_pdata, .num_resources = ARRAY_SIZE(ds1wm_resources), .resources = ds1wm_resources, }; @@ -783,7 +783,7 @@ static struct mfd_cell asic3_cell_mmc = { .name = "tmio-mmc", .enable = asic3_mmc_enable, .disable = asic3_mmc_disable, - .driver_data = &asic3_mmc_data, + .mfd_data = &asic3_mmc_data, .num_resources = ARRAY_SIZE(asic3_mmc_resources), .resources = asic3_mmc_resources, }; @@ -810,9 +810,6 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, ds1wm_resources[0].start >>= asic->bus_shift; ds1wm_resources[0].end >>= asic->bus_shift; - asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm; - asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm); - /* MMC */ asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) + mem_sdio->start, 0x400 >> asic->bus_shift); @@ -824,9 +821,6 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, asic3_mmc_resources[0].start >>= asic->bus_shift; asic3_mmc_resources[0].end >>= asic->bus_shift; - asic3_cell_mmc.platform_data = &asic3_cell_mmc; - asic3_cell_mmc.data_size = sizeof(asic3_cell_mmc); - ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_ds1wm, 1, mem, asic->irq_base); if (ret < 0) diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 59ca6f151e78..886a06871065 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -39,6 +39,37 @@ enum cs5535_mfd_bars { NR_BARS, }; +static int cs5535_mfd_res_enable(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -EIO; + } + + if (!request_region(res->start, resource_size(res), DRV_NAME)) { + dev_err(&pdev->dev, "can't request region\n"); + return -EIO; + } + + return 0; +} + +static int cs5535_mfd_res_disable(struct platform_device *pdev) +{ + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -EIO; + } + + release_region(res->start, resource_size(res)); + return 0; +} + static __devinitdata struct resource cs5535_mfd_resources[NR_BARS]; static __devinitdata struct mfd_cell cs5535_mfd_cells[] = { @@ -65,12 +96,18 @@ static __devinitdata struct mfd_cell cs5535_mfd_cells[] = { .name = "cs5535-pms", .num_resources = 1, .resources = &cs5535_mfd_resources[PMS_BAR], + + .enable = cs5535_mfd_res_enable, + .disable = cs5535_mfd_res_disable, }, { .id = ACPI_BAR, .name = "cs5535-acpi", .num_resources = 1, .resources = &cs5535_mfd_resources[ACPI_BAR], + + .enable = cs5535_mfd_res_enable, + .disable = cs5535_mfd_res_disable, }, }; diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index fdd8a1b8bc67..414783b04849 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -119,12 +119,12 @@ static int __init davinci_vc_probe(struct platform_device *pdev) /* Voice codec interface client */ cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL]; cell->name = "davinci-vcif"; - cell->driver_data = davinci_vc; + cell->mfd_data = davinci_vc; /* Voice codec CQ93VC client */ cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL]; cell->name = "cq93vc-codec"; - cell->driver_data = davinci_vc; + cell->mfd_data = davinci_vc; ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells, DAVINCI_VC_CELLS, NULL, 0); diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 7bc752272dc1..fb9770b39a32 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -117,7 +117,7 @@ static struct mfd_cell ds1wm_cell __initdata = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .driver_data = &ds1wm_pdata, + .mfd_data = &ds1wm_pdata, .num_resources = 2, .resources = ds1wm_resources, }; @@ -165,8 +165,6 @@ static int __init pasic3_probe(struct platform_device *pdev) ds1wm_pdata.clock_rate = pdata->clock_rate; /* the first 5 PASIC3 registers control the DS1WM */ ds1wm_resources[0].end = (5 << asic->bus_shift) - 1; - ds1wm_cell.platform_data = &ds1wm_cell; - ds1wm_cell.data_size = sizeof(ds1wm_cell); ret = mfd_add_devices(&pdev->dev, pdev->id, &ds1wm_cell, 1, r, irq); if (ret < 0) @@ -174,9 +172,6 @@ static int __init pasic3_probe(struct platform_device *pdev) } if (pdata && pdata->led_pdata) { - led_cell.driver_data = pdata->led_pdata; - led_cell.platform_data = &led_cell; - led_cell.data_size = sizeof(ds1wm_cell); ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0); if (ret < 0) dev_warn(dev, "failed to register LED device\n"); diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 36a166bcdb08..fc4191137e90 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -86,8 +86,7 @@ static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv, /* Add platform data */ pdata->modno = modno; - cell->platform_data = pdata; - cell->data_size = sizeof(*pdata); + cell->mfd_data = pdata; /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ res->flags = IORESOURCE_MEM; diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 0cc59795f600..aa518b9beaf5 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -232,8 +232,6 @@ const struct mfd_cell jz4740_adc_cells[] = { .name = "jz4740-hwmon", .num_resources = ARRAY_SIZE(jz4740_hwmon_resources), .resources = jz4740_hwmon_resources, - .platform_data = (void *)&jz4740_adc_cells[0], - .data_size = sizeof(struct mfd_cell), .enable = jz4740_adc_cell_enable, .disable = jz4740_adc_cell_disable, @@ -243,8 +241,6 @@ const struct mfd_cell jz4740_adc_cells[] = { .name = "jz4740-battery", .num_resources = ARRAY_SIZE(jz4740_battery_resources), .resources = jz4740_battery_resources, - .platform_data = (void *)&jz4740_adc_cells[1], - .data_size = sizeof(struct mfd_cell), .enable = jz4740_adc_cell_enable, .disable = jz4740_adc_cell_disable, diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 51b2f6065a0b..ea3f52c07ef7 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -61,6 +61,7 @@ static struct mfd_cell lpc_sch_cells[] = { static struct pci_device_id lpc_sch_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) }, { 0, } }; MODULE_DEVICE_TABLE(pci, lpc_sch_ids); @@ -70,6 +71,7 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, { unsigned int base_addr_cfg; unsigned short base_addr; + int i; pci_read_config_dword(dev, SMBASE, &base_addr_cfg); if (!(base_addr_cfg & (1 << 31))) { @@ -99,7 +101,10 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, gpio_sch_resource.start = base_addr; gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; - return mfd_add_devices(&dev->dev, -1, + for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++) + lpc_sch_cells[i].id = id->device; + + return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0); } diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c new file mode 100644 index 000000000000..5d1fca0277ef --- /dev/null +++ b/drivers/mfd/max8997.c @@ -0,0 +1,427 @@ +/* + * max8997.c - mfd core driver for the Maxim 8966 and 8997 + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@smasung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8998.c + */ + +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max8997.h> +#include <linux/mfd/max8997-private.h> + +#define I2C_ADDR_PMIC (0xCC >> 1) +#define I2C_ADDR_MUIC (0x4A >> 1) +#define I2C_ADDR_BATTERY (0x6C >> 1) +#define I2C_ADDR_RTC (0x0C >> 1) +#define I2C_ADDR_HAPTIC (0x90 >> 1) + +static struct mfd_cell max8997_devs[] = { + { .name = "max8997-pmic", }, + { .name = "max8997-rtc", }, + { .name = "max8997-battery", }, + { .name = "max8997-haptic", }, + { .name = "max8997-muic", }, + { .name = "max8997-flash", }, +}; + +int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_read_byte_data(i2c, reg); + mutex_unlock(&max8997->iolock); + if (ret < 0) + return ret; + + ret &= 0xff; + *dest = ret; + return 0; +} +EXPORT_SYMBOL_GPL(max8997_read_reg); + +int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&max8997->iolock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(max8997_bulk_read); + +int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_write_byte_data(i2c, reg, value); + mutex_unlock(&max8997->iolock); + return ret; +} +EXPORT_SYMBOL_GPL(max8997_write_reg); + +int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&max8997->iolock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(max8997_bulk_write); + +int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret >= 0) { + u8 old_val = ret & 0xff; + u8 new_val = (val & mask) | (old_val & (~mask)); + ret = i2c_smbus_write_byte_data(i2c, reg, new_val); + } + mutex_unlock(&max8997->iolock); + return ret; +} +EXPORT_SYMBOL_GPL(max8997_update_reg); + +static int max8997_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max8997_dev *max8997; + struct max8997_platform_data *pdata = i2c->dev.platform_data; + int ret = 0; + + max8997 = kzalloc(sizeof(struct max8997_dev), GFP_KERNEL); + if (max8997 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max8997); + max8997->dev = &i2c->dev; + max8997->i2c = i2c; + max8997->type = id->driver_data; + + if (!pdata) + goto err; + + max8997->wakeup = pdata->wakeup; + + mutex_init(&max8997->iolock); + + max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + i2c_set_clientdata(max8997->rtc, max8997); + max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); + i2c_set_clientdata(max8997->haptic, max8997); + max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); + i2c_set_clientdata(max8997->muic, max8997); + + pm_runtime_set_active(max8997->dev); + + mfd_add_devices(max8997->dev, -1, max8997_devs, + ARRAY_SIZE(max8997_devs), + NULL, 0); + + /* + * TODO: enable others (flash, muic, rtc, battery, ...) and + * check the return value + */ + + if (ret < 0) + goto err_mfd; + + return ret; + +err_mfd: + mfd_remove_devices(max8997->dev); + i2c_unregister_device(max8997->muic); + i2c_unregister_device(max8997->haptic); + i2c_unregister_device(max8997->rtc); +err: + kfree(max8997); + return ret; +} + +static int max8997_i2c_remove(struct i2c_client *i2c) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max8997->dev); + i2c_unregister_device(max8997->muic); + i2c_unregister_device(max8997->haptic); + i2c_unregister_device(max8997->rtc); + kfree(max8997); + + return 0; +} + +static const struct i2c_device_id max8997_i2c_id[] = { + { "max8997", TYPE_MAX8997 }, + { "max8966", TYPE_MAX8966 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); + +u8 max8997_dumpaddr_pmic[] = { + MAX8997_REG_INT1MSK, + MAX8997_REG_INT2MSK, + MAX8997_REG_INT3MSK, + MAX8997_REG_INT4MSK, + MAX8997_REG_MAINCON1, + MAX8997_REG_MAINCON2, + MAX8997_REG_BUCKRAMP, + MAX8997_REG_BUCK1CTRL, + MAX8997_REG_BUCK1DVS1, + MAX8997_REG_BUCK1DVS2, + MAX8997_REG_BUCK1DVS3, + MAX8997_REG_BUCK1DVS4, + MAX8997_REG_BUCK1DVS5, + MAX8997_REG_BUCK1DVS6, + MAX8997_REG_BUCK1DVS7, + MAX8997_REG_BUCK1DVS8, + MAX8997_REG_BUCK2CTRL, + MAX8997_REG_BUCK2DVS1, + MAX8997_REG_BUCK2DVS2, + MAX8997_REG_BUCK2DVS3, + MAX8997_REG_BUCK2DVS4, + MAX8997_REG_BUCK2DVS5, + MAX8997_REG_BUCK2DVS6, + MAX8997_REG_BUCK2DVS7, + MAX8997_REG_BUCK2DVS8, + MAX8997_REG_BUCK3CTRL, + MAX8997_REG_BUCK3DVS, + MAX8997_REG_BUCK4CTRL, + MAX8997_REG_BUCK4DVS, + MAX8997_REG_BUCK5CTRL, + MAX8997_REG_BUCK5DVS1, + MAX8997_REG_BUCK5DVS2, + MAX8997_REG_BUCK5DVS3, + MAX8997_REG_BUCK5DVS4, + MAX8997_REG_BUCK5DVS5, + MAX8997_REG_BUCK5DVS6, + MAX8997_REG_BUCK5DVS7, + MAX8997_REG_BUCK5DVS8, + MAX8997_REG_BUCK6CTRL, + MAX8997_REG_BUCK6BPSKIPCTRL, + MAX8997_REG_BUCK7CTRL, + MAX8997_REG_BUCK7DVS, + MAX8997_REG_LDO1CTRL, + MAX8997_REG_LDO2CTRL, + MAX8997_REG_LDO3CTRL, + MAX8997_REG_LDO4CTRL, + MAX8997_REG_LDO5CTRL, + MAX8997_REG_LDO6CTRL, + MAX8997_REG_LDO7CTRL, + MAX8997_REG_LDO8CTRL, + MAX8997_REG_LDO9CTRL, + MAX8997_REG_LDO10CTRL, + MAX8997_REG_LDO11CTRL, + MAX8997_REG_LDO12CTRL, + MAX8997_REG_LDO13CTRL, + MAX8997_REG_LDO14CTRL, + MAX8997_REG_LDO15CTRL, + MAX8997_REG_LDO16CTRL, + MAX8997_REG_LDO17CTRL, + MAX8997_REG_LDO18CTRL, + MAX8997_REG_LDO21CTRL, + MAX8997_REG_MBCCTRL1, + MAX8997_REG_MBCCTRL2, + MAX8997_REG_MBCCTRL3, + MAX8997_REG_MBCCTRL4, + MAX8997_REG_MBCCTRL5, + MAX8997_REG_MBCCTRL6, + MAX8997_REG_OTPCGHCVS, + MAX8997_REG_SAFEOUTCTRL, + MAX8997_REG_LBCNFG1, + MAX8997_REG_LBCNFG2, + MAX8997_REG_BBCCTRL, + + MAX8997_REG_FLASH1_CUR, + MAX8997_REG_FLASH2_CUR, + MAX8997_REG_MOVIE_CUR, + MAX8997_REG_GSMB_CUR, + MAX8997_REG_BOOST_CNTL, + MAX8997_REG_LEN_CNTL, + MAX8997_REG_FLASH_CNTL, + MAX8997_REG_WDT_CNTL, + MAX8997_REG_MAXFLASH1, + MAX8997_REG_MAXFLASH2, + MAX8997_REG_FLASHSTATUSMASK, + + MAX8997_REG_GPIOCNTL1, + MAX8997_REG_GPIOCNTL2, + MAX8997_REG_GPIOCNTL3, + MAX8997_REG_GPIOCNTL4, + MAX8997_REG_GPIOCNTL5, + MAX8997_REG_GPIOCNTL6, + MAX8997_REG_GPIOCNTL7, + MAX8997_REG_GPIOCNTL8, + MAX8997_REG_GPIOCNTL9, + MAX8997_REG_GPIOCNTL10, + MAX8997_REG_GPIOCNTL11, + MAX8997_REG_GPIOCNTL12, + + MAX8997_REG_LDO1CONFIG, + MAX8997_REG_LDO2CONFIG, + MAX8997_REG_LDO3CONFIG, + MAX8997_REG_LDO4CONFIG, + MAX8997_REG_LDO5CONFIG, + MAX8997_REG_LDO6CONFIG, + MAX8997_REG_LDO7CONFIG, + MAX8997_REG_LDO8CONFIG, + MAX8997_REG_LDO9CONFIG, + MAX8997_REG_LDO10CONFIG, + MAX8997_REG_LDO11CONFIG, + MAX8997_REG_LDO12CONFIG, + MAX8997_REG_LDO13CONFIG, + MAX8997_REG_LDO14CONFIG, + MAX8997_REG_LDO15CONFIG, + MAX8997_REG_LDO16CONFIG, + MAX8997_REG_LDO17CONFIG, + MAX8997_REG_LDO18CONFIG, + MAX8997_REG_LDO21CONFIG, + + MAX8997_REG_DVSOKTIMER1, + MAX8997_REG_DVSOKTIMER2, + MAX8997_REG_DVSOKTIMER4, + MAX8997_REG_DVSOKTIMER5, +}; + +u8 max8997_dumpaddr_muic[] = { + MAX8997_MUIC_REG_INTMASK1, + MAX8997_MUIC_REG_INTMASK2, + MAX8997_MUIC_REG_INTMASK3, + MAX8997_MUIC_REG_CDETCTRL, + MAX8997_MUIC_REG_CONTROL1, + MAX8997_MUIC_REG_CONTROL2, + MAX8997_MUIC_REG_CONTROL3, +}; + +u8 max8997_dumpaddr_haptic[] = { + MAX8997_HAPTIC_REG_CONF1, + MAX8997_HAPTIC_REG_CONF2, + MAX8997_HAPTIC_REG_DRVCONF, + MAX8997_HAPTIC_REG_CYCLECONF1, + MAX8997_HAPTIC_REG_CYCLECONF2, + MAX8997_HAPTIC_REG_SIGCONF1, + MAX8997_HAPTIC_REG_SIGCONF2, + MAX8997_HAPTIC_REG_SIGCONF3, + MAX8997_HAPTIC_REG_SIGCONF4, + MAX8997_HAPTIC_REG_SIGDC1, + MAX8997_HAPTIC_REG_SIGDC2, + MAX8997_HAPTIC_REG_SIGPWMDC1, + MAX8997_HAPTIC_REG_SIGPWMDC2, + MAX8997_HAPTIC_REG_SIGPWMDC3, + MAX8997_HAPTIC_REG_SIGPWMDC4, +}; + +static int max8997_freeze(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++) + max8997_read_reg(i2c, max8997_dumpaddr_pmic[i], + &max8997->reg_dump[i]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++) + max8997_read_reg(i2c, max8997_dumpaddr_muic[i], + &max8997->reg_dump[i + MAX8997_REG_PMIC_END]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++) + max8997_read_reg(i2c, max8997_dumpaddr_haptic[i], + &max8997->reg_dump[i + MAX8997_REG_PMIC_END + + MAX8997_MUIC_REG_END]); + + return 0; +} + +static int max8997_restore(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++) + max8997_write_reg(i2c, max8997_dumpaddr_pmic[i], + max8997->reg_dump[i]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++) + max8997_write_reg(i2c, max8997_dumpaddr_muic[i], + max8997->reg_dump[i + MAX8997_REG_PMIC_END]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++) + max8997_write_reg(i2c, max8997_dumpaddr_haptic[i], + max8997->reg_dump[i + MAX8997_REG_PMIC_END + + MAX8997_MUIC_REG_END]); + + return 0; +} + +const struct dev_pm_ops max8997_pm = { + .freeze = max8997_freeze, + .restore = max8997_restore, +}; + +static struct i2c_driver max8997_i2c_driver = { + .driver = { + .name = "max8997", + .owner = THIS_MODULE, + .pm = &max8997_pm, + }, + .probe = max8997_i2c_probe, + .remove = max8997_i2c_remove, + .id_table = max8997_i2c_id, +}; + +static int __init max8997_i2c_init(void) +{ + return i2c_add_driver(&max8997_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max8997_i2c_init); + +static void __exit max8997_i2c_exit(void) +{ + i2c_del_driver(&max8997_i2c_driver); +} +module_exit(max8997_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 8997 multi-function core driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index bbfe86732602..c00214257da2 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -233,7 +233,7 @@ struct max8998_reg_dump { u8 val; }; #define SAVE_ITEM(x) { .addr = (x), .val = 0x0, } -struct max8998_reg_dump max8998_dump[] = { +static struct max8998_reg_dump max8998_dump[] = { SAVE_ITEM(MAX8998_REG_IRQM1), SAVE_ITEM(MAX8998_REG_IRQM2), SAVE_ITEM(MAX8998_REG_IRQM3), @@ -298,7 +298,7 @@ static int max8998_restore(struct device *dev) return 0; } -const struct dev_pm_ops max8998_pm = { +static const struct dev_pm_ops max8998_pm = { .suspend = max8998_suspend, .resume = max8998_resume, .freeze = max8998_freeze, diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index b9fcaf0004da..668634e89e81 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -683,14 +683,13 @@ out: EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, - const char *format, void *pdata, size_t pdata_size) + const char *format, void *pdata) { char buf[30]; const char *name = mc13xxx_get_chipname(mc13xxx); struct mfd_cell cell = { - .platform_data = pdata, - .data_size = pdata_size, + .mfd_data = pdata, }; /* there is no asnprintf in the kernel :-( */ @@ -706,7 +705,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) { - return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0); + return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL); } static int mc13xxx_probe(struct spi_device *spi) @@ -764,13 +763,8 @@ err_revision: mc13xxx_add_subdevice(mc13xxx, "%s-codec"); if (pdata->flags & MC13XXX_USE_REGULATOR) { - struct mc13xxx_regulator_platform_data regulator_pdata = { - .num_regulators = pdata->num_regulators, - .regulators = pdata->regulators, - }; - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", - ®ulator_pdata, sizeof(regulator_pdata)); + &pdata->regulators); } if (pdata->flags & MC13XXX_USE_RTC) @@ -779,10 +773,8 @@ err_revision: if (pdata->flags & MC13XXX_USE_TOUCHSCREEN) mc13xxx_add_subdevice(mc13xxx, "%s-ts"); - if (pdata->flags & MC13XXX_USE_LED) { - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", - pdata->leds, sizeof(*pdata->leds)); - } + if (pdata->flags & MC13XXX_USE_LED) + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds); return 0; } @@ -811,6 +803,7 @@ static const struct spi_device_id mc13xxx_device_id[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); static struct spi_driver mc13xxx_driver = { .id_table = mc13xxx_device_id, diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index d83ad0f141af..79eda0264fb2 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -18,6 +18,43 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> +int mfd_cell_enable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + int err = 0; + + /* only call enable hook if the cell wasn't previously enabled */ + if (atomic_inc_return(cell->usage_count) == 1) + err = cell->enable(pdev); + + /* if the enable hook failed, decrement counter to allow retries */ + if (err) + atomic_dec(cell->usage_count); + + return err; +} +EXPORT_SYMBOL(mfd_cell_enable); + +int mfd_cell_disable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + int err = 0; + + /* only disable if no other clients are using it */ + if (atomic_dec_return(cell->usage_count) == 0) + err = cell->disable(pdev); + + /* if the disable hook failed, increment to allow retries */ + if (err) + atomic_inc(cell->usage_count); + + /* sanity check; did someone call disable too many times? */ + WARN_ON(atomic_read(cell->usage_count) < 0); + + return err; +} +EXPORT_SYMBOL(mfd_cell_disable); + static int mfd_add_device(struct device *parent, int id, const struct mfd_cell *cell, struct resource *mem_base, @@ -37,14 +74,10 @@ static int mfd_add_device(struct device *parent, int id, goto fail_device; pdev->dev.parent = parent; - platform_set_drvdata(pdev, cell->driver_data); - if (cell->data_size) { - ret = platform_device_add_data(pdev, - cell->platform_data, cell->data_size); - if (ret) - goto fail_res; - } + ret = platform_device_add_data(pdev, cell, sizeof(*cell)); + if (ret) + goto fail_res; for (r = 0; r < cell->num_resources; r++) { res[r].name = cell->resources[r].name; @@ -100,14 +133,22 @@ fail_alloc: } int mfd_add_devices(struct device *parent, int id, - const struct mfd_cell *cells, int n_devs, + struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base) { int i; int ret = 0; + atomic_t *cnts; + + /* initialize reference counting for all cells */ + cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL); + if (!cnts) + return -ENOMEM; for (i = 0; i < n_devs; i++) { + atomic_set(&cnts[i], 0); + cells[i].usage_count = &cnts[i]; ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); if (ret) break; @@ -120,17 +161,89 @@ int mfd_add_devices(struct device *parent, int id, } EXPORT_SYMBOL(mfd_add_devices); -static int mfd_remove_devices_fn(struct device *dev, void *unused) +static int mfd_remove_devices_fn(struct device *dev, void *c) { - platform_device_unregister(to_platform_device(dev)); + struct platform_device *pdev = to_platform_device(dev); + const struct mfd_cell *cell = mfd_get_cell(pdev); + atomic_t **usage_count = c; + + /* find the base address of usage_count pointers (for freeing) */ + if (!*usage_count || (cell->usage_count < *usage_count)) + *usage_count = cell->usage_count; + + platform_device_unregister(pdev); return 0; } void mfd_remove_devices(struct device *parent) { - device_for_each_child(parent, NULL, mfd_remove_devices_fn); + atomic_t *cnts = NULL; + + device_for_each_child(parent, &cnts, mfd_remove_devices_fn); + kfree(cnts); } EXPORT_SYMBOL(mfd_remove_devices); +static int add_shared_platform_device(const char *cell, const char *name) +{ + struct mfd_cell cell_entry; + struct device *dev; + struct platform_device *pdev; + int err; + + /* check if we've already registered a device (don't fail if we have) */ + if (bus_find_device_by_name(&platform_bus_type, NULL, name)) + return 0; + + /* fetch the parent cell's device (should already be registered!) */ + dev = bus_find_device_by_name(&platform_bus_type, NULL, cell); + if (!dev) { + printk(KERN_ERR "failed to find device for cell %s\n", cell); + return -ENODEV; + } + pdev = to_platform_device(dev); + memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry)); + + WARN_ON(!cell_entry.enable); + + cell_entry.name = name; + err = mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0); + if (err) + dev_err(dev, "MFD add devices failed: %d\n", err); + return err; +} + +int mfd_shared_platform_driver_register(struct platform_driver *drv, + const char *cellname) +{ + int err; + + err = add_shared_platform_device(cellname, drv->driver.name); + if (err) + printk(KERN_ERR "failed to add platform device %s\n", + drv->driver.name); + + err = platform_driver_register(drv); + if (err) + printk(KERN_ERR "failed to add platform driver %s\n", + drv->driver.name); + + return err; +} +EXPORT_SYMBOL(mfd_shared_platform_driver_register); + +void mfd_shared_platform_driver_unregister(struct platform_driver *drv) +{ + struct device *dev; + + dev = bus_find_device_by_name(&platform_bus_type, NULL, + drv->driver.name); + if (dev) + platform_device_unregister(to_platform_device(dev)); + + platform_driver_unregister(drv); +} +EXPORT_SYMBOL(mfd_shared_platform_driver_unregister); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 501ce13b693e..c1306ed43e3c 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -21,6 +21,7 @@ #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/i2c.h> +#include <linux/pm.h> #include <linux/slab.h> #include <linux/mfd/pcf50633/core.h> @@ -230,27 +231,26 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, } } -#ifdef CONFIG_PM -static int pcf50633_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int pcf50633_suspend(struct device *dev) { - struct pcf50633 *pcf; - pcf = i2c_get_clientdata(client); + struct i2c_client *client = to_i2c_client(dev); + struct pcf50633 *pcf = i2c_get_clientdata(client); return pcf50633_irq_suspend(pcf); } -static int pcf50633_resume(struct i2c_client *client) +static int pcf50633_resume(struct device *dev) { - struct pcf50633 *pcf; - pcf = i2c_get_clientdata(client); + struct i2c_client *client = to_i2c_client(dev); + struct pcf50633 *pcf = i2c_get_clientdata(client); return pcf50633_irq_resume(pcf); } -#else -#define pcf50633_suspend NULL -#define pcf50633_resume NULL #endif +static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); + static int __devinit pcf50633_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -360,16 +360,16 @@ static struct i2c_device_id pcf50633_id_table[] = { {"pcf50633", 0x73}, {/* end of list */} }; +MODULE_DEVICE_TABLE(i2c, pcf50633_id_table); static struct i2c_driver pcf50633_driver = { .driver = { .name = "pcf50633", + .pm = &pcf50633_pm, }, .id_table = pcf50633_id_table, .probe = pcf50633_probe, .remove = __devexit_p(pcf50633_remove), - .suspend = pcf50633_suspend, - .resume = pcf50633_resume, }; static int __init pcf50633_init(void) diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 50922975bda3..193c940225b5 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -61,12 +61,12 @@ static struct mfd_cell rdc321x_sb_cells[] = { .name = "rdc321x-wdt", .resources = rdc321x_wdt_resource, .num_resources = ARRAY_SIZE(rdc321x_wdt_resource), - .driver_data = &rdc321x_wdt_pdata, + .mfd_data = &rdc321x_wdt_pdata, }, { .name = "rdc321x-gpio", .resources = rdc321x_gpio_resources, .num_resources = ARRAY_SIZE(rdc321x_gpio_resources), - .driver_data = &rdc321x_gpio_pdata, + .mfd_data = &rdc321x_gpio_pdata, }, }; diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c index 0a7df44a93c0..53a63024bf11 100644 --- a/drivers/mfd/sh_mobile_sdhi.c +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -146,9 +146,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) } memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc)); - priv->cell_mmc.driver_data = mmc_data; - priv->cell_mmc.platform_data = &priv->cell_mmc; - priv->cell_mmc.data_size = sizeof(priv->cell_mmc); + priv->cell_mmc.mfd_data = mmc_data; platform_set_drvdata(pdev, priv); diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 9caeb4ac6ea6..af57fc706a4c 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -170,7 +170,7 @@ static struct mfd_cell t7l66xb_cells[] = { .name = "tmio-mmc", .enable = t7l66xb_mmc_enable, .disable = t7l66xb_mmc_disable, - .driver_data = &t7166xb_mmc_data, + .mfd_data = &t7166xb_mmc_data, .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), .resources = t7l66xb_mmc_resources, }, @@ -383,16 +383,7 @@ static int t7l66xb_probe(struct platform_device *dev) t7l66xb_attach_irq(dev); - t7l66xb_cells[T7L66XB_CELL_NAND].driver_data = pdata->nand_data; - t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = - &t7l66xb_cells[T7L66XB_CELL_NAND]; - t7l66xb_cells[T7L66XB_CELL_NAND].data_size = - sizeof(t7l66xb_cells[T7L66XB_CELL_NAND]); - - t7l66xb_cells[T7L66XB_CELL_MMC].platform_data = - &t7l66xb_cells[T7L66XB_CELL_MMC]; - t7l66xb_cells[T7L66XB_CELL_MMC].data_size = - sizeof(t7l66xb_cells[T7L66XB_CELL_MMC]); + t7l66xb_cells[T7L66XB_CELL_NAND].mfd_data = pdata->nand_data; ret = mfd_add_devices(&dev->dev, dev->id, t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells), diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index 6315f63f017d..b006f7cee952 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -131,7 +131,7 @@ static struct mfd_cell tc6387xb_cells[] = { .name = "tmio-mmc", .enable = tc6387xb_mmc_enable, .disable = tc6387xb_mmc_disable, - .driver_data = &tc6387xb_mmc_data, + .mfd_data = &tc6387xb_mmc_data, .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources), .resources = tc6387xb_mmc_resources, }, @@ -190,11 +190,6 @@ static int __devinit tc6387xb_probe(struct platform_device *dev) printk(KERN_INFO "Toshiba tc6387xb initialised\n"); - tc6387xb_cells[TC6387XB_CELL_MMC].platform_data = - &tc6387xb_cells[TC6387XB_CELL_MMC]; - tc6387xb_cells[TC6387XB_CELL_MMC].data_size = - sizeof(tc6387xb_cells[TC6387XB_CELL_MMC]); - ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells, ARRAY_SIZE(tc6387xb_cells), iomem, irq); diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 9a238633a54d..3d62ded86a8f 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -393,7 +393,7 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = { .name = "tmio-mmc", .enable = tc6393xb_mmc_enable, .resume = tc6393xb_mmc_resume, - .driver_data = &tc6393xb_mmc_data, + .mfd_data = &tc6393xb_mmc_data, .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), .resources = tc6393xb_mmc_resources, }, @@ -693,27 +693,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) goto err_setup; } - tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; - tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = - &tc6393xb_cells[TC6393XB_CELL_NAND]; - tc6393xb_cells[TC6393XB_CELL_NAND].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); - - tc6393xb_cells[TC6393XB_CELL_MMC].platform_data = - &tc6393xb_cells[TC6393XB_CELL_MMC]; - tc6393xb_cells[TC6393XB_CELL_MMC].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_MMC]); - - tc6393xb_cells[TC6393XB_CELL_OHCI].platform_data = - &tc6393xb_cells[TC6393XB_CELL_OHCI]; - tc6393xb_cells[TC6393XB_CELL_OHCI].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_OHCI]); - - tc6393xb_cells[TC6393XB_CELL_FB].driver_data = tcpd->fb_data; - tc6393xb_cells[TC6393XB_CELL_FB].platform_data = - &tc6393xb_cells[TC6393XB_CELL_FB]; - tc6393xb_cells[TC6393XB_CELL_FB].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_FB]); + tc6393xb_cells[TC6393XB_CELL_NAND].mfd_data = tcpd->nand_data; + tc6393xb_cells[TC6393XB_CELL_FB].mfd_data = tcpd->fb_data; ret = mfd_add_devices(&dev->dev, dev->id, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 6ad8a7f8d390..94c6c8afad12 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -384,8 +384,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -396,43 +395,37 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, - .data_size = sizeof(timberdale_xiic_platform_data), + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, - .data_size = sizeof(timberdale_video_platform_data), + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, - .data_size = sizeof(timberdale_ks8842_platform_data) + .mfd_data = &timberdale_ks8842_platform_data, }, }; @@ -441,8 +434,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -458,15 +450,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, - .data_size = sizeof(timberdale_xiic_platform_data), + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-mlogicore", @@ -477,29 +467,25 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, - .data_size = sizeof(timberdale_video_platform_data), + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, - .data_size = sizeof(timberdale_ks8842_platform_data) + .mfd_data = &timberdale_ks8842_platform_data, }, }; @@ -508,8 +494,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -520,36 +505,31 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, - .data_size = sizeof(timberdale_xiic_platform_data), + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, - .data_size = sizeof(timberdale_video_platform_data), + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, }; @@ -558,8 +538,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -570,43 +549,37 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "ocores-i2c", .num_resources = ARRAY_SIZE(timberdale_ocores_resources), .resources = timberdale_ocores_resources, - .platform_data = &timberdale_ocores_platform_data, - .data_size = sizeof(timberdale_ocores_platform_data), + .mfd_data = &timberdale_ocores_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, - .data_size = sizeof(timberdale_video_platform_data), + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, - .data_size = sizeof(timberdale_ks8842_platform_data) + .mfd_data = &timberdale_ks8842_platform_data, }, }; diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c new file mode 100644 index 000000000000..46d8205646b6 --- /dev/null +++ b/drivers/mfd/tps6105x.c @@ -0,0 +1,246 @@ +/* + * Core driver for TPS61050/61052 boost converters, used for while LED + * driving, audio power amplification, white LED flash, and generic + * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in) + * and a flash synchronization pin to synchronize flash events when used as + * flashgun. + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps6105x.h> + +int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value) +{ + int ret; + + ret = mutex_lock_interruptible(&tps6105x->lock); + if (ret) + return ret; + ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value); + mutex_unlock(&tps6105x->lock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(tps6105x_set); + +int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf) +{ + int ret; + + ret = mutex_lock_interruptible(&tps6105x->lock); + if (ret) + return ret; + ret = i2c_smbus_read_byte_data(tps6105x->client, reg); + mutex_unlock(&tps6105x->lock); + if (ret < 0) + return ret; + + *buf = ret; + return 0; +} +EXPORT_SYMBOL(tps6105x_get); + +/* + * Masks off the bits in the mask and sets the bits in the bitvalues + * parameter in one atomic operation + */ +int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg, + u8 bitmask, u8 bitvalues) +{ + int ret; + u8 regval; + + ret = mutex_lock_interruptible(&tps6105x->lock); + if (ret) + return ret; + ret = i2c_smbus_read_byte_data(tps6105x->client, reg); + if (ret < 0) + goto fail; + regval = ret; + regval = (~bitmask & regval) | (bitmask & bitvalues); + ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval); +fail: + mutex_unlock(&tps6105x->lock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(tps6105x_mask_and_set); + +static int __devinit tps6105x_startup(struct tps6105x *tps6105x) +{ + int ret; + u8 regval; + + ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); + if (ret) + return ret; + switch (regval >> TPS6105X_REG0_MODE_SHIFT) { + case TPS6105X_REG0_MODE_SHUTDOWN: + dev_info(&tps6105x->client->dev, + "TPS6105x found in SHUTDOWN mode\n"); + break; + case TPS6105X_REG0_MODE_TORCH: + dev_info(&tps6105x->client->dev, + "TPS6105x found in TORCH mode\n"); + break; + case TPS6105X_REG0_MODE_TORCH_FLASH: + dev_info(&tps6105x->client->dev, + "TPS6105x found in FLASH mode\n"); + break; + case TPS6105X_REG0_MODE_VOLTAGE: + dev_info(&tps6105x->client->dev, + "TPS6105x found in VOLTAGE mode\n"); + break; + default: + break; + } + + return ret; +} + +/* + * MFD cells - we have one cell which is selected operation + * mode, and we always have a GPIO cell. + */ +static struct mfd_cell tps6105x_cells[] = { + { + /* name will be runtime assigned */ + .id = -1, + }, + { + .name = "tps6105x-gpio", + .id = -1, + }, +}; + +static int __devinit tps6105x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps6105x *tps6105x; + struct tps6105x_platform_data *pdata; + int ret; + int i; + + tps6105x = kmalloc(sizeof(*tps6105x), GFP_KERNEL); + if (!tps6105x) + return -ENOMEM; + + i2c_set_clientdata(client, tps6105x); + tps6105x->client = client; + pdata = client->dev.platform_data; + tps6105x->pdata = pdata; + mutex_init(&tps6105x->lock); + + ret = tps6105x_startup(tps6105x); + if (ret) { + dev_err(&client->dev, "chip initialization failed\n"); + goto fail; + } + + /* Remove warning texts when you implement new cell drivers */ + switch (pdata->mode) { + case TPS6105X_MODE_SHUTDOWN: + dev_info(&client->dev, + "present, not used for anything, only GPIO\n"); + break; + case TPS6105X_MODE_TORCH: + tps6105x_cells[0].name = "tps6105x-leds"; + dev_warn(&client->dev, + "torch mode is unsupported\n"); + break; + case TPS6105X_MODE_TORCH_FLASH: + tps6105x_cells[0].name = "tps6105x-flash"; + dev_warn(&client->dev, + "flash mode is unsupported\n"); + break; + case TPS6105X_MODE_VOLTAGE: + tps6105x_cells[0].name ="tps6105x-regulator"; + break; + default: + break; + } + + /* Set up and register the platform devices. */ + for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) { + /* One state holder for all drivers, this is simple */ + tps6105x_cells[i].mfd_data = tps6105x; + } + + ret = mfd_add_devices(&client->dev, 0, tps6105x_cells, + ARRAY_SIZE(tps6105x_cells), NULL, 0); + if (ret) + goto fail; + + return 0; + +fail: + kfree(tps6105x); + return ret; +} + +static int __devexit tps6105x_remove(struct i2c_client *client) +{ + struct tps6105x *tps6105x = i2c_get_clientdata(client); + + mfd_remove_devices(&client->dev); + + /* Put chip in shutdown mode */ + tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK, + TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); + + kfree(tps6105x); + return 0; +} + +static const struct i2c_device_id tps6105x_id[] = { + { "tps61050", 0 }, + { "tps61052", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps6105x_id); + +static struct i2c_driver tps6105x_driver = { + .driver = { + .name = "tps6105x", + }, + .probe = tps6105x_probe, + .remove = __devexit_p(tps6105x_remove), + .id_table = tps6105x_id, +}; + +static int __init tps6105x_init(void) +{ + return i2c_add_driver(&tps6105x_driver); +} +subsys_initcall(tps6105x_init); + +static void __exit tps6105x_exit(void) +{ + i2c_del_driver(&tps6105x_driver); +} +module_exit(tps6105x_exit); + +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index e9018d1394ee..0aa9186aec19 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -288,12 +288,10 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask); } -static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) +static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) { - int ret; - if (!gpio_base) - return; + return 0; tps6586x->gpio.owner = THIS_MODULE; tps6586x->gpio.label = tps6586x->client->name; @@ -307,9 +305,7 @@ static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) tps6586x->gpio.set = tps6586x_gpio_set; tps6586x->gpio.get = tps6586x_gpio_get; - ret = gpiochip_add(&tps6586x->gpio); - if (ret) - dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret); + return gpiochip_add(&tps6586x->gpio); } static int __remove_subdev(struct device *dev, void *unused) @@ -517,17 +513,28 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, } } + ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base); + if (ret) { + dev_err(&client->dev, "GPIO registration failed: %d\n", ret); + goto err_gpio_init; + } + ret = tps6586x_add_subdevs(tps6586x, pdata); if (ret) { dev_err(&client->dev, "add devices failed: %d\n", ret); goto err_add_devs; } - tps6586x_gpio_init(tps6586x, pdata->gpio_base); - return 0; err_add_devs: + if (pdata->gpio_base) { + ret = gpiochip_remove(&tps6586x->gpio); + if (ret) + dev_err(&client->dev, "Can't remove gpio chip: %d\n", + ret); + } +err_gpio_init: if (client->irq) free_irq(client->irq, tps6586x); err_irq_init: @@ -587,4 +594,3 @@ module_exit(tps6586x_exit); MODULE_DESCRIPTION("TPS6586X core driver"); MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); MODULE_LICENSE("GPL"); - diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index a35fa7dcbf53..960b5bed7f52 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -721,13 +721,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) } - if (twl_has_watchdog()) { + if (twl_has_watchdog() && twl_class_is_4030()) { child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } - if (twl_has_pwrbutton()) { + if (twl_has_pwrbutton() && twl_class_is_4030()) { child = add_child(1, "twl4030_pwrbutton", NULL, 0, true, pdata->irq_base + 8 + 0, 0); if (IS_ERR(child)) @@ -864,6 +864,10 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3); if (IS_ERR(child)) return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_bci() && pdata->bci && diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index 9a4b196d6deb..c02fded316c9 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -208,15 +208,13 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) if (pdata->audio) { cell = &codec->cells[childs]; cell->name = "twl4030-codec"; - cell->platform_data = pdata->audio; - cell->data_size = sizeof(*pdata->audio); + cell->mfd_data = pdata->audio; childs++; } if (pdata->vibra) { cell = &codec->cells[childs]; cell->name = "twl4030-vibra"; - cell->platform_data = pdata->vibra; - cell->data_size = sizeof(*pdata->vibra); + cell->mfd_data = pdata->vibra; childs++; } diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c new file mode 100644 index 000000000000..3941ddcf15fe --- /dev/null +++ b/drivers/mfd/twl4030-madc.c @@ -0,0 +1,802 @@ +/* + * + * TWL4030 MADC module driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen <mikko.k.ylinen@nokia.com> + * + * Amit Kucheria <amit.kucheria@canonical.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/i2c/twl.h> +#include <linux/i2c/twl4030-madc.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/err.h> + +/* + * struct twl4030_madc_data - a container for madc info + * @dev - pointer to device structure for madc + * @lock - mutex protecting this data structure + * @requests - Array of request struct corresponding to SW1, SW2 and RT + * @imr - Interrupt mask register of MADC + * @isr - Interrupt status register of MADC + */ +struct twl4030_madc_data { + struct device *dev; + struct mutex lock; /* mutex protecting this data structure */ + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; + int imr; + int isr; +}; + +static struct twl4030_madc_data *twl4030_madc; + +struct twl4030_prescale_divider_ratios { + s16 numerator; + s16 denominator; +}; + +static const struct twl4030_prescale_divider_ratios +twl4030_divider_ratios[16] = { + {1, 1}, /* CHANNEL 0 No Prescaler */ + {1, 1}, /* CHANNEL 1 No Prescaler */ + {6, 10}, /* CHANNEL 2 */ + {6, 10}, /* CHANNEL 3 */ + {6, 10}, /* CHANNEL 4 */ + {6, 10}, /* CHANNEL 5 */ + {6, 10}, /* CHANNEL 6 */ + {6, 10}, /* CHANNEL 7 */ + {3, 14}, /* CHANNEL 8 */ + {1, 3}, /* CHANNEL 9 */ + {1, 1}, /* CHANNEL 10 No Prescaler */ + {15, 100}, /* CHANNEL 11 */ + {1, 4}, /* CHANNEL 12 */ + {1, 1}, /* CHANNEL 13 Reserved channels */ + {1, 1}, /* CHANNEL 14 Reseved channels */ + {5, 11}, /* CHANNEL 15 */ +}; + + +/* + * Conversion table from -3 to 55 degree Celcius + */ +static int therm_tbl[] = { +30800, 29500, 28300, 27100, +26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, +17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, +11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, +8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, +5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, +4040, 3910, 3790, 3670, 3550 +}; + +/* + * Structure containing the registers + * of different conversion methods supported by MADC. + * Hardware or RT real time conversion request initiated by external host + * processor for RT Signal conversions. + * External host processors can also request for non RT conversions + * SW1 and SW2 software conversions also called asynchronous or GPC request. + */ +static +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { + [TWL4030_MADC_RT] = { + .sel = TWL4030_MADC_RTSELECT_LSB, + .avg = TWL4030_MADC_RTAVERAGE_LSB, + .rbase = TWL4030_MADC_RTCH0_LSB, + }, + [TWL4030_MADC_SW1] = { + .sel = TWL4030_MADC_SW1SELECT_LSB, + .avg = TWL4030_MADC_SW1AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW1, + }, + [TWL4030_MADC_SW2] = { + .sel = TWL4030_MADC_SW2SELECT_LSB, + .avg = TWL4030_MADC_SW2AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW2, + }, +}; + +/* + * Function to read a particular channel value. + * @madc - pointer to struct twl4030_madc_data + * @reg - lsb of ADC Channel + * If the i2c read fails it returns an error else returns 0. + */ +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) +{ + u8 msb, lsb; + int ret; + /* + * For each ADC channel, we have MSB and LSB register pair. MSB address + * is always LSB address+1. reg parameter is the address of LSB register + */ + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); + if (ret) { + dev_err(madc->dev, "unable to read MSB register 0x%X\n", + reg + 1); + return ret; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg); + if (ret) { + dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg); + return ret; + } + + return (int)(((msb << 8) | lsb) >> 6); +} + +/* + * Return battery temperature + * Or < 0 on failure. + */ +static int twl4030battery_temperature(int raw_volt) +{ + u8 val; + int temp, curr, volt, res, ret; + + volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; + /* Getting and calculating the supply current in micro ampers */ + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + REG_BCICTL2); + if (ret < 0) + return ret; + curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; + /* Getting and calculating the thermistor resistance in ohms */ + res = volt * 1000 / curr; + /* calculating temperature */ + for (temp = 58; temp >= 0; temp--) { + int actual = therm_tbl[temp]; + + if ((actual - res) >= 0) + break; + } + + return temp + 1; +} + +static int twl4030battery_current(int raw_volt) +{ + int ret; + u8 val; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + TWL4030_BCI_BCICTL1); + if (ret) + return ret; + if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; + else /* slope of 0.88 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; +} +/* + * Function to read channel values + * @madc - pointer to twl4030_madc_data struct + * @reg_base - Base address of the first channel + * @Channels - 16 bit bitmap. If the bit is set, channel value is read + * @buf - The channel values are stored here. if read fails error + * value is stored + * Returns the number of successfully read channels. + */ +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, + u8 reg_base, unsigned + long channels, int *buf) +{ + int count = 0, count_req = 0, i; + u8 reg; + + for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { + reg = reg_base + 2 * i; + buf[i] = twl4030_madc_channel_raw_read(madc, reg); + if (buf[i] < 0) { + dev_err(madc->dev, + "Unable to read register 0x%X\n", reg); + count_req++; + continue; + } + switch (i) { + case 10: + buf[i] = twl4030battery_current(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading current\n"); + count_req++; + } else { + count++; + buf[i] = buf[i] - 750; + } + break; + case 1: + buf[i] = twl4030battery_temperature(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading temperature\n"); + count_req++; + } else { + buf[i] -= 3; + count++; + } + break; + default: + count++; + /* Analog Input (V) = conv_result * step_size / R + * conv_result = decimal value of 10-bit conversion + * result + * step size = 1.5 / (2 ^ 10 -1) + * R = Prescaler ratio for input channels. + * Result given in mV hence multiplied by 1000. + */ + buf[i] = (buf[i] * 3 * 1000 * + twl4030_divider_ratios[i].denominator) + / (2 * 1023 * + twl4030_divider_ratios[i].numerator); + } + } + if (count_req) + dev_err(madc->dev, "%d channel conversion failed\n", count_req); + + return count; +} + +/* + * Enables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be enabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * If the i2c read fails it returns an error else returns 0. + */ +static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val &= ~(1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + + } + + return 0; +} + +/* + * Disables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be disabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * Returns error if i2c read/write fails. + */ +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val |= (1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + } + + return 0; +} + +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) +{ + struct twl4030_madc_data *madc = _madc; + const struct twl4030_madc_conversion_method *method; + u8 isr_val, imr_val; + int i, len, ret; + struct twl4030_madc_request *r; + + mutex_lock(&madc->lock); + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); + if (ret) { + dev_err(madc->dev, "unable to read isr register 0x%X\n", + madc->isr); + goto err_i2c; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + goto err_i2c; + } + isr_val &= ~imr_val; + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + if (!(isr_val & (1 << i))) + continue; + ret = twl4030_madc_disable_irq(madc, i); + if (ret < 0) + dev_dbg(madc->dev, "Disable interrupt failed%d\n", i); + madc->requests[i].result_pending = 1; + } + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + /* No pending results for this method, move to next one */ + if (!r->result_pending) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + len = twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf); + /* Return results to caller */ + if (r->func_cb != NULL) { + r->func_cb(len, r->channels, r->rbuf); + r->func_cb = NULL; + } + /* Free request */ + r->result_pending = 0; + r->active = 0; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; + +err_i2c: + /* + * In case of error check whichever request is active + * and service the same. + */ + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + if (r->active == 0) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + len = twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf); + /* Return results to caller */ + if (r->func_cb != NULL) { + r->func_cb(len, r->channels, r->rbuf); + r->func_cb = NULL; + } + /* Free request */ + r->result_pending = 0; + r->active = 0; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; +} + +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, + struct twl4030_madc_request *req) +{ + struct twl4030_madc_request *p; + int ret; + + p = &madc->requests[req->method]; + memcpy(p, req, sizeof(*req)); + ret = twl4030_madc_enable_irq(madc, req->method); + if (ret < 0) { + dev_err(madc->dev, "enable irq failed!!\n"); + return ret; + } + + return 0; +} + +/* + * Function which enables the madc conversion + * by writing to the control register. + * @madc - pointer to twl4030_madc_data struct + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 + * corresponding to RT SW1 or SW2 conversion methods. + * Returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, + int conv_method) +{ + const struct twl4030_madc_conversion_method *method; + int ret = 0; + method = &twl4030_conversion_methods[conv_method]; + switch (conv_method) { + case TWL4030_MADC_SW1: + case TWL4030_MADC_SW2: + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, + TWL4030_MADC_SW_START, method->ctrl); + if (ret) { + dev_err(madc->dev, + "unable to write ctrl register 0x%X\n", + method->ctrl); + return ret; + } + break; + default: + break; + } + + return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, + unsigned int timeout_ms, + u8 status_reg) +{ + unsigned long timeout; + int ret; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + do { + u8 reg; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); + if (ret) { + dev_err(madc->dev, + "unable to read status register 0x%X\n", + status_reg); + return ret; + } + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) + return 0; + usleep_range(500, 2000); + } while (!time_after(jiffies, timeout)); + dev_err(madc->dev, "conversion timeout!\n"); + + return -EAGAIN; +} + +/* + * An exported function which can be called from other kernel drivers. + * @req twl4030_madc_request structure + * req->rbuf will be filled with read values of channels based on the + * channel index. If a particular channel reading fails there will + * be a negative error value in the corresponding array element. + * returns 0 if succeeds else error value + */ +int twl4030_madc_conversion(struct twl4030_madc_request *req) +{ + const struct twl4030_madc_conversion_method *method; + u8 ch_msb, ch_lsb; + int ret; + + if (!req) + return -EINVAL; + mutex_lock(&twl4030_madc->lock); + if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { + ret = -EINVAL; + goto out; + } + /* Do we have a conversion request ongoing */ + if (twl4030_madc->requests[req->method].active) { + ret = -EBUSY; + goto out; + } + ch_msb = (req->channels >> 8) & 0xff; + ch_lsb = req->channels & 0xff; + method = &twl4030_conversion_methods[req->method]; + /* Select channels to be converted */ + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel + 1); + return ret; + } + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel + 1); + return ret; + } + /* Select averaging for all channels if do_avg is set */ + if (req->do_avg) { + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, + ch_msb, method->avg + 1); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write avg register 0x%X\n", + method->avg + 1); + return ret; + } + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, + ch_lsb, method->avg); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel reg 0x%X\n", + method->sel + 1); + return ret; + } + } + if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { + ret = twl4030_madc_set_irq(twl4030_madc, req); + if (ret < 0) + goto out; + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = 1; + ret = 0; + goto out; + } + /* With RT method we should not be here anymore */ + if (req->method == TWL4030_MADC_RT) { + ret = -EINVAL; + goto out; + } + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = 1; + /* Wait until conversion is ready (ctrl register returns EOC) */ + ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); + if (ret) { + twl4030_madc->requests[req->method].active = 0; + goto out; + } + ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, + req->channels, req->rbuf); + twl4030_madc->requests[req->method].active = 0; + +out: + mutex_unlock(&twl4030_madc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(twl4030_madc_conversion); + +/* + * Return channel value + * Or < 0 on failure. + */ +int twl4030_get_madc_conversion(int channel_no) +{ + struct twl4030_madc_request req; + int temp = 0; + int ret; + + req.channels = (1 << channel_no); + req.method = TWL4030_MADC_SW2; + req.active = 0; + req.func_cb = NULL; + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + if (req.rbuf[channel_no] > 0) + temp = req.rbuf[channel_no]; + + return temp; +} +EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); + +/* + * Function to enable or disable bias current for + * main battery type reading or temperature sensing + * @madc - pointer to twl4030_madc_data struct + * @chan - can be one of the two values + * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading + * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature + * sensing + * @on - enable or disable chan. + */ +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, + int chan, int on) +{ + int ret; + u8 regval; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", + TWL4030_BCI_BCICTL1); + return ret; + } + if (on) + regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; + else + regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", + TWL4030_BCI_BCICTL1); + return ret; + } + + return 0; +} + +/* + * Function that sets MADC software power on bit to enable MADC + * @madc - pointer to twl4030_madc_data struct + * @on - Enable or disable MADC software powen on bit. + * returns error if i2c read/write fails else 0 + */ +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) +{ + u8 regval; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ®val, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + if (on) + regval |= TWL4030_MADC_MADCON; + else + regval &= ~TWL4030_MADC_MADCON; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + + return 0; +} + +/* + * Initialize MADC and request for threaded irq + */ +static int __devinit twl4030_madc_probe(struct platform_device *pdev) +{ + struct twl4030_madc_data *madc; + struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data; + int ret; + u8 regval; + + if (!pdata) { + dev_err(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + madc = kzalloc(sizeof(*madc), GFP_KERNEL); + if (!madc) + return -ENOMEM; + + /* + * Phoenix provides 2 interrupt lines. The first one is connected to + * the OMAP. The other one can be connected to the other processor such + * as modem. Hence two separate ISR and IMR registers. + */ + madc->imr = (pdata->irq_line == 1) ? + TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; + madc->isr = (pdata->irq_line == 1) ? + TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; + ret = twl4030_madc_set_power(madc, 1); + if (ret < 0) + goto err_power; + ret = twl4030_madc_set_current_generator(madc, 0, 1); + if (ret < 0) + goto err_current_generator; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + regval |= TWL4030_BCI_MESBAT; + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + platform_set_drvdata(pdev, madc); + mutex_init(&madc->lock); + ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, + twl4030_madc_threaded_irq_handler, + IRQF_TRIGGER_RISING, "twl4030_madc", madc); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq\n"); + goto err_irq; + } + twl4030_madc = madc; + return 0; +err_irq: + platform_set_drvdata(pdev, NULL); +err_i2c: + twl4030_madc_set_current_generator(madc, 0, 0); +err_current_generator: + twl4030_madc_set_power(madc, 0); +err_power: + kfree(madc); + + return ret; +} + +static int __devexit twl4030_madc_remove(struct platform_device *pdev) +{ + struct twl4030_madc_data *madc = platform_get_drvdata(pdev); + + free_irq(platform_get_irq(pdev, 0), madc); + platform_set_drvdata(pdev, NULL); + twl4030_madc_set_current_generator(madc, 0, 0); + twl4030_madc_set_power(madc, 0); + kfree(madc); + + return 0; +} + +static struct platform_driver twl4030_madc_driver = { + .probe = twl4030_madc_probe, + .remove = __exit_p(twl4030_madc_remove), + .driver = { + .name = "twl4030_madc", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_madc_init(void) +{ + return platform_driver_register(&twl4030_madc_driver); +} + +module_init(twl4030_madc_init); + +static void __exit twl4030_madc_exit(void) +{ + platform_driver_unregister(&twl4030_madc_driver); +} + +module_exit(twl4030_madc_exit); + +MODULE_DESCRIPTION("TWL4030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 92b85e28a15e..38ffbd50a0d2 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -60,6 +60,7 @@ static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x input_report_abs(idev, ABS_X, x); input_report_abs(idev, ABS_Y, y); input_report_abs(idev, ABS_PRESSURE, pressure); + input_report_key(idev, BTN_TOUCH, 1); input_sync(idev); } @@ -68,6 +69,7 @@ static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) struct input_dev *idev = ts->idev; input_report_abs(idev, ABS_PRESSURE, 0); + input_report_key(idev, BTN_TOUCH, 0); input_sync(idev); } @@ -384,7 +386,8 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) idev->open = ucb1x00_ts_open; idev->close = ucb1x00_ts_close; - __set_bit(EV_ABS, idev->evbit); + idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_drvdata(idev, ts); diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c index 348052aa5dbf..d698703dbd46 100644 --- a/drivers/mfd/vx855.c +++ b/drivers/mfd/vx855.c @@ -122,6 +122,7 @@ static struct pci_device_id vx855_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) }, { 0, } }; +MODULE_DEVICE_TABLE(pci, vx855_pci_tbl); static struct pci_driver vx855_pci_driver = { .name = "vx855", diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c index d2ecc2435736..529d65ba5353 100644 --- a/drivers/mfd/wl1273-core.c +++ b/drivers/mfd/wl1273-core.c @@ -38,7 +38,6 @@ static int wl1273_core_remove(struct i2c_client *client) dev_dbg(&client->dev, "%s\n", __func__); mfd_remove_devices(&client->dev); - i2c_set_clientdata(client, NULL); kfree(core); return 0; @@ -79,8 +78,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, cell = &core->cells[children]; cell->name = "wl1273_fm_radio"; - cell->platform_data = &core; - cell->data_size = sizeof(core); + cell->mfd_data = &core; children++; if (pdata->children & WL1273_CODEC_CHILD) { @@ -88,8 +86,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, dev_dbg(&client->dev, "%s: Have codec.\n", __func__); cell->name = "wl1273-codec"; - cell->platform_data = &core; - cell->data_size = sizeof(core); + cell->mfd_data = &core; children++; } @@ -104,7 +101,6 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, return 0; err: - i2c_set_clientdata(client, NULL); pdata->free_resources(); kfree(core); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 3853fa8e7cc2..a06cbc739716 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -51,17 +51,25 @@ static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, int bytes, void *src) { struct i2c_client *i2c = wm831x->control_data; - unsigned char msg[bytes + 2]; + struct i2c_msg xfer[2]; int ret; reg = cpu_to_be16(reg); - memcpy(&msg[0], ®, 2); - memcpy(&msg[2], src, bytes); - ret = i2c_master_send(i2c, msg, bytes + 2); + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = (char *)® + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_NOSTART; + xfer[1].len = bytes; + xfer[1].buf = (char *)src; + + ret = i2c_transfer(i2c->adapter, xfer, 2); if (ret < 0) return ret; - if (ret < bytes + 2) + if (ret != 2) return -EIO; return 0; diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index f7192d438aab..a5cd17e18d09 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -26,15 +26,6 @@ #include <linux/delay.h> -/* - * Since generic IRQs don't currently support interrupt controllers on - * interrupt driven buses we don't use genirq but instead provide an - * interface that looks very much like the standard ones. This leads - * to some bodges, including storing interrupt handler information in - * the static irq_data table we use to look up the data for individual - * interrupts, but hopefully won't last too long. - */ - struct wm831x_irq_data { int primary; int reg; @@ -361,6 +352,10 @@ static void wm831x_irq_sync_unlock(struct irq_data *data) /* If there's been a change in the mask write it back * to the hardware. */ if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) { + dev_dbg(wm831x->dev, "IRQ mask sync: %x = %x\n", + WM831X_INTERRUPT_STATUS_1_MASK + i, + wm831x->irq_masks_cur[i]); + wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i]; wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, @@ -371,7 +366,7 @@ static void wm831x_irq_sync_unlock(struct irq_data *data) mutex_unlock(&wm831x->irq_lock); } -static void wm831x_irq_unmask(struct irq_data *data) +static void wm831x_irq_enable(struct irq_data *data) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, @@ -380,7 +375,7 @@ static void wm831x_irq_unmask(struct irq_data *data) wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } -static void wm831x_irq_mask(struct irq_data *data) +static void wm831x_irq_disable(struct irq_data *data) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, @@ -426,8 +421,8 @@ static struct irq_chip wm831x_irq_chip = { .name = "wm831x", .irq_bus_lock = wm831x_irq_lock, .irq_bus_sync_unlock = wm831x_irq_sync_unlock, - .irq_mask = wm831x_irq_mask, - .irq_unmask = wm831x_irq_unmask, + .irq_disable = wm831x_irq_disable, + .irq_enable = wm831x_irq_enable, .irq_set_type = wm831x_irq_set_type, }; @@ -449,6 +444,18 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) goto out; } + /* The touch interrupts are visible in the primary register as + * an optimisation; open code this to avoid complicating the + * main handling loop and so we can also skip iterating the + * descriptors. + */ + if (primary & WM831X_TCHPD_INT) + handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD); + if (primary & WM831X_TCHDATA_INT) + handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA); + if (primary & (WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT)) + goto out; + for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) { int offset = wm831x_irqs[i].reg - 1; @@ -481,6 +488,9 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) } out: + /* Touchscreen interrupts are handled specially in the driver */ + status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT); + for (i = 0; i < ARRAY_SIZE(status_regs); i++) { if (status_regs[i]) wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i, @@ -517,6 +527,14 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) return 0; } + if (pdata->irq_cmos) + i = 0; + else + i = WM831X_IRQ_OD; + + wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, + WM831X_IRQ_OD, i); + /* Try to flag /IRQ as a wake source; there are a number of * unconditional wake sources in the PMIC so this isn't * conditional but we don't actually care *too* much if it diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 0a8f772be88c..eed8e4f7a5a1 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/pm.h> #include <linux/spi/spi.h> #include <linux/mfd/wm831x/core.h> @@ -113,22 +114,27 @@ static int __devexit wm831x_spi_remove(struct spi_device *spi) return 0; } -static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m) +static int wm831x_spi_suspend(struct device *dev) { - struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + struct wm831x *wm831x = dev_get_drvdata(dev); return wm831x_device_suspend(wm831x); } +static const struct dev_pm_ops wm831x_spi_pm = { + .freeze = wm831x_spi_suspend, + .suspend = wm831x_spi_suspend, +}; + static struct spi_driver wm8310_spi_driver = { .driver = { .name = "wm8310", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8311_spi_driver = { @@ -136,10 +142,10 @@ static struct spi_driver wm8311_spi_driver = { .name = "wm8311", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8312_spi_driver = { @@ -147,10 +153,10 @@ static struct spi_driver wm8312_spi_driver = { .name = "wm8312", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8320_spi_driver = { @@ -158,10 +164,10 @@ static struct spi_driver wm8320_spi_driver = { .name = "wm8320", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8321_spi_driver = { @@ -169,10 +175,10 @@ static struct spi_driver wm8321_spi_driver = { .name = "wm8321", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8325_spi_driver = { @@ -180,10 +186,10 @@ static struct spi_driver wm8325_spi_driver = { .name = "wm8325", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8326_spi_driver = { @@ -191,10 +197,10 @@ static struct spi_driver wm8326_spi_driver = { .name = "wm8326", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static int __init wm831x_spi_init(void) diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 1bfef4846b07..3a6e78cb0384 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -245,7 +245,7 @@ static int wm8400_register_codec(struct wm8400 *wm8400) { struct mfd_cell cell = { .name = "wm8400-codec", - .driver_data = wm8400, + .mfd_data = wm8400, }; return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0); diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index f4016a075fd6..e198d40292e7 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -40,10 +40,8 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg, return ret; for (i = 0; i < bytes / 2; i++) { - buf[i] = be16_to_cpu(buf[i]); - dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n", - buf[i], reg + i, reg + i); + be16_to_cpu(buf[i]), reg + i, reg + i); } return 0; @@ -69,7 +67,7 @@ int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg) if (ret < 0) return ret; else - return val; + return be16_to_cpu(val); } EXPORT_SYMBOL_GPL(wm8994_reg_read); @@ -79,7 +77,7 @@ EXPORT_SYMBOL_GPL(wm8994_reg_read); * @wm8994: Device to read from * @reg: First register * @count: Number of registers - * @buf: Buffer to fill. + * @buf: Buffer to fill. The data will be returned big endian. */ int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, int count, u16 *buf) @@ -97,9 +95,9 @@ int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, EXPORT_SYMBOL_GPL(wm8994_bulk_read); static int wm8994_write(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *src) + int bytes, const void *src) { - u16 *buf = src; + const u16 *buf = src; int i; BUG_ON(bytes % 2); @@ -107,9 +105,7 @@ static int wm8994_write(struct wm8994 *wm8994, unsigned short reg, for (i = 0; i < bytes / 2; i++) { dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n", - buf[i], reg + i, reg + i); - - buf[i] = cpu_to_be16(buf[i]); + be16_to_cpu(buf[i]), reg + i, reg + i); } return wm8994->write_dev(wm8994, reg, bytes, src); @@ -127,6 +123,8 @@ int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg, { int ret; + val = cpu_to_be16(val); + mutex_lock(&wm8994->io_lock); ret = wm8994_write(wm8994, reg, 2, &val); @@ -138,6 +136,29 @@ int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg, EXPORT_SYMBOL_GPL(wm8994_reg_write); /** + * wm8994_bulk_write: Write multiple WM8994 registers + * + * @wm8994: Device to write to + * @reg: First register + * @count: Number of registers + * @buf: Buffer to write from. Data must be big-endian formatted. + */ +int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg, + int count, const u16 *buf) +{ + int ret; + + mutex_lock(&wm8994->io_lock); + + ret = wm8994_write(wm8994, reg, count * 2, buf); + + mutex_unlock(&wm8994->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8994_bulk_write); + +/** * wm8994_set_bits: Set the value of a bitfield in a WM8994 register * * @wm8994: Device to write to. @@ -157,9 +178,13 @@ int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg, if (ret < 0) goto out; + r = be16_to_cpu(r); + r &= ~mask; r |= val; + r = cpu_to_be16(r); + ret = wm8994_write(wm8994, reg, 2, &r); out: @@ -271,6 +296,11 @@ static int wm8994_suspend(struct device *dev) if (ret < 0) dev_err(dev, "Failed to save LDO registers: %d\n", ret); + /* Explicitly put the device into reset in case regulators + * don't get disabled in order to ensure consistent restart. + */ + wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET, 0x8994); + wm8994->suspended = true; ret = regulator_bulk_disable(wm8994->num_supplies, @@ -552,25 +582,29 @@ static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg, return 0; } -/* Currently we allocate the write buffer on the stack; this is OK for - * small writes - if we need to do large writes this will need to be - * revised. - */ static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *src) + int bytes, const void *src) { struct i2c_client *i2c = wm8994->control_data; - unsigned char msg[bytes + 2]; + struct i2c_msg xfer[2]; int ret; reg = cpu_to_be16(reg); - memcpy(&msg[0], ®, 2); - memcpy(&msg[2], src, bytes); - ret = i2c_master_send(i2c, msg, bytes + 2); + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = (char *)® + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_NOSTART; + xfer[1].len = bytes; + xfer[1].buf = (char *)src; + + ret = i2c_transfer(i2c->adapter, xfer, 2); if (ret < 0) return ret; - if (ret < bytes + 2) + if (ret != 2) return -EIO; return 0; @@ -612,7 +646,8 @@ static const struct i2c_device_id wm8994_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id); -UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume, NULL); +static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume, + NULL); static struct i2c_driver wm8994_i2c_driver = { .driver = { diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index 29e8faf9c01c..1e3bf4a2ff8e 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -182,7 +182,7 @@ static void wm8994_irq_sync_unlock(struct irq_data *data) mutex_unlock(&wm8994->irq_lock); } -static void wm8994_irq_unmask(struct irq_data *data) +static void wm8994_irq_enable(struct irq_data *data) { struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, @@ -191,7 +191,7 @@ static void wm8994_irq_unmask(struct irq_data *data) wm8994->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } -static void wm8994_irq_mask(struct irq_data *data) +static void wm8994_irq_disable(struct irq_data *data) { struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, @@ -204,8 +204,8 @@ static struct irq_chip wm8994_irq_chip = { .name = "wm8994", .irq_bus_lock = wm8994_irq_lock, .irq_bus_sync_unlock = wm8994_irq_sync_unlock, - .irq_mask = wm8994_irq_mask, - .irq_unmask = wm8994_irq_unmask, + .irq_disable = wm8994_irq_disable, + .irq_enable = wm8994_irq_enable, }; /* The processing of the primary interrupt occurs in a thread so that @@ -225,9 +225,11 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data) return IRQ_NONE; } - /* Apply masking */ - for (i = 0; i < WM8994_NUM_IRQ_REGS; i++) + /* Bit swap and apply masking */ + for (i = 0; i < WM8994_NUM_IRQ_REGS; i++) { + status[i] = be16_to_cpu(status[i]); status[i] &= ~wm8994->irq_masks_cur[i]; + } /* Report */ for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) { diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index ac52eb65395e..ab1adeabdd22 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -303,8 +303,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) { - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); /* * Testing on sh-mobile showed that SDIO IRQs are unmasked when @@ -327,8 +326,7 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) static void tmio_mmc_clk_start(struct tmio_mmc_host *host) { - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); @@ -669,8 +667,7 @@ out: static irqreturn_t tmio_mmc_irq(int irq, void *devid) { struct tmio_mmc_host *host = devid; - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); unsigned int ireg, irq_mask, status; unsigned int sdio_ireg, sdio_irq_mask, sdio_status; @@ -799,8 +796,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_rx; - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; @@ -869,8 +865,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; @@ -1063,8 +1058,7 @@ static void tmio_mmc_release_dma(struct tmio_mmc_host *host) static int tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data) { - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n", data->blksz, data->blocks); @@ -1169,8 +1163,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int tmio_mmc_get_ro(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); return ((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) || (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)) ? 0 : 1; @@ -1179,8 +1172,7 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) static int tmio_mmc_get_cd(struct mmc_host *mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); - struct mfd_cell *cell = host->pdev->dev.platform_data; - struct tmio_mmc_data *pdata = cell->driver_data; + struct tmio_mmc_data *pdata = mfd_get_data(host->pdev); if (!pdata->get_cd) return -ENOSYS; @@ -1199,7 +1191,7 @@ static const struct mmc_host_ops tmio_mmc_ops = { #ifdef CONFIG_PM static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct mmc_host *mmc = platform_get_drvdata(dev); int ret; @@ -1214,7 +1206,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) static int tmio_mmc_resume(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct mmc_host *mmc = platform_get_drvdata(dev); int ret = 0; @@ -1237,7 +1229,7 @@ out: static int __devinit tmio_mmc_probe(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct tmio_mmc_data *pdata; struct resource *res_ctl; struct tmio_mmc_host *host; @@ -1252,7 +1244,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) if (!res_ctl) goto out; - pdata = cell->driver_data; + pdata = mfd_get_data(dev); if (!pdata || !pdata->hclk) goto out; @@ -1352,7 +1344,7 @@ out: static int __devexit tmio_mmc_remove(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct mmc_host *mmc = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 3041d1f7ae3f..38fb16771f85 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -319,7 +319,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); int ret; if (cell->enable) { @@ -363,7 +363,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); if (cell->disable) @@ -372,8 +372,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); - struct tmio_nand_data *data = cell->driver_data; + struct tmio_nand_data *data = mfd_get_data(dev); struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *ccr = platform_get_resource(dev, @@ -516,7 +515,7 @@ static int tmio_remove(struct platform_device *dev) #ifdef CONFIG_PM static int tmio_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); if (cell->suspend) cell->suspend(dev); @@ -527,7 +526,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state) static int tmio_resume(struct platform_device *dev) { - struct mfd_cell *cell = dev_get_platdata(&dev->dev); + const struct mfd_cell *cell = mfd_get_cell(dev); /* FIXME - is this required or merely another attack of the broken * SHARP platform? Looks suspicious. diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 366f5cc050ae..102b16c6cc97 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/netdevice.h> #include <linux/can.h> @@ -1643,7 +1644,7 @@ static int __devinit ican3_probe(struct platform_device *pdev) struct device *dev; int ret; - pdata = pdev->dev.platform_data; + pdata = mfd_get_data(pdev); if (!pdata) return -ENXIO; diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c index 928b2b83cef5..efd44afeae83 100644 --- a/drivers/net/ks8842.c +++ b/drivers/net/ks8842.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -1145,7 +1146,7 @@ static int __devinit ks8842_probe(struct platform_device *pdev) struct resource *iomem; struct net_device *netdev; struct ks8842_adapter *adapter; - struct ks8842_platform_data *pdata = pdev->dev.platform_data; + struct ks8842_platform_data *pdata = mfd_get_data(pdev); u16 id; unsigned i; diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 02414db6a94c..763f894ed188 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -39,7 +39,7 @@ struct jz_battery { int irq; int charge_irq; - struct mfd_cell *cell; + const struct mfd_cell *cell; int status; long voltage; @@ -258,7 +258,7 @@ static int __devinit jz_battery_probe(struct platform_device *pdev) return -ENOMEM; } - jz_battery->cell = pdev->dev.platform_data; + jz_battery->cell = mfd_get_cell(pdev); jz_battery->irq = platform_get_irq(pdev, 0); if (jz_battery->irq < 0) { diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index dd6308499bd4..859251250b55 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -15,6 +15,7 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> struct pm8607_regulator_info { @@ -394,47 +395,48 @@ static struct pm8607_regulator_info pm8607_regulator_info[] = { PM8607_LDO(14, LDO14, 0, 4, SUPPLIES_EN12, 6), }; -static inline struct pm8607_regulator_info *find_regulator_info(int id) +static int __devinit pm8607_regulator_probe(struct platform_device *pdev) { - struct pm8607_regulator_info *info; + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm8607_regulator_info *info = NULL; + struct regulator_init_data *pdata; + struct mfd_cell *cell; int i; + cell = pdev->dev.platform_data; + if (cell == NULL) + return -ENODEV; + pdata = cell->mfd_data; + if (pdata == NULL) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) { info = &pm8607_regulator_info[i]; - if (info->desc.id == id) - return info; + if (!strcmp(info->desc.name, pdata->constraints.name)) + break; } - return NULL; -} - -static int __devinit pm8607_regulator_probe(struct platform_device *pdev) -{ - struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct pm860x_platform_data *pdata = chip->dev->platform_data; - struct pm8607_regulator_info *info = NULL; - - info = find_regulator_info(pdev->id); - if (info == NULL) { - dev_err(&pdev->dev, "invalid regulator ID specified\n"); + if (i > ARRAY_SIZE(pm8607_regulator_info)) { + dev_err(&pdev->dev, "Failed to find regulator %s\n", + pdata->constraints.name); return -EINVAL; } info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; info->chip = chip; + /* check DVC ramp slope double */ + if (!strcmp(info->desc.name, "BUCK3")) + if (info->chip->buck3_double) + info->slope_double = 1; + info->regulator = regulator_register(&info->desc, &pdev->dev, - pdata->regulator[pdev->id], info); + pdata, info); if (IS_ERR(info->regulator)) { dev_err(&pdev->dev, "failed to register regulator %s\n", info->desc.name); return PTR_ERR(info->regulator); } - /* check DVC ramp slope double */ - if (info->desc.id == PM8607_ID_BUCK3) - if (info->chip->buck3_double) - info->slope_double = 1; - platform_set_drvdata(pdev, info); return 0; } diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e1d943619ab8..de75f67f4cc3 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -108,6 +108,15 @@ config REGULATOR_MAX8952 via I2C bus. Maxim 8952 has one voltage output and supports 4 DVS modes ranging from 0.77V to 1.40V by 0.01V steps. +config REGULATOR_MAX8997 + tristate "Maxim 8997/8966 regulator" + depends on MFD_MAX8997 + help + This driver controls a Maxim 8997/8966 regulator + via I2C bus. The provided regulator is suitable for S5PC110, + S5PV210, and Exynos-4 chips to control VCC_CORE and + VCC_USIM voltages. + config REGULATOR_MAX8998 tristate "Maxim 8998 voltage regulator" depends on MFD_MAX8998 @@ -214,6 +223,15 @@ config REGULATOR_AB3100 AB3100 analog baseband dealing with power regulators for the system. +config REGULATOR_TPS6105X + tristate "TI TPS6105X Power regulators" + depends on TPS6105X + default y if TPS6105X + help + This driver supports TPS61050/TPS61052 voltage regulator chips. + It is a single boost converter primarily for white LEDs and + audio amplifiers. + config REGULATOR_TPS65023 tristate "TI TPS65023 Power regulators" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 0b5e88c2b8d7..d72a42756778 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o +obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o @@ -33,7 +34,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o - +obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index ed6feaf9398d..2dec589a8908 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/mfd/abx500.h> +#include <linux/mfd/core.h> /* LDO registers and some handy masking definitions for AB3100 */ #define AB3100_LDO_A 0x40 @@ -576,7 +577,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { - struct ab3100_platform_data *plfdata = pdev->dev.platform_data; + struct ab3100_platform_data *plfdata = mfd_get_data(pdev); int err = 0; u8 data; int i; diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c new file mode 100644 index 000000000000..01ef7e9903bb --- /dev/null +++ b/drivers/regulator/max8997.c @@ -0,0 +1,1213 @@ +/* + * max8997.c - Regulator driver for the Maxim 8997/8966 + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@smasung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8998.c + */ + +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/max8997.h> +#include <linux/mfd/max8997-private.h> + +struct max8997_data { + struct device *dev; + struct max8997_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + int ramp_delay; /* in mV/us */ + + u8 buck1_vol[8]; + u8 buck2_vol[8]; + u8 buck5_vol[8]; + int buck125_gpioindex; + + u8 saved_states[MAX8997_REG_MAX]; +}; + +static inline void max8997_set_gpio(struct max8997_data *max8997) +{ + struct max8997_platform_data *pdata = + dev_get_platdata(max8997->iodev->dev); + int set3 = (max8997->buck125_gpioindex) & 0x1; + int set2 = ((max8997->buck125_gpioindex) >> 1) & 0x1; + int set1 = ((max8997->buck125_gpioindex) >> 2) & 0x1; + + gpio_set_value(pdata->buck125_gpios[0], set1); + gpio_set_value(pdata->buck125_gpios[1], set2); + gpio_set_value(pdata->buck125_gpios[2], set3); +} + +struct voltage_map_desc { + int min; + int max; + int step; + unsigned int n_bits; +}; + +/* Voltage maps in mV */ +static const struct voltage_map_desc ldo_voltage_map_desc = { + .min = 800, .max = 3950, .step = 50, .n_bits = 6, +}; /* LDO1 ~ 18, 21 all */ + +static const struct voltage_map_desc buck1245_voltage_map_desc = { + .min = 650, .max = 2225, .step = 25, .n_bits = 6, +}; /* Buck1, 2, 4, 5 */ + +static const struct voltage_map_desc buck37_voltage_map_desc = { + .min = 750, .max = 3900, .step = 50, .n_bits = 6, +}; /* Buck3, 7 */ + +/* current map in mA */ +static const struct voltage_map_desc charger_current_map_desc = { + .min = 200, .max = 950, .step = 50, .n_bits = 4, +}; + +static const struct voltage_map_desc topoff_current_map_desc = { + .min = 50, .max = 200, .step = 10, .n_bits = 4, +}; + +static const struct voltage_map_desc *reg_voltage_map[] = { + [MAX8997_LDO1] = &ldo_voltage_map_desc, + [MAX8997_LDO2] = &ldo_voltage_map_desc, + [MAX8997_LDO3] = &ldo_voltage_map_desc, + [MAX8997_LDO4] = &ldo_voltage_map_desc, + [MAX8997_LDO5] = &ldo_voltage_map_desc, + [MAX8997_LDO6] = &ldo_voltage_map_desc, + [MAX8997_LDO7] = &ldo_voltage_map_desc, + [MAX8997_LDO8] = &ldo_voltage_map_desc, + [MAX8997_LDO9] = &ldo_voltage_map_desc, + [MAX8997_LDO10] = &ldo_voltage_map_desc, + [MAX8997_LDO11] = &ldo_voltage_map_desc, + [MAX8997_LDO12] = &ldo_voltage_map_desc, + [MAX8997_LDO13] = &ldo_voltage_map_desc, + [MAX8997_LDO14] = &ldo_voltage_map_desc, + [MAX8997_LDO15] = &ldo_voltage_map_desc, + [MAX8997_LDO16] = &ldo_voltage_map_desc, + [MAX8997_LDO17] = &ldo_voltage_map_desc, + [MAX8997_LDO18] = &ldo_voltage_map_desc, + [MAX8997_LDO21] = &ldo_voltage_map_desc, + [MAX8997_BUCK1] = &buck1245_voltage_map_desc, + [MAX8997_BUCK2] = &buck1245_voltage_map_desc, + [MAX8997_BUCK3] = &buck37_voltage_map_desc, + [MAX8997_BUCK4] = &buck1245_voltage_map_desc, + [MAX8997_BUCK5] = &buck1245_voltage_map_desc, + [MAX8997_BUCK6] = NULL, + [MAX8997_BUCK7] = &buck37_voltage_map_desc, + [MAX8997_EN32KHZ_AP] = NULL, + [MAX8997_EN32KHZ_CP] = NULL, + [MAX8997_ENVICHG] = NULL, + [MAX8997_ESAFEOUT1] = NULL, + [MAX8997_ESAFEOUT2] = NULL, + [MAX8997_CHARGER_CV] = NULL, + [MAX8997_CHARGER] = &charger_current_map_desc, + [MAX8997_CHARGER_TOPOFF] = &topoff_current_map_desc, +}; + +static inline int max8997_get_rid(struct regulator_dev *rdev) +{ + return rdev_get_id(rdev); +} + +static int max8997_list_voltage_safeout(struct regulator_dev *rdev, + unsigned int selector) +{ + int rid = max8997_get_rid(rdev); + + if (rid == MAX8997_ESAFEOUT1 || rid == MAX8997_ESAFEOUT2) { + switch (selector) { + case 0: + return 4850000; + case 1: + return 4900000; + case 2: + return 4950000; + case 3: + return 3300000; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int max8997_list_voltage_charger_cv(struct regulator_dev *rdev, + unsigned int selector) +{ + int rid = max8997_get_rid(rdev); + + if (rid != MAX8997_CHARGER_CV) + goto err; + + switch (selector) { + case 0x00: + return 4200000; + case 0x01 ... 0x0E: + return 4000000 + 20000 * (selector - 0x01); + case 0x0F: + return 4350000; + default: + return -EINVAL; + } +err: + return -EINVAL; +} + +static int max8997_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct voltage_map_desc *desc; + int rid = max8997_get_rid(rdev); + int val; + + if (rid >= ARRAY_SIZE(reg_voltage_map) || + rid < 0) + return -EINVAL; + + desc = reg_voltage_map[rid]; + if (desc == NULL) + return -EINVAL; + + val = desc->min + desc->step * selector; + if (val > desc->max) + return -EINVAL; + + return val * 1000; +} + +static int max8997_get_enable_register(struct regulator_dev *rdev, + int *reg, int *mask, int *pattern) +{ + int rid = max8997_get_rid(rdev); + + switch (rid) { + case MAX8997_LDO1 ... MAX8997_LDO21: + *reg = MAX8997_REG_LDO1CTRL + (rid - MAX8997_LDO1); + *mask = 0xC0; + *pattern = 0xC0; + break; + case MAX8997_BUCK1: + *reg = MAX8997_REG_BUCK1CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK2: + *reg = MAX8997_REG_BUCK2CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK3: + *reg = MAX8997_REG_BUCK3CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK4: + *reg = MAX8997_REG_BUCK4CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK5: + *reg = MAX8997_REG_BUCK5CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK6: + *reg = MAX8997_REG_BUCK6CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_BUCK7: + *reg = MAX8997_REG_BUCK7CTRL; + *mask = 0x01; + *pattern = 0x01; + break; + case MAX8997_EN32KHZ_AP ... MAX8997_EN32KHZ_CP: + *reg = MAX8997_REG_MAINCON1; + *mask = 0x01 << (rid - MAX8997_EN32KHZ_AP); + *pattern = 0x01 << (rid - MAX8997_EN32KHZ_AP); + break; + case MAX8997_ENVICHG: + *reg = MAX8997_REG_MBCCTRL1; + *mask = 0x80; + *pattern = 0x80; + break; + case MAX8997_ESAFEOUT1 ... MAX8997_ESAFEOUT2: + *reg = MAX8997_REG_SAFEOUTCTRL; + *mask = 0x40 << (rid - MAX8997_ESAFEOUT1); + *pattern = 0x40 << (rid - MAX8997_ESAFEOUT1); + break; + case MAX8997_CHARGER: + *reg = MAX8997_REG_MBCCTRL2; + *mask = 0x40; + *pattern = 0x40; + break; + default: + /* Not controllable or not exists */ + return -EINVAL; + break; + } + + return 0; +} + +static int max8997_reg_is_enabled(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + u8 val; + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret == -EINVAL) + return 1; /* "not controllable" */ + else if (ret) + return ret; + + ret = max8997_read_reg(i2c, reg, &val); + if (ret) + return ret; + + return (val & mask) == pattern; +} + +static int max8997_reg_enable(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + return max8997_update_reg(i2c, reg, pattern, mask); +} + +static int max8997_reg_disable(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + return max8997_update_reg(i2c, reg, ~pattern, mask); +} + +static int max8997_get_voltage_register(struct regulator_dev *rdev, + int *_reg, int *_shift, int *_mask) +{ + int rid = max8997_get_rid(rdev); + int reg, shift = 0, mask = 0x3f; + + switch (rid) { + case MAX8997_LDO1 ... MAX8997_LDO21: + reg = MAX8997_REG_LDO1CTRL + (rid - MAX8997_LDO1); + break; + case MAX8997_BUCK1: + reg = MAX8997_REG_BUCK1DVS1; + break; + case MAX8997_BUCK2: + reg = MAX8997_REG_BUCK2DVS1; + break; + case MAX8997_BUCK3: + reg = MAX8997_REG_BUCK3DVS; + break; + case MAX8997_BUCK4: + reg = MAX8997_REG_BUCK4DVS; + break; + case MAX8997_BUCK5: + reg = MAX8997_REG_BUCK5DVS1; + break; + case MAX8997_BUCK7: + reg = MAX8997_REG_BUCK7DVS; + break; + case MAX8997_ESAFEOUT1 ... MAX8997_ESAFEOUT2: + reg = MAX8997_REG_SAFEOUTCTRL; + shift = (rid == MAX8997_ESAFEOUT2) ? 2 : 0; + mask = 0x3; + break; + case MAX8997_CHARGER_CV: + reg = MAX8997_REG_MBCCTRL3; + shift = 0; + mask = 0xf; + break; + case MAX8997_CHARGER: + reg = MAX8997_REG_MBCCTRL4; + shift = 0; + mask = 0xf; + break; + case MAX8997_CHARGER_TOPOFF: + reg = MAX8997_REG_MBCCTRL5; + shift = 0; + mask = 0xf; + break; + default: + return -EINVAL; + } + + *_reg = reg; + *_shift = shift; + *_mask = mask; + + return 0; +} + +static int max8997_get_voltage(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct max8997_platform_data *pdata = + dev_get_platdata(max8997->iodev->dev); + struct i2c_client *i2c = max8997->iodev->i2c; + int reg, shift, mask, ret; + int rid = max8997_get_rid(rdev); + u8 val; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + if ((rid == MAX8997_BUCK1 && pdata->buck1_gpiodvs) || + (rid == MAX8997_BUCK2 && pdata->buck2_gpiodvs) || + (rid == MAX8997_BUCK5 && pdata->buck5_gpiodvs)) + reg += max8997->buck125_gpioindex; + + ret = max8997_read_reg(i2c, reg, &val); + if (ret) + return ret; + + val >>= shift; + val &= mask; + + if (rdev->desc && rdev->desc->ops && rdev->desc->ops->list_voltage) + return rdev->desc->ops->list_voltage(rdev, val); + + /* + * max8997_list_voltage returns value for any rdev with voltage_map, + * which works for "CHARGER" and "CHARGER TOPOFF" that do not have + * list_voltage ops (they are current regulators). + */ + return max8997_list_voltage(rdev, val); +} + +static inline int max8997_get_voltage_proper_val( + const struct voltage_map_desc *desc, + int min_vol, int max_vol) +{ + int i = 0; + + if (desc == NULL) + return -EINVAL; + + if (max_vol < desc->min || min_vol > desc->max) + return -EINVAL; + + while (desc->min + desc->step * i < min_vol && + desc->min + desc->step * i < desc->max) + i++; + + if (desc->min + desc->step * i > max_vol) + return -EINVAL; + + if (i >= (1 << desc->n_bits)) + return -EINVAL; + + return i; +} + +static int max8997_set_voltage_charger_cv(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int rid = max8997_get_rid(rdev); + int lb, ub; + int reg, shift = 0, mask, ret = 0; + u8 val = 0x0; + + if (rid != MAX8997_CHARGER_CV) + return -EINVAL; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + if (max_uV < 4000000 || min_uV > 4350000) + return -EINVAL; + + if (min_uV <= 4000000) { + if (max_uV >= 4000000) + return -EINVAL; + else + val = 0x1; + } else if (min_uV <= 4200000 && max_uV >= 4200000) + val = 0x0; + else { + lb = (min_uV - 4000001) / 20000 + 2; + ub = (max_uV - 4000000) / 20000 + 1; + + if (lb > ub) + return -EINVAL; + + if (lb < 0xf) + val = lb; + else { + if (ub >= 0xf) + val = 0xf; + else + return -EINVAL; + } + } + + *selector = val; + + ret = max8997_update_reg(i2c, reg, val << shift, mask); + + return ret; +} + +/* + * For LDO1 ~ LDO21, BUCK1~5, BUCK7, CHARGER, CHARGER_TOPOFF + * BUCK1, 2, and 5 are available if they are not controlled by gpio + */ +static int max8997_set_voltage_ldobuck(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + const struct voltage_map_desc *desc; + int rid = max8997_get_rid(rdev); + int reg, shift = 0, mask, ret; + int i; + u8 org; + + switch (rid) { + case MAX8997_LDO1 ... MAX8997_LDO21: + break; + case MAX8997_BUCK1 ... MAX8997_BUCK5: + break; + case MAX8997_BUCK6: + return -EINVAL; + case MAX8997_BUCK7: + break; + case MAX8997_CHARGER: + break; + case MAX8997_CHARGER_TOPOFF: + break; + default: + return -EINVAL; + } + + desc = reg_voltage_map[rid]; + + i = max8997_get_voltage_proper_val(desc, min_vol, max_vol); + if (i < 0) + return i; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + max8997_read_reg(i2c, reg, &org); + org = (org & mask) >> shift; + + ret = max8997_update_reg(i2c, reg, i << shift, mask << shift); + *selector = i; + + if (rid == MAX8997_BUCK1 || rid == MAX8997_BUCK2 || + rid == MAX8997_BUCK4 || rid == MAX8997_BUCK5) { + /* If the voltage is increasing */ + if (org < i) + udelay(desc->step * (i - org) / max8997->ramp_delay); + } + + return ret; +} + +/* + * Assess the damage on the voltage setting of BUCK1,2,5 by the change. + * + * When GPIO-DVS mode is used for multiple bucks, changing the voltage value + * of one of the bucks may affect that of another buck, which is the side + * effect of the change (set_voltage). This function examines the GPIO-DVS + * configurations and checks whether such side-effect exists. + */ +static int max8997_assess_side_effect(struct regulator_dev *rdev, + u8 new_val, int *best) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct max8997_platform_data *pdata = + dev_get_platdata(max8997->iodev->dev); + int rid = max8997_get_rid(rdev); + u8 *buckx_val[3]; + bool buckx_gpiodvs[3]; + int side_effect[8]; + int min_side_effect = INT_MAX; + int i; + + *best = -1; + + switch (rid) { + case MAX8997_BUCK1: + rid = 0; + break; + case MAX8997_BUCK2: + rid = 1; + break; + case MAX8997_BUCK5: + rid = 2; + break; + default: + return -EINVAL; + } + + buckx_val[0] = max8997->buck1_vol; + buckx_val[1] = max8997->buck2_vol; + buckx_val[2] = max8997->buck5_vol; + buckx_gpiodvs[0] = pdata->buck1_gpiodvs; + buckx_gpiodvs[1] = pdata->buck2_gpiodvs; + buckx_gpiodvs[2] = pdata->buck5_gpiodvs; + + for (i = 0; i < 8; i++) { + int others; + + if (new_val != (buckx_val[rid])[i]) { + side_effect[i] = -1; + continue; + } + + side_effect[i] = 0; + for (others = 0; others < 3; others++) { + int diff; + + if (others == rid) + continue; + if (buckx_gpiodvs[others] == false) + continue; /* Not affected */ + diff = (buckx_val[others])[i] - + (buckx_val[others])[max8997->buck125_gpioindex]; + if (diff > 0) + side_effect[i] += diff; + else if (diff < 0) + side_effect[i] -= diff; + } + if (side_effect[i] == 0) { + *best = i; + return 0; /* NO SIDE EFFECT! Use This! */ + } + if (side_effect[i] < min_side_effect) { + min_side_effect = side_effect[i]; + *best = i; + } + } + + if (*best == -1) + return -EINVAL; + + return side_effect[*best]; +} + +/* + * For Buck 1 ~ 5 and 7. If it is not controlled by GPIO, this calls + * max8997_set_voltage_ldobuck to do the job. + */ +static int max8997_set_voltage_buck(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct max8997_platform_data *pdata = + dev_get_platdata(max8997->iodev->dev); + int rid = max8997_get_rid(rdev); + const struct voltage_map_desc *desc; + int new_val, new_idx, damage, tmp_val, tmp_idx, tmp_dmg; + bool gpio_dvs_mode = false; + int min_vol = min_uV / 1000, max_vol = max_uV / 1000; + + if (rid < MAX8997_BUCK1 || rid > MAX8997_BUCK7) + return -EINVAL; + + switch (rid) { + case MAX8997_BUCK1: + if (pdata->buck1_gpiodvs) + gpio_dvs_mode = true; + break; + case MAX8997_BUCK2: + if (pdata->buck2_gpiodvs) + gpio_dvs_mode = true; + break; + case MAX8997_BUCK5: + if (pdata->buck5_gpiodvs) + gpio_dvs_mode = true; + break; + } + + if (!gpio_dvs_mode) + return max8997_set_voltage_ldobuck(rdev, min_uV, max_uV, + selector); + + desc = reg_voltage_map[rid]; + new_val = max8997_get_voltage_proper_val(desc, min_vol, max_vol); + if (new_val < 0) + return new_val; + + tmp_dmg = INT_MAX; + tmp_idx = -1; + tmp_val = -1; + do { + damage = max8997_assess_side_effect(rdev, new_val, &new_idx); + if (damage == 0) + goto out; + + if (tmp_dmg > damage) { + tmp_idx = new_idx; + tmp_val = new_val; + tmp_dmg = damage; + } + + new_val++; + } while (desc->min + desc->step + new_val <= desc->max); + + new_idx = tmp_idx; + new_val = tmp_val; + + if (pdata->ignore_gpiodvs_side_effect == false) + return -EINVAL; + + dev_warn(&rdev->dev, "MAX8997 GPIO-DVS Side Effect Warning: GPIO SET:" + " %d -> %d\n", max8997->buck125_gpioindex, tmp_idx); + +out: + if (new_idx < 0 || new_val < 0) + return -EINVAL; + + max8997->buck125_gpioindex = new_idx; + max8997_set_gpio(max8997); + *selector = new_val; + + return 0; +} + +static const int safeoutvolt[] = { + 3300000, + 4850000, + 4900000, + 4950000, +}; + +/* For SAFEOUT1 and SAFEOUT2 */ +static int max8997_set_voltage_safeout(struct regulator_dev *rdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int rid = max8997_get_rid(rdev); + int reg, shift = 0, mask, ret; + int i = 0; + u8 val; + + if (rid != MAX8997_ESAFEOUT1 && rid != MAX8997_ESAFEOUT2) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(safeoutvolt); i++) { + if (min_uV <= safeoutvolt[i] && + max_uV >= safeoutvolt[i]) + break; + } + + if (i >= ARRAY_SIZE(safeoutvolt)) + return -EINVAL; + + if (i == 0) + val = 0x3; + else + val = i - 1; + + ret = max8997_get_voltage_register(rdev, ®, &shift, &mask); + if (ret) + return ret; + + ret = max8997_update_reg(i2c, reg, val << shift, mask << shift); + *selector = val; + + return ret; +} + +static int max8997_reg_enable_suspend(struct regulator_dev *rdev) +{ + return 0; +} + +static int max8997_reg_disable_suspend(struct regulator_dev *rdev) +{ + struct max8997_data *max8997 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8997->iodev->i2c; + int ret, reg, mask, pattern; + int rid = max8997_get_rid(rdev); + + ret = max8997_get_enable_register(rdev, ®, &mask, &pattern); + if (ret) + return ret; + + max8997_read_reg(i2c, reg, &max8997->saved_states[rid]); + + if (rid == MAX8997_LDO1 || + rid == MAX8997_LDO10 || + rid == MAX8997_LDO21) { + dev_dbg(&rdev->dev, "Conditional Power-Off for %s\n", + rdev->desc->name); + return max8997_update_reg(i2c, reg, 0x40, mask); + } + + dev_dbg(&rdev->dev, "Full Power-Off for %s (%xh -> %xh)\n", + rdev->desc->name, max8997->saved_states[rid] & mask, + (~pattern) & mask); + return max8997_update_reg(i2c, reg, ~pattern, mask); +} + +static struct regulator_ops max8997_ldo_ops = { + .list_voltage = max8997_list_voltage, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_voltage = max8997_get_voltage, + .set_voltage = max8997_set_voltage_ldobuck, + .set_suspend_enable = max8997_reg_enable_suspend, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_buck_ops = { + .list_voltage = max8997_list_voltage, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_voltage = max8997_get_voltage, + .set_voltage = max8997_set_voltage_buck, + .set_suspend_enable = max8997_reg_enable_suspend, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_fixedvolt_ops = { + .list_voltage = max8997_list_voltage, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .set_suspend_enable = max8997_reg_enable_suspend, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_safeout_ops = { + .list_voltage = max8997_list_voltage_safeout, + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_voltage = max8997_get_voltage, + .set_voltage = max8997_set_voltage_safeout, + .set_suspend_enable = max8997_reg_enable_suspend, + .set_suspend_disable = max8997_reg_disable_suspend, +}; + +static struct regulator_ops max8997_fixedstate_ops = { + .list_voltage = max8997_list_voltage_charger_cv, + .get_voltage = max8997_get_voltage, + .set_voltage = max8997_set_voltage_charger_cv, +}; + +static int max8997_set_voltage_ldobuck_wrap(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + unsigned dummy; + + return max8997_set_voltage_ldobuck(rdev, min_uV, max_uV, &dummy); +} + + +static struct regulator_ops max8997_charger_ops = { + .is_enabled = max8997_reg_is_enabled, + .enable = max8997_reg_enable, + .disable = max8997_reg_disable, + .get_current_limit = max8997_get_voltage, + .set_current_limit = max8997_set_voltage_ldobuck_wrap, +}; + +static struct regulator_ops max8997_charger_fixedstate_ops = { + .is_enabled = max8997_reg_is_enabled, + .get_current_limit = max8997_get_voltage, + .set_current_limit = max8997_set_voltage_ldobuck_wrap, +}; + +#define regulator_desc_ldo(num) { \ + .name = "LDO"#num, \ + .id = MAX8997_LDO##num, \ + .ops = &max8997_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} +#define regulator_desc_buck(num) { \ + .name = "BUCK"#num, \ + .id = MAX8997_BUCK##num, \ + .ops = &max8997_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_ldo(1), + regulator_desc_ldo(2), + regulator_desc_ldo(3), + regulator_desc_ldo(4), + regulator_desc_ldo(5), + regulator_desc_ldo(6), + regulator_desc_ldo(7), + regulator_desc_ldo(8), + regulator_desc_ldo(9), + regulator_desc_ldo(10), + regulator_desc_ldo(11), + regulator_desc_ldo(12), + regulator_desc_ldo(13), + regulator_desc_ldo(14), + regulator_desc_ldo(15), + regulator_desc_ldo(16), + regulator_desc_ldo(17), + regulator_desc_ldo(18), + regulator_desc_ldo(21), + regulator_desc_buck(1), + regulator_desc_buck(2), + regulator_desc_buck(3), + regulator_desc_buck(4), + regulator_desc_buck(5), + { + .name = "BUCK6", + .id = MAX8997_BUCK6, + .ops = &max8997_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + regulator_desc_buck(7), + { + .name = "EN32KHz AP", + .id = MAX8997_EN32KHZ_AP, + .ops = &max8997_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "EN32KHz CP", + .id = MAX8997_EN32KHZ_CP, + .ops = &max8997_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ENVICHG", + .id = MAX8997_ENVICHG, + .ops = &max8997_fixedvolt_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT1", + .id = MAX8997_ESAFEOUT1, + .ops = &max8997_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "ESAFEOUT2", + .id = MAX8997_ESAFEOUT2, + .ops = &max8997_safeout_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "CHARGER CV", + .id = MAX8997_CHARGER_CV, + .ops = &max8997_fixedstate_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, { + .name = "CHARGER", + .id = MAX8997_CHARGER, + .ops = &max8997_charger_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, { + .name = "CHARGER TOPOFF", + .id = MAX8997_CHARGER_TOPOFF, + .ops = &max8997_charger_fixedstate_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, +}; + +static __devinit int max8997_pmic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max8997_data *max8997; + struct i2c_client *i2c; + int i, ret, size; + u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; + + if (!pdata) { + dev_err(pdev->dev.parent, "No platform init data supplied.\n"); + return -ENODEV; + } + + max8997 = kzalloc(sizeof(struct max8997_data), GFP_KERNEL); + if (!max8997) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * pdata->num_regulators; + max8997->rdev = kzalloc(size, GFP_KERNEL); + if (!max8997->rdev) { + kfree(max8997); + return -ENOMEM; + } + + rdev = max8997->rdev; + max8997->dev = &pdev->dev; + max8997->iodev = iodev; + max8997->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, max8997); + i2c = max8997->iodev->i2c; + + max8997->buck125_gpioindex = pdata->buck125_default_idx; + + for (i = 0; i < 8; i++) { + max8997->buck1_vol[i] = ret = + max8997_get_voltage_proper_val( + &buck1245_voltage_map_desc, + pdata->buck1_voltage[i] / 1000, + pdata->buck1_voltage[i] / 1000 + + buck1245_voltage_map_desc.step); + if (ret < 0) + goto err_alloc; + + max8997->buck2_vol[i] = ret = + max8997_get_voltage_proper_val( + &buck1245_voltage_map_desc, + pdata->buck2_voltage[i] / 1000, + pdata->buck2_voltage[i] / 1000 + + buck1245_voltage_map_desc.step); + if (ret < 0) + goto err_alloc; + + max8997->buck5_vol[i] = ret = + max8997_get_voltage_proper_val( + &buck1245_voltage_map_desc, + pdata->buck5_voltage[i] / 1000, + pdata->buck5_voltage[i] / 1000 + + buck1245_voltage_map_desc.step); + if (ret < 0) + goto err_alloc; + + if (max_buck1 < max8997->buck1_vol[i]) + max_buck1 = max8997->buck1_vol[i]; + if (max_buck2 < max8997->buck2_vol[i]) + max_buck2 = max8997->buck2_vol[i]; + if (max_buck5 < max8997->buck5_vol[i]) + max_buck5 = max8997->buck5_vol[i]; + } + + /* For the safety, set max voltage before setting up */ + for (i = 0; i < 8; i++) { + max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS(i + 1), + max_buck1, 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS(i + 1), + max_buck2, 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS(i + 1), + max_buck5, 0x3f); + } + + /* + * If buck 1, 2, and 5 do not care DVS GPIO settings, ignore them. + * If at least one of them cares, set gpios. + */ + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + bool gpio1set = false, gpio2set = false; + + if (!gpio_is_valid(pdata->buck125_gpios[0]) || + !gpio_is_valid(pdata->buck125_gpios[1]) || + !gpio_is_valid(pdata->buck125_gpios[2])) { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + ret = -EINVAL; + goto err_alloc; + } + + ret = gpio_request(pdata->buck125_gpios[0], + "MAX8997 SET1"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " on SET1\n"); + else if (ret) + goto err_alloc; + else + gpio1set = true; + + ret = gpio_request(pdata->buck125_gpios[1], + "MAX8997 SET2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " on SET2\n"); + else if (ret) { + if (gpio1set) + gpio_free(pdata->buck125_gpios[0]); + goto err_alloc; + } else + gpio2set = true; + + ret = gpio_request(pdata->buck125_gpios[2], + "MAX8997 SET3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " on SET3\n"); + else if (ret) { + if (gpio1set) + gpio_free(pdata->buck125_gpios[0]); + if (gpio2set) + gpio_free(pdata->buck125_gpios[1]); + goto err_alloc; + } + + gpio_direction_output(pdata->buck125_gpios[0], + (max8997->buck125_gpioindex >> 2) + & 0x1); /* SET1 */ + gpio_direction_output(pdata->buck125_gpios[1], + (max8997->buck125_gpioindex >> 1) + & 0x1); /* SET2 */ + gpio_direction_output(pdata->buck125_gpios[2], + (max8997->buck125_gpioindex >> 0) + & 0x1); /* SET3 */ + ret = 0; + } + + /* DVS-GPIO disabled */ + max8997_update_reg(i2c, MAX8997_REG_BUCK1CTRL, (pdata->buck1_gpiodvs) ? + (1 << 1) : (0 << 1), 1 << 1); + max8997_update_reg(i2c, MAX8997_REG_BUCK2CTRL, (pdata->buck2_gpiodvs) ? + (1 << 1) : (0 << 1), 1 << 1); + max8997_update_reg(i2c, MAX8997_REG_BUCK5CTRL, (pdata->buck5_gpiodvs) ? + (1 << 1) : (0 << 1), 1 << 1); + + /* Initialize all the DVS related BUCK registers */ + for (i = 0; i < 8; i++) { + max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS(i + 1), + max8997->buck1_vol[i], + 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS(i + 1), + max8997->buck2_vol[i], + 0x3f); + max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS(i + 1), + max8997->buck5_vol[i], + 0x3f); + } + + for (i = 0; i < pdata->num_regulators; i++) { + const struct voltage_map_desc *desc; + int id = pdata->regulators[i].id; + + desc = reg_voltage_map[id]; + if (desc) + regulators[id].n_voltages = + (desc->max - desc->min) / desc->step + 1; + else if (id == MAX8997_ESAFEOUT1 || id == MAX8997_ESAFEOUT2) + regulators[id].n_voltages = 4; + else if (id == MAX8997_CHARGER_CV) + regulators[id].n_voltages = 16; + + rdev[i] = regulator_register(®ulators[id], max8997->dev, + pdata->regulators[i].initdata, max8997); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max8997->dev, "regulator init failed for %d\n", + id); + rdev[i] = NULL; + goto err; + } + } + + /* Misc Settings */ + max8997->ramp_delay = 10; /* set 10mV/us, which is the default */ + max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9); + + return 0; +err: + for (i = 0; i < max8997->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); +err_alloc: + kfree(max8997->rdev); + kfree(max8997); + + return ret; +} + +static int __devexit max8997_pmic_remove(struct platform_device *pdev) +{ + struct max8997_data *max8997 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max8997->rdev; + int i; + + for (i = 0; i < max8997->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + kfree(max8997->rdev); + kfree(max8997); + + return 0; +} + +static const struct platform_device_id max8997_pmic_id[] = { + { "max8997-pmic", 0}, + { }, +}; + +static struct platform_driver max8997_pmic_driver = { + .driver = { + .name = "max8997-pmic", + .owner = THIS_MODULE, + }, + .probe = max8997_pmic_probe, + .remove = __devexit_p(max8997_pmic_remove), + .id_table = max8997_pmic_id, +}; + +static int __init max8997_pmic_init(void) +{ + return platform_driver_register(&max8997_pmic_driver); +} +subsys_initcall(max8997_pmic_init); + +static void __exit max8997_pmic_cleanup(void) +{ + platform_driver_unregister(&max8997_pmic_driver); +} +module_exit(max8997_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 8997/8966 Regulator Driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 3e5d0c3b4e53..23249cb0a8bd 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -15,6 +15,7 @@ #include <linux/regulator/driver.h> #include <linux/platform_device.h> #include <linux/kernel.h> +#include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> @@ -336,8 +337,7 @@ static int __devinit mc13783_regulator_probe(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv; struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); - struct mc13783_regulator_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct mc13783_regulator_platform_data *pdata = mfd_get_data(pdev); struct mc13783_regulator_init_data *init_data; int i, ret; @@ -381,8 +381,7 @@ err: static int __devexit mc13783_regulator_remove(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - struct mc13783_regulator_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct mc13783_regulator_platform_data *pdata = mfd_get_data(pdev); int i; platform_set_drvdata(pdev, NULL); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index 1b8f7398a4a8..6f15168e5ed4 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -15,6 +15,7 @@ #include <linux/regulator/driver.h> #include <linux/platform_device.h> #include <linux/kernel.h> +#include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> @@ -520,8 +521,7 @@ static int __devinit mc13892_regulator_probe(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv; struct mc13xxx *mc13892 = dev_get_drvdata(pdev->dev.parent); - struct mc13xxx_regulator_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct mc13xxx_regulator_platform_data *pdata = mfd_get_data(pdev); struct mc13xxx_regulator_init_data *init_data; int i, ret; u32 val; @@ -595,8 +595,7 @@ err_free: static int __devexit mc13892_regulator_remove(struct platform_device *pdev) { struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); - struct mc13xxx_regulator_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct mc13xxx_regulator_platform_data *pdata = mfd_get_data(pdev); int i; platform_set_drvdata(pdev, NULL); diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c new file mode 100644 index 000000000000..1661499feda4 --- /dev/null +++ b/drivers/regulator/tps6105x-regulator.c @@ -0,0 +1,196 @@ +/* + * Driver for TPS61050/61052 boost converters, typically used for white LEDs + * or audio amplifiers. + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps6105x.h> + +static const int tps6105x_voltages[] = { + 4500000, + 5000000, + 5250000, + 5000000, /* There is an additional 5V */ +}; + +static int tps6105x_regulator_enable(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + int ret; + + /* Activate voltage mode */ + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK, + TPS6105X_REG0_MODE_VOLTAGE << TPS6105X_REG0_MODE_SHIFT); + if (ret) + return ret; + + return 0; +} + +static int tps6105x_regulator_disable(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + int ret; + + /* Set into shutdown mode */ + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK, + TPS6105X_REG0_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); + if (ret) + return ret; + + return 0; +} + +static int tps6105x_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + u8 regval; + int ret; + + ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); + if (ret) + return ret; + regval &= TPS6105X_REG0_MODE_MASK; + regval >>= TPS6105X_REG0_MODE_SHIFT; + + if (regval == TPS6105X_REG0_MODE_VOLTAGE) + return 1; + + return 0; +} + +static int tps6105x_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + u8 regval; + int ret; + + ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); + if (ret) + return ret; + + regval &= TPS6105X_REG0_VOLTAGE_MASK; + regval >>= TPS6105X_REG0_VOLTAGE_SHIFT; + return (int) regval; +} + +static int tps6105x_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct tps6105x *tps6105x = rdev_get_drvdata(rdev); + int ret; + + ret = tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_VOLTAGE_MASK, + selector << TPS6105X_REG0_VOLTAGE_SHIFT); + if (ret) + return ret; + + return 0; +} + +static int tps6105x_regulator_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector >= ARRAY_SIZE(tps6105x_voltages)) + return -EINVAL; + + return tps6105x_voltages[selector]; +} + +static struct regulator_ops tps6105x_regulator_ops = { + .enable = tps6105x_regulator_enable, + .disable = tps6105x_regulator_disable, + .is_enabled = tps6105x_regulator_is_enabled, + .get_voltage_sel = tps6105x_regulator_get_voltage_sel, + .set_voltage_sel = tps6105x_regulator_set_voltage_sel, + .list_voltage = tps6105x_regulator_list_voltage, +}; + +static struct regulator_desc tps6105x_regulator_desc = { + .name = "tps6105x-boost", + .ops = &tps6105x_regulator_ops, + .type = REGULATOR_VOLTAGE, + .id = 0, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(tps6105x_voltages), +}; + +/* + * Registers the chip as a voltage regulator + */ +static int __devinit tps6105x_regulator_probe(struct platform_device *pdev) +{ + struct tps6105x *tps6105x = mfd_get_data(pdev); + struct tps6105x_platform_data *pdata = tps6105x->pdata; + int ret; + + /* This instance is not set for regulator mode so bail out */ + if (pdata->mode != TPS6105X_MODE_VOLTAGE) { + dev_info(&pdev->dev, + "chip not in voltage mode mode, exit probe \n"); + return 0; + } + + /* Register regulator with framework */ + tps6105x->regulator = regulator_register(&tps6105x_regulator_desc, + &tps6105x->client->dev, + pdata->regulator_data, tps6105x); + if (IS_ERR(tps6105x->regulator)) { + ret = PTR_ERR(tps6105x->regulator); + dev_err(&tps6105x->client->dev, + "failed to register regulator\n"); + return ret; + } + + return 0; +} + +static int __devexit tps6105x_regulator_remove(struct platform_device *pdev) +{ + struct tps6105x *tps6105x = platform_get_drvdata(pdev); + regulator_unregister(tps6105x->regulator); + return 0; +} + +static struct platform_driver tps6105x_regulator_driver = { + .driver = { + .name = "tps6105x-regulator", + .owner = THIS_MODULE, + }, + .probe = tps6105x_regulator_probe, + .remove = __devexit_p(tps6105x_regulator_remove), +}; + +static __init int tps6105x_regulator_init(void) +{ + return platform_driver_register(&tps6105x_regulator_driver); +} +subsys_initcall(tps6105x_regulator_init); + +static __exit void tps6105x_regulator_exit(void) +{ + platform_driver_unregister(&tps6105x_regulator_driver); +} +module_exit(tps6105x_regulator_exit); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("TPS6105x regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps6105x-regulator"); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index bd332cf1cc3f..6a292852a358 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -475,6 +475,13 @@ static struct regulator_ops twlfixed_ops = { .get_status = twlreg_get_status, }; +static struct regulator_ops twl6030_fixed_resource = { + .enable = twlreg_enable, + .disable = twlreg_disable, + .is_enabled = twlreg_is_enabled, + .get_status = twlreg_get_status, +}; + /*----------------------------------------------------------------------*/ #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ @@ -538,6 +545,20 @@ static struct regulator_ops twlfixed_ops = { }, \ } +#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay, remap_conf) { \ + .base = offset, \ + .id = num, \ + .delay = turnon_delay, \ + .remap = remap_conf, \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .ops = &twl6030_fixed_resource, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + /* * We list regulators here if systems need some level of * software control over them after boot. @@ -577,7 +598,8 @@ static struct twlreg_info twl_regs[] = { TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x21), TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x21), TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x21), - TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21) + TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21), + TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0, 0x21), }; static int __devinit twlreg_probe(struct platform_device *pdev) diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 4d2c75df886c..c69c6f2c2c5c 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> #include <linux/spi/xilinx_spi.h> @@ -470,7 +471,7 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev) struct spi_master *master; u8 i; - pdata = dev->dev.platform_data; + pdata = mfd_get_data(dev); if (pdata) { num_cs = pdata->num_chipselect; little_endian = pdata->little_endian; diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 8dabe8e31d8c..3558491dd87d 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -185,7 +185,7 @@ static struct platform_driver ohci_hcd_tmio_driver; static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct resource *regs = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *config = platform_get_resource(dev, IORESOURCE_MEM, 1); struct resource *sram = platform_get_resource(dev, IORESOURCE_MEM, 2); @@ -274,7 +274,7 @@ static int __devexit ohci_hcd_tmio_drv_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct tmio_hcd *tmio = hcd_to_tmio(hcd); - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); usb_remove_hcd(hcd); tmio_stop_hc(dev); @@ -293,7 +293,7 @@ static int __devexit ohci_hcd_tmio_drv_remove(struct platform_device *dev) #ifdef CONFIG_PM static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct tmio_hcd *tmio = hcd_to_tmio(hcd); @@ -326,7 +326,7 @@ static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t s static int ohci_hcd_tmio_drv_resume(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct tmio_hcd *tmio = hcd_to_tmio(hcd); diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index e59623a15f3f..c8b520e9a11a 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -12,11 +12,12 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/fb.h> #include <linux/i2c.h> #include <linux/backlight.h> +#include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> -#include <linux/slab.h> #define MAX_BRIGHTNESS (0xFF) #define MIN_BRIGHTNESS (0) @@ -161,32 +162,13 @@ static const struct backlight_ops pm860x_backlight_ops = { .get_brightness = pm860x_backlight_get_brightness, }; -static int __check_device(struct pm860x_backlight_pdata *pdata, char *name) -{ - struct pm860x_backlight_pdata *p = pdata; - int ret = -EINVAL; - - while (p && p->id) { - if ((p->id != PM8606_ID_BACKLIGHT) || (p->flags < 0)) - break; - - if (!strncmp(name, pm860x_backlight_name[p->flags], - MFD_NAME_SIZE)) { - ret = (int)p->flags; - break; - } - p++; - } - return ret; -} - static int pm860x_backlight_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct pm860x_platform_data *pm860x_pdata; struct pm860x_backlight_pdata *pdata = NULL; struct pm860x_backlight_data *data; struct backlight_device *bl; + struct mfd_cell *cell; struct resource *res; struct backlight_properties props; unsigned char value; @@ -199,10 +181,10 @@ static int pm860x_backlight_probe(struct platform_device *pdev) return -EINVAL; } - if (pdev->dev.parent->platform_data) { - pm860x_pdata = pdev->dev.parent->platform_data; - pdata = pm860x_pdata->backlight; - } + cell = pdev->dev.platform_data; + if (cell == NULL) + return -ENODEV; + pdata = cell->mfd_data; if (pdata == NULL) { dev_err(&pdev->dev, "platform data isn't assigned to " "backlight\n"); @@ -219,7 +201,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev) data->current_brightness = MAX_BRIGHTNESS; data->pwm = pdata->pwm; data->iset = pdata->iset; - data->port = __check_device(pdata, name); + data->port = pdata->flags; if (data->port < 0) { dev_err(&pdev->dev, "wrong platform data is assigned"); kfree(data); diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c index dfef88c803d4..9710bf8caeae 100644 --- a/drivers/video/tmiofb.c +++ b/drivers/video/tmiofb.c @@ -250,8 +250,7 @@ static irqreturn_t tmiofb_irq(int irq, void *__info) */ static int tmiofb_hw_stop(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; - struct tmio_fb_data *data = cell->driver_data; + struct tmio_fb_data *data = mfd_get_data(dev); struct fb_info *info = platform_get_drvdata(dev); struct tmiofb_par *par = info->par; @@ -268,7 +267,7 @@ static int tmiofb_hw_stop(struct platform_device *dev) */ static int tmiofb_hw_init(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct fb_info *info = platform_get_drvdata(dev); struct tmiofb_par *par = info->par; const struct resource *nlcr = &cell->resources[0]; @@ -312,8 +311,7 @@ static int tmiofb_hw_init(struct platform_device *dev) */ static void tmiofb_hw_mode(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; - struct tmio_fb_data *data = cell->driver_data; + struct tmio_fb_data *data = mfd_get_data(dev); struct fb_info *info = platform_get_drvdata(dev); struct fb_videomode *mode = info->mode; struct tmiofb_par *par = info->par; @@ -559,9 +557,8 @@ static int tmiofb_ioctl(struct fb_info *fbi, static struct fb_videomode * tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var) { - struct mfd_cell *cell = - info->device->platform_data; - struct tmio_fb_data *data = cell->driver_data; + struct tmio_fb_data *data = + mfd_get_data(to_platform_device(info->device)); struct fb_videomode *best = NULL; int i; @@ -581,9 +578,8 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct fb_videomode *mode; - struct mfd_cell *cell = - info->device->platform_data; - struct tmio_fb_data *data = cell->driver_data; + struct tmio_fb_data *data = + mfd_get_data(to_platform_device(info->device)); mode = tmiofb_find_mode(info, var); if (!mode || var->bits_per_pixel > 16) @@ -683,8 +679,8 @@ static struct fb_ops tmiofb_ops = { static int __devinit tmiofb_probe(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; - struct tmio_fb_data *data = cell->driver_data; + const struct mfd_cell *cell = mfd_get_cell(dev); + struct tmio_fb_data *data = mfd_get_data(dev); struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1); struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2); @@ -811,7 +807,7 @@ err_ioremap_ccr: static int __devexit tmiofb_remove(struct platform_device *dev) { - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); struct fb_info *info = platform_get_drvdata(dev); int irq = platform_get_irq(dev, 0); struct tmiofb_par *par; @@ -941,7 +937,7 @@ static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) #ifdef CONFIG_FB_TMIO_ACCELL struct tmiofb_par *par = info->par; #endif - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); int retval = 0; console_lock(); @@ -973,7 +969,7 @@ static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) static int tmiofb_resume(struct platform_device *dev) { struct fb_info *info = platform_get_drvdata(dev); - struct mfd_cell *cell = dev->dev.platform_data; + const struct mfd_cell *cell = mfd_get_cell(dev); int retval = 0; console_lock(); diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 6b85e7fefa43..95921b77cf86 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -90,7 +90,7 @@ struct ds1wm_data { void __iomem *map; int bus_shift; /* # of shifts to calc register offsets */ struct platform_device *pdev; - struct mfd_cell *cell; + const struct mfd_cell *cell; int irq; int active_high; int slave_present; @@ -216,7 +216,7 @@ static int ds1wm_find_divisor(int gclk) static void ds1wm_up(struct ds1wm_data *ds1wm_data) { int divisor; - struct ds1wm_driver_data *plat = ds1wm_data->cell->driver_data; + struct ds1wm_driver_data *plat = mfd_get_data(ds1wm_data->pdev); if (ds1wm_data->cell->enable) ds1wm_data->cell->enable(ds1wm_data->pdev); @@ -330,16 +330,11 @@ static int ds1wm_probe(struct platform_device *pdev) struct ds1wm_data *ds1wm_data; struct ds1wm_driver_data *plat; struct resource *res; - struct mfd_cell *cell; int ret; if (!pdev) return -ENODEV; - cell = pdev->dev.platform_data; - if (!cell) - return -ENODEV; - ds1wm_data = kzalloc(sizeof(*ds1wm_data), GFP_KERNEL); if (!ds1wm_data) return -ENOMEM; @@ -356,13 +351,13 @@ static int ds1wm_probe(struct platform_device *pdev) ret = -ENOMEM; goto err0; } - plat = cell->driver_data; + plat = mfd_get_data(pdev); /* calculate bus shift from mem resource */ ds1wm_data->bus_shift = resource_size(res) >> 3; ds1wm_data->pdev = pdev; - ds1wm_data->cell = cell; + ds1wm_data->cell = mfd_get_cell(pdev); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 3939e53f5f98..d8e725082fdc 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -37,6 +37,7 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <linux/mfd/rdc321x.h> +#include <linux/mfd/core.h> #define RDC_WDT_MASK 0x80000000 /* Mask */ #define RDC_WDT_EN 0x00800000 /* Enable bit */ @@ -231,7 +232,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) struct resource *r; struct rdc321x_wdt_pdata *pdata; - pdata = platform_get_drvdata(pdev); + pdata = mfd_get_data(pdev); if (!pdata) { dev_err(&pdev->dev, "no platform data supplied\n"); return -ENODEV; diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 58afd9d2c438..0c0d1ae79981 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -698,6 +698,7 @@ struct twl4030_platform_data { struct regulator_init_data *vana; struct regulator_init_data *vcxio; struct regulator_init_data *vusb; + struct regulator_init_data *clk32kg; }; /*----------------------------------------------------------------------*/ @@ -777,5 +778,6 @@ static inline int twl4030charger_usb_en(int enable) { return 0; } /* INTERNAL LDOs */ #define TWL6030_REG_VRTC 47 +#define TWL6030_REG_CLK32KG 48 #endif /* End of __TWL4030_H */ diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h new file mode 100644 index 000000000000..6427d298fbfc --- /dev/null +++ b/include/linux/i2c/twl4030-madc.h @@ -0,0 +1,141 @@ +/* + * twl4030_madc.h - Header for TWL4030 MADC + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef _TWL4030_MADC_H +#define _TWL4030_MADC_H + +struct twl4030_madc_conversion_method { + u8 sel; + u8 avg; + u8 rbase; + u8 ctrl; +}; + +#define TWL4030_MADC_MAX_CHANNELS 16 + + +/* + * twl4030_madc_request- madc request packet for channel conversion + * @channels: 16 bit bitmap for individual channels + * @do_avgP: sample the input channel for 4 consecutive cycles + * @method: RT, SW1, SW2 + * @type: Polling or interrupt based method + */ + +struct twl4030_madc_request { + unsigned long channels; + u16 do_avg; + u16 method; + u16 type; + bool active; + bool result_pending; + int rbuf[TWL4030_MADC_MAX_CHANNELS]; + void (*func_cb)(int len, int channels, int *buf); +}; + +enum conversion_methods { + TWL4030_MADC_RT, + TWL4030_MADC_SW1, + TWL4030_MADC_SW2, + TWL4030_MADC_NUM_METHODS +}; + +enum sample_type { + TWL4030_MADC_WAIT, + TWL4030_MADC_IRQ_ONESHOT, + TWL4030_MADC_IRQ_REARM +}; + +#define TWL4030_MADC_CTRL1 0x00 +#define TWL4030_MADC_CTRL2 0x01 + +#define TWL4030_MADC_RTSELECT_LSB 0x02 +#define TWL4030_MADC_SW1SELECT_LSB 0x06 +#define TWL4030_MADC_SW2SELECT_LSB 0x0A + +#define TWL4030_MADC_RTAVERAGE_LSB 0x04 +#define TWL4030_MADC_SW1AVERAGE_LSB 0x08 +#define TWL4030_MADC_SW2AVERAGE_LSB 0x0C + +#define TWL4030_MADC_CTRL_SW1 0x12 +#define TWL4030_MADC_CTRL_SW2 0x13 + +#define TWL4030_MADC_RTCH0_LSB 0x17 +#define TWL4030_MADC_GPCH0_LSB 0x37 + +#define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */ +#define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */ +/* MADC conversion completion */ +#define TWL4030_MADC_EOC_SW (1 << 1) +/* MADC SWx start conversion */ +#define TWL4030_MADC_SW_START (1 << 5) +#define TWL4030_MADC_ADCIN0 (1 << 0) +#define TWL4030_MADC_ADCIN1 (1 << 1) +#define TWL4030_MADC_ADCIN2 (1 << 2) +#define TWL4030_MADC_ADCIN3 (1 << 3) +#define TWL4030_MADC_ADCIN4 (1 << 4) +#define TWL4030_MADC_ADCIN5 (1 << 5) +#define TWL4030_MADC_ADCIN6 (1 << 6) +#define TWL4030_MADC_ADCIN7 (1 << 7) +#define TWL4030_MADC_ADCIN8 (1 << 8) +#define TWL4030_MADC_ADCIN9 (1 << 9) +#define TWL4030_MADC_ADCIN10 (1 << 10) +#define TWL4030_MADC_ADCIN11 (1 << 11) +#define TWL4030_MADC_ADCIN12 (1 << 12) +#define TWL4030_MADC_ADCIN13 (1 << 13) +#define TWL4030_MADC_ADCIN14 (1 << 14) +#define TWL4030_MADC_ADCIN15 (1 << 15) + +/* Fixed channels */ +#define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1 +#define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8 +#define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9 +#define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10 +#define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11 +#define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12 + +/* Step size and prescaler ratio */ +#define TEMP_STEP_SIZE 147 +#define TEMP_PSR_R 100 +#define CURR_STEP_SIZE 147 +#define CURR_PSR_R1 44 +#define CURR_PSR_R2 88 + +#define TWL4030_BCI_BCICTL1 0x23 +#define TWL4030_BCI_CGAIN 0x020 +#define TWL4030_BCI_MESBAT (1 << 1) +#define TWL4030_BCI_TYPEN (1 << 4) +#define TWL4030_BCI_ITHEN (1 << 3) + +#define REG_BCICTL2 0x024 +#define TWL4030_BCI_ITHSENS 0x007 + +struct twl4030_madc_user_parms { + int channel; + int average; + int status; + u16 result; +}; + +int twl4030_madc_conversion(struct twl4030_madc_request *conv); +int twl4030_get_madc_conversion(int channel_no); +#endif diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index 4db1fbd8969e..8fba7972ff5f 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -131,9 +131,11 @@ enum { PM8607_ID_LDO8, PM8607_ID_LDO9, PM8607_ID_LDO10, + PM8607_ID_LDO11, PM8607_ID_LDO12, PM8607_ID_LDO13, PM8607_ID_LDO14, + PM8607_ID_LDO15, PM8607_ID_RG_MAX, }; @@ -310,8 +312,6 @@ struct pm860x_chip { }; -#define PM8607_MAX_REGULATOR PM8607_ID_RG_MAX /* 3 Bucks, 13 LDOs */ - enum { GI2C_PORT = 0, PI2C_PORT, @@ -351,23 +351,31 @@ struct pm860x_platform_data { struct pm860x_led_pdata *led; struct pm860x_touch_pdata *touch; struct pm860x_power_pdata *power; + struct regulator_init_data *regulator; unsigned short companion_addr; /* I2C address of companion chip */ int i2c_port; /* Controlled by GI2C or PI2C */ int irq_mode; /* Clear interrupt by read/write(0/1) */ int irq_base; /* IRQ base number of 88pm860x */ - struct regulator_init_data *regulator[PM8607_MAX_REGULATOR]; + int num_leds; + int num_backlights; + int num_regulators; }; -extern char pm860x_backlight_name[][MFD_NAME_SIZE]; -extern char pm860x_led_name[][MFD_NAME_SIZE]; - extern int pm860x_reg_read(struct i2c_client *, int); extern int pm860x_reg_write(struct i2c_client *, int, unsigned char); extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *); extern int pm860x_bulk_write(struct i2c_client *, int, int, unsigned char *); extern int pm860x_set_bits(struct i2c_client *, int, unsigned char, unsigned char); +extern int pm860x_page_reg_read(struct i2c_client *, int); +extern int pm860x_page_reg_write(struct i2c_client *, int, unsigned char); +extern int pm860x_page_bulk_read(struct i2c_client *, int, int, + unsigned char *); +extern int pm860x_page_bulk_write(struct i2c_client *, int, int, + unsigned char *); +extern int pm860x_page_set_bits(struct i2c_client *, int, unsigned char, + unsigned char); extern int pm860x_device_init(struct pm860x_chip *chip, struct pm860x_platform_data *pdata) __devinit ; diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h index 37f56b7c4c15..56f8dea72152 100644 --- a/include/linux/mfd/ab8500.h +++ b/include/linux/mfd/ab8500.h @@ -111,8 +111,8 @@ * @dev: parent device * @lock: read/write operations lock * @irq_lock: genirq bus lock - * @revision: chip revision * @irq: irq line + * @chip_id: chip revision id * @write: register write * @read: register read * @rx_buf: rx buf for SPI @@ -124,7 +124,7 @@ struct ab8500 { struct device *dev; struct mutex lock; struct mutex irq_lock; - int revision; + int irq_base; int irq; u8 chip_id; diff --git a/include/linux/mfd/ab8500/gpadc.h b/include/linux/mfd/ab8500/gpadc.h new file mode 100644 index 000000000000..46b954011f16 --- /dev/null +++ b/include/linux/mfd/ab8500/gpadc.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010 ST-Ericsson SA + * Licensed under GPLv2. + * + * Author: Arun R Murthy <arun.murthy@stericsson.com> + * Author: Daniel Willerud <daniel.willerud@stericsson.com> + */ + +#ifndef _AB8500_GPADC_H +#define _AB8500_GPADC_H + +/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2) */ +#define BAT_CTRL 0x01 +#define BTEMP_BALL 0x02 +#define MAIN_CHARGER_V 0x03 +#define ACC_DETECT1 0x04 +#define ACC_DETECT2 0x05 +#define ADC_AUX1 0x06 +#define ADC_AUX2 0x07 +#define MAIN_BAT_V 0x08 +#define VBUS_V 0x09 +#define MAIN_CHARGER_C 0x0A +#define USB_CHARGER_C 0x0B +#define BK_BAT_V 0x0C +#define DIE_TEMP 0x0D + +struct ab8500_gpadc; + +struct ab8500_gpadc *ab8500_gpadc_get(char *name); +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input); + +#endif /* _AB8500_GPADC_H */ diff --git a/include/linux/mfd/ab8500/sysctrl.h b/include/linux/mfd/ab8500/sysctrl.h new file mode 100644 index 000000000000..10da0291f8f8 --- /dev/null +++ b/include/linux/mfd/ab8500/sysctrl.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson. + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef __AB8500_SYSCTRL_H +#define __AB8500_SYSCTRL_H + +#include <linux/bitops.h> + +#ifdef CONFIG_AB8500_CORE + +int ab8500_sysctrl_read(u16 reg, u8 *value); +int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value); + +#else + +static inline int ab8500_sysctrl_read(u16 reg, u8 *value) +{ + return 0; +} + +static inline int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) +{ + return 0; +} + +#endif /* CONFIG_AB8500_CORE */ + +static inline int ab8500_sysctrl_set(u16 reg, u8 bits) +{ + return ab8500_sysctrl_write(reg, bits, bits); +} + +static inline int ab8500_sysctrl_clear(u16 reg, u8 bits) +{ + return ab8500_sysctrl_write(reg, bits, 0); +} + +/* Registers */ +#define AB8500_TURNONSTATUS 0x100 +#define AB8500_RESETSTATUS 0x101 +#define AB8500_PONKEY1PRESSSTATUS 0x102 +#define AB8500_SYSCLKREQSTATUS 0x142 +#define AB8500_STW4500CTRL1 0x180 +#define AB8500_STW4500CTRL2 0x181 +#define AB8500_STW4500CTRL3 0x200 +#define AB8500_MAINWDOGCTRL 0x201 +#define AB8500_MAINWDOGTIMER 0x202 +#define AB8500_LOWBAT 0x203 +#define AB8500_BATTOK 0x204 +#define AB8500_SYSCLKTIMER 0x205 +#define AB8500_SMPSCLKCTRL 0x206 +#define AB8500_SMPSCLKSEL1 0x207 +#define AB8500_SMPSCLKSEL2 0x208 +#define AB8500_SMPSCLKSEL3 0x209 +#define AB8500_SYSULPCLKCONF 0x20A +#define AB8500_SYSULPCLKCTRL1 0x20B +#define AB8500_SYSCLKCTRL 0x20C +#define AB8500_SYSCLKREQ1VALID 0x20D +#define AB8500_SYSTEMCTRLSUP 0x20F +#define AB8500_SYSCLKREQ1RFCLKBUF 0x210 +#define AB8500_SYSCLKREQ2RFCLKBUF 0x211 +#define AB8500_SYSCLKREQ3RFCLKBUF 0x212 +#define AB8500_SYSCLKREQ4RFCLKBUF 0x213 +#define AB8500_SYSCLKREQ5RFCLKBUF 0x214 +#define AB8500_SYSCLKREQ6RFCLKBUF 0x215 +#define AB8500_SYSCLKREQ7RFCLKBUF 0x216 +#define AB8500_SYSCLKREQ8RFCLKBUF 0x217 +#define AB8500_DITHERCLKCTRL 0x220 +#define AB8500_SWATCTRL 0x230 +#define AB8500_HIQCLKCTRL 0x232 +#define AB8500_VSIMSYSCLKCTRL 0x233 + +/* Bits */ +#define AB8500_TURNONSTATUS_PORNVBAT BIT(0) +#define AB8500_TURNONSTATUS_PONKEY1DBF BIT(1) +#define AB8500_TURNONSTATUS_PONKEY2DBF BIT(2) +#define AB8500_TURNONSTATUS_RTCALARM BIT(3) +#define AB8500_TURNONSTATUS_MAINCHDET BIT(4) +#define AB8500_TURNONSTATUS_VBUSDET BIT(5) +#define AB8500_TURNONSTATUS_USBIDDETECT BIT(6) + +#define AB8500_RESETSTATUS_RESETN4500NSTATUS BIT(0) +#define AB8500_RESETSTATUS_SWRESETN4500NSTATUS BIT(2) + +#define AB8500_PONKEY1PRESSSTATUS_PONKEY1PRESSTIME_MASK 0x7F +#define AB8500_PONKEY1PRESSSTATUS_PONKEY1PRESSTIME_SHIFT 0 + +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ1STATUS BIT(0) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ2STATUS BIT(1) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ3STATUS BIT(2) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ4STATUS BIT(3) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ5STATUS BIT(4) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ6STATUS BIT(5) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ7STATUS BIT(6) +#define AB8500_SYSCLKREQSTATUS_SYSCLKREQ8STATUS BIT(7) + +#define AB8500_STW4500CTRL1_SWOFF BIT(0) +#define AB8500_STW4500CTRL1_SWRESET4500N BIT(1) +#define AB8500_STW4500CTRL1_THDB8500SWOFF BIT(2) + +#define AB8500_STW4500CTRL2_RESETNVAUX1VALID BIT(0) +#define AB8500_STW4500CTRL2_RESETNVAUX2VALID BIT(1) +#define AB8500_STW4500CTRL2_RESETNVAUX3VALID BIT(2) +#define AB8500_STW4500CTRL2_RESETNVMODVALID BIT(3) +#define AB8500_STW4500CTRL2_RESETNVEXTSUPPLY1VALID BIT(4) +#define AB8500_STW4500CTRL2_RESETNVEXTSUPPLY2VALID BIT(5) +#define AB8500_STW4500CTRL2_RESETNVEXTSUPPLY3VALID BIT(6) +#define AB8500_STW4500CTRL2_RESETNVSMPS1VALID BIT(7) + +#define AB8500_STW4500CTRL3_CLK32KOUT2DIS BIT(0) +#define AB8500_STW4500CTRL3_RESETAUDN BIT(1) +#define AB8500_STW4500CTRL3_RESETDENCN BIT(2) +#define AB8500_STW4500CTRL3_THSDENA BIT(3) + +#define AB8500_MAINWDOGCTRL_MAINWDOGENA BIT(0) +#define AB8500_MAINWDOGCTRL_MAINWDOGKICK BIT(1) +#define AB8500_MAINWDOGCTRL_WDEXPTURNONVALID BIT(4) + +#define AB8500_MAINWDOGTIMER_MAINWDOGTIMER_MASK 0x7F +#define AB8500_MAINWDOGTIMER_MAINWDOGTIMER_SHIFT 0 + +#define AB8500_LOWBAT_LOWBATENA BIT(0) +#define AB8500_LOWBAT_LOWBAT_MASK 0x7E +#define AB8500_LOWBAT_LOWBAT_SHIFT 1 + +#define AB8500_BATTOK_BATTOKSEL0THF_MASK 0x0F +#define AB8500_BATTOK_BATTOKSEL0THF_SHIFT 0 +#define AB8500_BATTOK_BATTOKSEL1THF_MASK 0xF0 +#define AB8500_BATTOK_BATTOKSEL1THF_SHIFT 4 + +#define AB8500_SYSCLKTIMER_SYSCLKTIMER_MASK 0x0F +#define AB8500_SYSCLKTIMER_SYSCLKTIMER_SHIFT 0 +#define AB8500_SYSCLKTIMER_SYSCLKTIMERADJ_MASK 0xF0 +#define AB8500_SYSCLKTIMER_SYSCLKTIMERADJ_SHIFT 4 + +#define AB8500_SMPSCLKCTRL_SMPSCLKINTSEL_MASK 0x03 +#define AB8500_SMPSCLKCTRL_SMPSCLKINTSEL_SHIFT 0 +#define AB8500_SMPSCLKCTRL_3M2CLKINTENA BIT(2) + +#define AB8500_SMPSCLKSEL1_VARMCLKSEL_MASK 0x07 +#define AB8500_SMPSCLKSEL1_VARMCLKSEL_SHIFT 0 +#define AB8500_SMPSCLKSEL1_VAPECLKSEL_MASK 0x38 +#define AB8500_SMPSCLKSEL1_VAPECLKSEL_SHIFT 3 + +#define AB8500_SMPSCLKSEL2_VMODCLKSEL_MASK 0x07 +#define AB8500_SMPSCLKSEL2_VMODCLKSEL_SHIFT 0 +#define AB8500_SMPSCLKSEL2_VSMPS1CLKSEL_MASK 0x38 +#define AB8500_SMPSCLKSEL2_VSMPS1CLKSEL_SHIFT 3 + +#define AB8500_SMPSCLKSEL3_VSMPS2CLKSEL_MASK 0x07 +#define AB8500_SMPSCLKSEL3_VSMPS2CLKSEL_SHIFT 0 +#define AB8500_SMPSCLKSEL3_VSMPS3CLKSEL_MASK 0x38 +#define AB8500_SMPSCLKSEL3_VSMPS3CLKSEL_SHIFT 3 + +#define AB8500_SYSULPCLKCONF_ULPCLKCONF_MASK 0x03 +#define AB8500_SYSULPCLKCONF_ULPCLKCONF_SHIFT 0 +#define AB8500_SYSULPCLKCONF_CLK27MHZSTRE BIT(2) +#define AB8500_SYSULPCLKCONF_TVOUTCLKDELN BIT(3) +#define AB8500_SYSULPCLKCONF_TVOUTCLKINV BIT(4) +#define AB8500_SYSULPCLKCONF_ULPCLKSTRE BIT(5) +#define AB8500_SYSULPCLKCONF_CLK27MHZBUFENA BIT(6) +#define AB8500_SYSULPCLKCONF_CLK27MHZPDENA BIT(7) + +#define AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK 0x03 +#define AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT 0 +#define AB8500_SYSULPCLKCTRL1_ULPCLKREQ BIT(2) +#define AB8500_SYSULPCLKCTRL1_4500SYSCLKREQ BIT(3) +#define AB8500_SYSULPCLKCTRL1_AUDIOCLKENA BIT(4) +#define AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ BIT(5) +#define AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ BIT(6) +#define AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ BIT(7) + +#define AB8500_SYSCLKCTRL_TVOUTPLLENA BIT(0) +#define AB8500_SYSCLKCTRL_TVOUTCLKENA BIT(1) +#define AB8500_SYSCLKCTRL_USBCLKENA BIT(2) + +#define AB8500_SYSCLKREQ1VALID_SYSCLKREQ1VALID BIT(0) +#define AB8500_SYSCLKREQ1VALID_ULPCLKREQ1VALID BIT(1) +#define AB8500_SYSCLKREQ1VALID_USBSYSCLKREQ1VALID BIT(2) + +#define AB8500_SYSTEMCTRLSUP_EXTSUP12LPNCLKSEL_MASK 0x03 +#define AB8500_SYSTEMCTRLSUP_EXTSUP12LPNCLKSEL_SHIFT 0 +#define AB8500_SYSTEMCTRLSUP_EXTSUP3LPNCLKSEL_MASK 0x0C +#define AB8500_SYSTEMCTRLSUP_EXTSUP3LPNCLKSEL_SHIFT 2 +#define AB8500_SYSTEMCTRLSUP_INTDB8500NOD BIT(4) + +#define AB8500_SYSCLKREQ1RFCLKBUF_SYSCLKREQ1RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ1RFCLKBUF_SYSCLKREQ1RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ1RFCLKBUF_SYSCLKREQ1RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ2RFCLKBUF_SYSCLKREQ2RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ2RFCLKBUF_SYSCLKREQ2RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ2RFCLKBUF_SYSCLKREQ2RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ3RFCLKBUF_SYSCLKREQ3RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ3RFCLKBUF_SYSCLKREQ3RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ3RFCLKBUF_SYSCLKREQ3RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ4RFCLKBUF_SYSCLKREQ4RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ4RFCLKBUF_SYSCLKREQ4RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ4RFCLKBUF_SYSCLKREQ4RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ5RFCLKBUF_SYSCLKREQ5RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ5RFCLKBUF_SYSCLKREQ5RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ5RFCLKBUF_SYSCLKREQ5RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ6RFCLKBUF_SYSCLKREQ6RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ6RFCLKBUF_SYSCLKREQ6RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ6RFCLKBUF_SYSCLKREQ6RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ7RFCLKBUF_SYSCLKREQ7RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ7RFCLKBUF_SYSCLKREQ7RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ7RFCLKBUF_SYSCLKREQ7RFCLKBUF4 BIT(4) + +#define AB8500_SYSCLKREQ8RFCLKBUF_SYSCLKREQ8RFCLKBUF2 BIT(2) +#define AB8500_SYSCLKREQ8RFCLKBUF_SYSCLKREQ8RFCLKBUF3 BIT(3) +#define AB8500_SYSCLKREQ8RFCLKBUF_SYSCLKREQ8RFCLKBUF4 BIT(4) + +#define AB8500_DITHERCLKCTRL_VARMDITHERENA BIT(0) +#define AB8500_DITHERCLKCTRL_VSMPS3DITHERENA BIT(1) +#define AB8500_DITHERCLKCTRL_VSMPS1DITHERENA BIT(2) +#define AB8500_DITHERCLKCTRL_VSMPS2DITHERENA BIT(3) +#define AB8500_DITHERCLKCTRL_VMODDITHERENA BIT(4) +#define AB8500_DITHERCLKCTRL_VAPEDITHERENA BIT(5) +#define AB8500_DITHERCLKCTRL_DITHERDEL_MASK 0xC0 +#define AB8500_DITHERCLKCTRL_DITHERDEL_SHIFT 6 + +#define AB8500_SWATCTRL_UPDATERF BIT(0) +#define AB8500_SWATCTRL_SWATENABLE BIT(1) +#define AB8500_SWATCTRL_RFOFFTIMER_MASK 0x1C +#define AB8500_SWATCTRL_RFOFFTIMER_SHIFT 2 +#define AB8500_SWATCTRL_SWATBIT5 BIT(6) + +#define AB8500_HIQCLKCTRL_SYSCLKREQ1HIQENAVALID BIT(0) +#define AB8500_HIQCLKCTRL_SYSCLKREQ2HIQENAVALID BIT(1) +#define AB8500_HIQCLKCTRL_SYSCLKREQ3HIQENAVALID BIT(2) +#define AB8500_HIQCLKCTRL_SYSCLKREQ4HIQENAVALID BIT(3) +#define AB8500_HIQCLKCTRL_SYSCLKREQ5HIQENAVALID BIT(4) +#define AB8500_HIQCLKCTRL_SYSCLKREQ6HIQENAVALID BIT(5) +#define AB8500_HIQCLKCTRL_SYSCLKREQ7HIQENAVALID BIT(6) +#define AB8500_HIQCLKCTRL_SYSCLKREQ8HIQENAVALID BIT(7) + +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ1VALID BIT(0) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ2VALID BIT(1) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ3VALID BIT(2) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ4VALID BIT(3) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ5VALID BIT(4) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ6VALID BIT(5) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ7VALID BIT(6) +#define AB8500_VSIMSYSCLKCTRL_VSIMSYSCLKREQ8VALID BIT(7) + +#endif /* __AB8500_SYSCTRL_H */ diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 67bd6f7ecf32..7d9b6ae1c203 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -186,7 +186,6 @@ struct abx500_init_settings { 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; }; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 835996e167e1..1408bf8eed5f 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -25,22 +25,20 @@ struct mfd_cell { const char *name; int id; + /* refcounting for multiple drivers to use a single cell */ + atomic_t *usage_count; int (*enable)(struct platform_device *dev); int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); - /* driver-specific data for MFD-aware "cell" drivers */ - void *driver_data; - - /* platform_data can be used to either pass data to "generic" - driver or as a hook to mfd_cell for the "cell" drivers */ - void *platform_data; - size_t data_size; + /* mfd_data can be used to pass data to client drivers */ + void *mfd_data; /* - * This resources can be specified relatively to the parent device. - * For accessing device you should use resources from device + * These resources can be specified relative to the parent device. + * For accessing hardware you should use resources from the platform dev */ int num_resources; const struct resource *resources; @@ -55,11 +53,47 @@ struct mfd_cell { bool pm_runtime_no_callbacks; }; +/* + * Convenience functions for clients using shared cells. Refcounting + * happens automatically, with the cell's enable/disable callbacks + * being called only when a device is first being enabled or no other + * clients are making use of it. + */ +extern int mfd_cell_enable(struct platform_device *pdev); +extern int mfd_cell_disable(struct platform_device *pdev); + +/* + * Given a platform device that's been created by mfd_add_devices(), fetch + * the mfd_cell that created it. + */ +static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev) +{ + return pdev->dev.platform_data; +} + +/* + * Given a platform device that's been created by mfd_add_devices(), fetch + * the .mfd_data entry from the mfd_cell that created it. + */ +static inline void *mfd_get_data(struct platform_device *pdev) +{ + return mfd_get_cell(pdev)->mfd_data; +} + extern int mfd_add_devices(struct device *parent, int id, - const struct mfd_cell *cells, int n_devs, + struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base); extern void mfd_remove_devices(struct device *parent); +/* + * For MFD drivers with clients sharing access to resources, these create + * multiple platform devices per cell. Contention handling must still be + * handled via drivers (ie, with enable/disable hooks). + */ +extern int mfd_shared_platform_driver_register(struct platform_driver *drv, + const char *cellname); +extern void mfd_shared_platform_driver_unregister(struct platform_driver *drv); + #endif diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h new file mode 100644 index 000000000000..93a9477e075f --- /dev/null +++ b/include/linux/mfd/max8997-private.h @@ -0,0 +1,347 @@ +/* + * max8997.h - Voltage regulator driver for the Maxim 8997 + * + * Copyright (C) 2010 Samsung Electrnoics + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_MFD_MAX8997_PRIV_H +#define __LINUX_MFD_MAX8997_PRIV_H + +#include <linux/i2c.h> + +enum max8997_pmic_reg { + MAX8997_REG_PMIC_ID0 = 0x00, + MAX8997_REG_PMIC_ID1 = 0x01, + MAX8997_REG_INTSRC = 0x02, + MAX8997_REG_INT1 = 0x03, + MAX8997_REG_INT2 = 0x04, + MAX8997_REG_INT3 = 0x05, + MAX8997_REG_INT4 = 0x06, + + MAX8997_REG_INT1MSK = 0x08, + MAX8997_REG_INT2MSK = 0x09, + MAX8997_REG_INT3MSK = 0x0a, + MAX8997_REG_INT4MSK = 0x0b, + + MAX8997_REG_STATUS1 = 0x0d, + MAX8997_REG_STATUS2 = 0x0e, + MAX8997_REG_STATUS3 = 0x0f, + MAX8997_REG_STATUS4 = 0x10, + + MAX8997_REG_MAINCON1 = 0x13, + MAX8997_REG_MAINCON2 = 0x14, + MAX8997_REG_BUCKRAMP = 0x15, + + MAX8997_REG_BUCK1CTRL = 0x18, + MAX8997_REG_BUCK1DVS1 = 0x19, + MAX8997_REG_BUCK1DVS2 = 0x1a, + MAX8997_REG_BUCK1DVS3 = 0x1b, + MAX8997_REG_BUCK1DVS4 = 0x1c, + MAX8997_REG_BUCK1DVS5 = 0x1d, + MAX8997_REG_BUCK1DVS6 = 0x1e, + MAX8997_REG_BUCK1DVS7 = 0x1f, + MAX8997_REG_BUCK1DVS8 = 0x20, + MAX8997_REG_BUCK2CTRL = 0x21, + MAX8997_REG_BUCK2DVS1 = 0x22, + MAX8997_REG_BUCK2DVS2 = 0x23, + MAX8997_REG_BUCK2DVS3 = 0x24, + MAX8997_REG_BUCK2DVS4 = 0x25, + MAX8997_REG_BUCK2DVS5 = 0x26, + MAX8997_REG_BUCK2DVS6 = 0x27, + MAX8997_REG_BUCK2DVS7 = 0x28, + MAX8997_REG_BUCK2DVS8 = 0x29, + MAX8997_REG_BUCK3CTRL = 0x2a, + MAX8997_REG_BUCK3DVS = 0x2b, + MAX8997_REG_BUCK4CTRL = 0x2c, + MAX8997_REG_BUCK4DVS = 0x2d, + MAX8997_REG_BUCK5CTRL = 0x2e, + MAX8997_REG_BUCK5DVS1 = 0x2f, + MAX8997_REG_BUCK5DVS2 = 0x30, + MAX8997_REG_BUCK5DVS3 = 0x31, + MAX8997_REG_BUCK5DVS4 = 0x32, + MAX8997_REG_BUCK5DVS5 = 0x33, + MAX8997_REG_BUCK5DVS6 = 0x34, + MAX8997_REG_BUCK5DVS7 = 0x35, + MAX8997_REG_BUCK5DVS8 = 0x36, + MAX8997_REG_BUCK6CTRL = 0x37, + MAX8997_REG_BUCK6BPSKIPCTRL = 0x38, + MAX8997_REG_BUCK7CTRL = 0x39, + MAX8997_REG_BUCK7DVS = 0x3a, + MAX8997_REG_LDO1CTRL = 0x3b, + MAX8997_REG_LDO2CTRL = 0x3c, + MAX8997_REG_LDO3CTRL = 0x3d, + MAX8997_REG_LDO4CTRL = 0x3e, + MAX8997_REG_LDO5CTRL = 0x3f, + MAX8997_REG_LDO6CTRL = 0x40, + MAX8997_REG_LDO7CTRL = 0x41, + MAX8997_REG_LDO8CTRL = 0x42, + MAX8997_REG_LDO9CTRL = 0x43, + MAX8997_REG_LDO10CTRL = 0x44, + MAX8997_REG_LDO11CTRL = 0x45, + MAX8997_REG_LDO12CTRL = 0x46, + MAX8997_REG_LDO13CTRL = 0x47, + MAX8997_REG_LDO14CTRL = 0x48, + MAX8997_REG_LDO15CTRL = 0x49, + MAX8997_REG_LDO16CTRL = 0x4a, + MAX8997_REG_LDO17CTRL = 0x4b, + MAX8997_REG_LDO18CTRL = 0x4c, + MAX8997_REG_LDO21CTRL = 0x4d, + + MAX8997_REG_MBCCTRL1 = 0x50, + MAX8997_REG_MBCCTRL2 = 0x51, + MAX8997_REG_MBCCTRL3 = 0x52, + MAX8997_REG_MBCCTRL4 = 0x53, + MAX8997_REG_MBCCTRL5 = 0x54, + MAX8997_REG_MBCCTRL6 = 0x55, + MAX8997_REG_OTPCGHCVS = 0x56, + + MAX8997_REG_SAFEOUTCTRL = 0x5a, + + MAX8997_REG_LBCNFG1 = 0x5e, + MAX8997_REG_LBCNFG2 = 0x5f, + MAX8997_REG_BBCCTRL = 0x60, + + MAX8997_REG_FLASH1_CUR = 0x63, /* 0x63 ~ 0x6e for FLASH */ + MAX8997_REG_FLASH2_CUR = 0x64, + MAX8997_REG_MOVIE_CUR = 0x65, + MAX8997_REG_GSMB_CUR = 0x66, + MAX8997_REG_BOOST_CNTL = 0x67, + MAX8997_REG_LEN_CNTL = 0x68, + MAX8997_REG_FLASH_CNTL = 0x69, + MAX8997_REG_WDT_CNTL = 0x6a, + MAX8997_REG_MAXFLASH1 = 0x6b, + MAX8997_REG_MAXFLASH2 = 0x6c, + MAX8997_REG_FLASHSTATUS = 0x6d, + MAX8997_REG_FLASHSTATUSMASK = 0x6e, + + MAX8997_REG_GPIOCNTL1 = 0x70, + MAX8997_REG_GPIOCNTL2 = 0x71, + MAX8997_REG_GPIOCNTL3 = 0x72, + MAX8997_REG_GPIOCNTL4 = 0x73, + MAX8997_REG_GPIOCNTL5 = 0x74, + MAX8997_REG_GPIOCNTL6 = 0x75, + MAX8997_REG_GPIOCNTL7 = 0x76, + MAX8997_REG_GPIOCNTL8 = 0x77, + MAX8997_REG_GPIOCNTL9 = 0x78, + MAX8997_REG_GPIOCNTL10 = 0x79, + MAX8997_REG_GPIOCNTL11 = 0x7a, + MAX8997_REG_GPIOCNTL12 = 0x7b, + + MAX8997_REG_LDO1CONFIG = 0x80, + MAX8997_REG_LDO2CONFIG = 0x81, + MAX8997_REG_LDO3CONFIG = 0x82, + MAX8997_REG_LDO4CONFIG = 0x83, + MAX8997_REG_LDO5CONFIG = 0x84, + MAX8997_REG_LDO6CONFIG = 0x85, + MAX8997_REG_LDO7CONFIG = 0x86, + MAX8997_REG_LDO8CONFIG = 0x87, + MAX8997_REG_LDO9CONFIG = 0x88, + MAX8997_REG_LDO10CONFIG = 0x89, + MAX8997_REG_LDO11CONFIG = 0x8a, + MAX8997_REG_LDO12CONFIG = 0x8b, + MAX8997_REG_LDO13CONFIG = 0x8c, + MAX8997_REG_LDO14CONFIG = 0x8d, + MAX8997_REG_LDO15CONFIG = 0x8e, + MAX8997_REG_LDO16CONFIG = 0x8f, + MAX8997_REG_LDO17CONFIG = 0x90, + MAX8997_REG_LDO18CONFIG = 0x91, + MAX8997_REG_LDO21CONFIG = 0x92, + + MAX8997_REG_DVSOKTIMER1 = 0x97, + MAX8997_REG_DVSOKTIMER2 = 0x98, + MAX8997_REG_DVSOKTIMER4 = 0x99, + MAX8997_REG_DVSOKTIMER5 = 0x9a, + + MAX8997_REG_PMIC_END = 0x9b, +}; + +enum max8997_muic_reg { + MAX8997_MUIC_REG_ID = 0x0, + MAX8997_MUIC_REG_INT1 = 0x1, + MAX8997_MUIC_REG_INT2 = 0x2, + MAX8997_MUIC_REG_INT3 = 0x3, + MAX8997_MUIC_REG_STATUS1 = 0x4, + MAX8997_MUIC_REG_STATUS2 = 0x5, + MAX8997_MUIC_REG_STATUS3 = 0x6, + MAX8997_MUIC_REG_INTMASK1 = 0x7, + MAX8997_MUIC_REG_INTMASK2 = 0x8, + MAX8997_MUIC_REG_INTMASK3 = 0x9, + MAX8997_MUIC_REG_CDETCTRL = 0xa, + + MAX8997_MUIC_REG_CONTROL1 = 0xc, + MAX8997_MUIC_REG_CONTROL2 = 0xd, + MAX8997_MUIC_REG_CONTROL3 = 0xe, + + MAX8997_MUIC_REG_END = 0xf, +}; + +enum max8997_haptic_reg { + MAX8997_HAPTIC_REG_GENERAL = 0x00, + MAX8997_HAPTIC_REG_CONF1 = 0x01, + MAX8997_HAPTIC_REG_CONF2 = 0x02, + MAX8997_HAPTIC_REG_DRVCONF = 0x03, + MAX8997_HAPTIC_REG_CYCLECONF1 = 0x04, + MAX8997_HAPTIC_REG_CYCLECONF2 = 0x05, + MAX8997_HAPTIC_REG_SIGCONF1 = 0x06, + MAX8997_HAPTIC_REG_SIGCONF2 = 0x07, + MAX8997_HAPTIC_REG_SIGCONF3 = 0x08, + MAX8997_HAPTIC_REG_SIGCONF4 = 0x09, + MAX8997_HAPTIC_REG_SIGDC1 = 0x0a, + MAX8997_HAPTIC_REG_SIGDC2 = 0x0b, + MAX8997_HAPTIC_REG_SIGPWMDC1 = 0x0c, + MAX8997_HAPTIC_REG_SIGPWMDC2 = 0x0d, + MAX8997_HAPTIC_REG_SIGPWMDC3 = 0x0e, + MAX8997_HAPTIC_REG_SIGPWMDC4 = 0x0f, + MAX8997_HAPTIC_REG_MTR_REV = 0x10, + + MAX8997_HAPTIC_REG_END = 0x11, +}; + +/* slave addr = 0x0c: using "2nd part" of rev4 datasheet */ +enum max8997_rtc_reg { + MAX8997_RTC_CTRLMASK = 0x02, + MAX8997_RTC_CTRL = 0x03, + MAX8997_RTC_UPDATE1 = 0x04, + MAX8997_RTC_UPDATE2 = 0x05, + MAX8997_RTC_WTSR_SMPL = 0x06, + + MAX8997_RTC_SEC = 0x10, + MAX8997_RTC_MIN = 0x11, + MAX8997_RTC_HOUR = 0x12, + MAX8997_RTC_DAY_OF_WEEK = 0x13, + MAX8997_RTC_MONTH = 0x14, + MAX8997_RTC_YEAR = 0x15, + MAX8997_RTC_DAY_OF_MONTH = 0x16, + MAX8997_RTC_ALARM1_SEC = 0x17, + MAX8997_RTC_ALARM1_MIN = 0x18, + MAX8997_RTC_ALARM1_HOUR = 0x19, + MAX8997_RTC_ALARM1_DAY_OF_WEEK = 0x1a, + MAX8997_RTC_ALARM1_MONTH = 0x1b, + MAX8997_RTC_ALARM1_YEAR = 0x1c, + MAX8997_RTC_ALARM1_DAY_OF_MONTH = 0x1d, + MAX8997_RTC_ALARM2_SEC = 0x1e, + MAX8997_RTC_ALARM2_MIN = 0x1f, + MAX8997_RTC_ALARM2_HOUR = 0x20, + MAX8997_RTC_ALARM2_DAY_OF_WEEK = 0x21, + MAX8997_RTC_ALARM2_MONTH = 0x22, + MAX8997_RTC_ALARM2_YEAR = 0x23, + MAX8997_RTC_ALARM2_DAY_OF_MONTH = 0x24, +}; + +enum max8997_irq_source { + PMIC_INT1 = 0, + PMIC_INT2, + PMIC_INT3, + PMIC_INT4, + + FUEL_GAUGE, /* Ignored (MAX17042 driver handles) */ + + MUIC_INT1, + MUIC_INT2, + MUIC_INT3, + + GPIO_LOW, /* Not implemented */ + GPIO_HI, /* Not implemented */ + + FLASH_STATUS, /* Not implemented */ + + MAX8997_IRQ_GROUP_NR, +}; + +enum max8997_irq { + MAX8997_PMICIRQ_PWRONR, + MAX8997_PMICIRQ_PWRONF, + MAX8997_PMICIRQ_PWRON1SEC, + MAX8997_PMICIRQ_JIGONR, + MAX8997_PMICIRQ_JIGONF, + MAX8997_PMICIRQ_LOWBAT2, + MAX8997_PMICIRQ_LOWBAT1, + + MAX8997_PMICIRQ_JIGR, + MAX8997_PMICIRQ_JIGF, + MAX8997_PMICIRQ_MR, + MAX8997_PMICIRQ_DVS1OK, + MAX8997_PMICIRQ_DVS2OK, + MAX8997_PMICIRQ_DVS3OK, + MAX8997_PMICIRQ_DVS4OK, + + MAX8997_PMICIRQ_CHGINS, + MAX8997_PMICIRQ_CHGRM, + MAX8997_PMICIRQ_DCINOVP, + MAX8997_PMICIRQ_TOPOFFR, + MAX8997_PMICIRQ_CHGRSTF, + MAX8997_PMICIRQ_MBCHGTMEXPD, + + MAX8997_PMICIRQ_RTC60S, + MAX8997_PMICIRQ_RTCA1, + MAX8997_PMICIRQ_RTCA2, + MAX8997_PMICIRQ_SMPL_INT, + MAX8997_PMICIRQ_RTC1S, + MAX8997_PMICIRQ_WTSR, + + MAX8997_MUICIRQ_ADCError, + MAX8997_MUICIRQ_ADCLow, + MAX8997_MUICIRQ_ADC, + + MAX8997_MUICIRQ_VBVolt, + MAX8997_MUICIRQ_DBChg, + MAX8997_MUICIRQ_DCDTmr, + MAX8997_MUICIRQ_ChgDetRun, + MAX8997_MUICIRQ_ChgTyp, + + MAX8997_MUICIRQ_OVP, + + MAX8997_IRQ_NR, +}; + +#define MAX8997_REG_BUCK1DVS(x) (MAX8997_REG_BUCK1DVS1 + (x) - 1) +#define MAX8997_REG_BUCK2DVS(x) (MAX8997_REG_BUCK2DVS1 + (x) - 1) +#define MAX8997_REG_BUCK5DVS(x) (MAX8997_REG_BUCK5DVS1 + (x) - 1) + +struct max8997_dev { + struct device *dev; + struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ + struct i2c_client *rtc; /* slave addr 0x0c */ + struct i2c_client *haptic; /* slave addr 0x90 */ + struct i2c_client *muic; /* slave addr 0x4a */ + struct mutex iolock; + + int type; + struct platform_device *battery; /* battery control (not fuel gauge) */ + + bool wakeup; + + /* For hibernation */ + u8 reg_dump[MAX8997_REG_PMIC_END + MAX8997_MUIC_REG_END + + MAX8997_HAPTIC_REG_END]; +}; + +enum max8997_types { + TYPE_MAX8997, + TYPE_MAX8966, +}; + +extern int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +extern int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); +extern int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +extern int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); +extern int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); + +#endif /* __LINUX_MFD_MAX8997_PRIV_H */ diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h new file mode 100644 index 000000000000..cb671b3451bf --- /dev/null +++ b/include/linux/mfd/max8997.h @@ -0,0 +1,114 @@ +/* + * max8997.h - Driver for the Maxim 8997/8966 + * + * Copyright (C) 2009-2010 Samsung Electrnoics + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8998.h + * + * MAX8997 has PMIC, MUIC, HAPTIC, RTC, FLASH, and Fuel Gauge devices. + * Except Fuel Gauge, every device shares the same I2C bus and included in + * this mfd driver. Although the fuel gauge is included in the chip, it is + * excluded from the driver because a) it has a different I2C bus from + * others and b) it can be enabled simply by using MAX17042 driver. + */ + +#ifndef __LINUX_MFD_MAX8998_H +#define __LINUX_MFD_MAX8998_H + +#include <linux/regulator/consumer.h> + +/* MAX8997/8966 regulator IDs */ +enum max8998_regulators { + MAX8997_LDO1 = 0, + MAX8997_LDO2, + MAX8997_LDO3, + MAX8997_LDO4, + MAX8997_LDO5, + MAX8997_LDO6, + MAX8997_LDO7, + MAX8997_LDO8, + MAX8997_LDO9, + MAX8997_LDO10, + MAX8997_LDO11, + MAX8997_LDO12, + MAX8997_LDO13, + MAX8997_LDO14, + MAX8997_LDO15, + MAX8997_LDO16, + MAX8997_LDO17, + MAX8997_LDO18, + MAX8997_LDO21, + MAX8997_BUCK1, + MAX8997_BUCK2, + MAX8997_BUCK3, + MAX8997_BUCK4, + MAX8997_BUCK5, + MAX8997_BUCK6, + MAX8997_BUCK7, + MAX8997_EN32KHZ_AP, + MAX8997_EN32KHZ_CP, + MAX8997_ENVICHG, + MAX8997_ESAFEOUT1, + MAX8997_ESAFEOUT2, + MAX8997_CHARGER_CV, /* control MBCCV of MBCCTRL3 */ + MAX8997_CHARGER, /* charger current, MBCCTRL4 */ + MAX8997_CHARGER_TOPOFF, /* MBCCTRL5 */ + + MAX8997_REG_MAX, +}; + +struct max8997_regulator_data { + int id; + struct regulator_init_data *initdata; +}; + +struct max8997_platform_data { + bool wakeup; + /* IRQ: Not implemented */ + /* ---- PMIC ---- */ + struct max8997_regulator_data *regulators; + int num_regulators; + + /* + * SET1~3 DVS GPIOs control Buck1, 2, and 5 simultaneously. Therefore, + * With buckx_gpiodvs enabled, the buckx cannot be controlled + * independently. To control buckx (of 1, 2, and 5) independently, + * disable buckx_gpiodvs and control with BUCKxDVS1 register. + * + * When buckx_gpiodvs and bucky_gpiodvs are both enabled, set_voltage + * on buckx will change the voltage of bucky at the same time. + * + */ + bool ignore_gpiodvs_side_effect; + int buck125_gpios[3]; /* GPIO of [0]SET1, [1]SET2, [2]SET3 */ + int buck125_default_idx; /* Default value of SET1, 2, 3 */ + unsigned int buck1_voltage[8]; /* buckx_voltage in uV */ + bool buck1_gpiodvs; + unsigned int buck2_voltage[8]; + bool buck2_gpiodvs; + unsigned int buck5_voltage[8]; + bool buck5_gpiodvs; + + /* MUIC: Not implemented */ + /* HAPTIC: Not implemented */ + /* RTC: Not implemented */ + /* Flash: Not implemented */ + /* Charger control: Not implemented */ +}; + +#endif /* __LINUX_MFD_MAX8998_H */ diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index a1d391b40e68..c064beaaccb7 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -146,8 +146,7 @@ struct mc13xxx_platform_data { #define MC13XXX_USE_LED (1 << 5) unsigned int flags; - int num_regulators; - struct mc13xxx_regulator_init_data *regulators; + struct mc13xxx_regulator_platform_data regulators; struct mc13xxx_leds_platform_data *leds; }; diff --git a/include/linux/mfd/tps6105x.h b/include/linux/mfd/tps6105x.h new file mode 100644 index 000000000000..386743dd931c --- /dev/null +++ b/include/linux/mfd/tps6105x.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef MFD_TPS6105X_H +#define MFD_TPS6105X_H + +#include <linux/i2c.h> +#include <linux/regulator/machine.h> + +/* + * Register definitions to all subdrivers + */ +#define TPS6105X_REG_0 0x00 +#define TPS6105X_REG0_MODE_SHIFT 6 +#define TPS6105X_REG0_MODE_MASK (0x03<<6) +/* These defines for both reg0 and reg1 */ +#define TPS6105X_REG0_MODE_SHUTDOWN 0x00 +#define TPS6105X_REG0_MODE_TORCH 0x01 +#define TPS6105X_REG0_MODE_TORCH_FLASH 0x02 +#define TPS6105X_REG0_MODE_VOLTAGE 0x03 +#define TPS6105X_REG0_VOLTAGE_SHIFT 4 +#define TPS6105X_REG0_VOLTAGE_MASK (3<<4) +#define TPS6105X_REG0_VOLTAGE_450 0 +#define TPS6105X_REG0_VOLTAGE_500 1 +#define TPS6105X_REG0_VOLTAGE_525 2 +#define TPS6105X_REG0_VOLTAGE_500_2 3 +#define TPS6105X_REG0_DIMMING_SHIFT 3 +#define TPS6105X_REG0_TORCHC_SHIFT 0 +#define TPS6105X_REG0_TORCHC_MASK (7<<0) +#define TPS6105X_REG0_TORCHC_0 0x00 +#define TPS6105X_REG0_TORCHC_50 0x01 +#define TPS6105X_REG0_TORCHC_75 0x02 +#define TPS6105X_REG0_TORCHC_100 0x03 +#define TPS6105X_REG0_TORCHC_150 0x04 +#define TPS6105X_REG0_TORCHC_200 0x05 +#define TPS6105X_REG0_TORCHC_250_400 0x06 +#define TPS6105X_REG0_TORCHC_250_500 0x07 +#define TPS6105X_REG_1 0x01 +#define TPS6105X_REG1_MODE_SHIFT 6 +#define TPS6105X_REG1_MODE_MASK (0x03<<6) +#define TPS6105X_REG1_MODE_SHUTDOWN 0x00 +#define TPS6105X_REG1_MODE_TORCH 0x01 +#define TPS6105X_REG1_MODE_TORCH_FLASH 0x02 +#define TPS6105X_REG1_MODE_VOLTAGE 0x03 +#define TPS6105X_REG_2 0x02 +#define TPS6105X_REG_3 0x03 + +/** + * enum tps6105x_mode - desired mode for the TPS6105x + * @TPS6105X_MODE_SHUTDOWN: this instance is inactive, not used for anything + * @TPS61905X_MODE_TORCH: this instance is used as a LED, usually a while + * LED, for example as backlight or flashlight. If this is set, the + * TPS6105X will register to the LED framework + * @TPS6105X_MODE_TORCH_FLASH: this instance is used as a flashgun, usually + * in a camera + * @TPS6105X_MODE_VOLTAGE: this instance is used as a voltage regulator and + * will register to the regulator framework + */ +enum tps6105x_mode { + TPS6105X_MODE_SHUTDOWN, + TPS6105X_MODE_TORCH, + TPS6105X_MODE_TORCH_FLASH, + TPS6105X_MODE_VOLTAGE, +}; + +/** + * struct tps6105x_platform_data - TPS61905x platform data + * @mode: what mode this instance shall be operated in, + * this is not selectable at runtime + * @regulator_data: initialization data for the voltage + * regulator if used as a voltage source + */ +struct tps6105x_platform_data { + enum tps6105x_mode mode; + struct regulator_init_data *regulator_data; +}; + +/** + * struct tps6105x - state holder for the TPS6105x drivers + * @mutex: mutex to serialize I2C accesses + * @i2c_client: corresponding I2C client + * @regulator: regulator device if used in voltage mode + */ +struct tps6105x { + struct tps6105x_platform_data *pdata; + struct mutex lock; + struct i2c_client *client; + struct regulator_dev *regulator; +}; + +extern int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value); +extern int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf); +extern int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg, + u8 bitmask, u8 bitvalues); + +#endif diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index 173086d42af4..afe4db49402d 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -104,11 +104,17 @@ struct wm831x_watchdog_pdata { #define WM831X_MAX_ISINK 2 struct wm831x_pdata { + /** Used to distinguish multiple WM831x chips */ + int wm831x_num; + /** Called before subdevices are set up */ int (*pre_init)(struct wm831x *wm831x); /** Called after subdevices are set up */ int (*post_init)(struct wm831x *wm831x); + /** Put the /IRQ line into CMOS mode */ + bool irq_cmos; + int irq_base; int gpio_base; struct wm831x_backlight_pdata *backlight; diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index ef4f0b6083a3..f0b69cdae41c 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -59,7 +59,7 @@ struct wm8994 { int (*read_dev)(struct wm8994 *wm8994, unsigned short reg, int bytes, void *dest); int (*write_dev)(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *src); + int bytes, const void *src); void *control_data; @@ -88,6 +88,8 @@ int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg, unsigned short mask, unsigned short val); int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, int count, u16 *buf); +int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg, + int count, const u16 *buf); /* Helper to save on boilerplate */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index bda221dfaf0a..11fd38151cc9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2737,6 +2737,7 @@ #define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 #define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119 #define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a +#define PCI_DEVICE_ID_INTEL_ITC_LPC 0x8186 #define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 #define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 #define PCI_DEVICE_ID_INTEL_82451NX 0x84ca diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 347a567b01e1..b8066ef10bb0 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -153,7 +153,8 @@ static int cq93vc_resume(struct snd_soc_codec *codec) static int cq93vc_probe(struct snd_soc_codec *codec) { - struct davinci_vc *davinci_vc = snd_soc_codec_get_drvdata(codec); + struct davinci_vc *davinci_vc = + mfd_get_data(to_platform_device(codec->dev)); davinci_vc->cq93vc.codec = codec; codec->control_data = davinci_vc; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index e4d464b937d6..8512800f6326 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -26,6 +26,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/i2c/twl.h> #include <linux/slab.h> #include <sound/core.h> @@ -732,7 +733,8 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; + struct twl4030_codec_audio_data *pdata = + mfd_get_data(to_platform_device(codec->dev)); unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -2297,7 +2299,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_audio_data *pdata = mfd_get_data(pdev); if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 861b28f543d2..1ad0d5aecece 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -436,7 +436,8 @@ EXPORT_SYMBOL_GPL(wl1273_get_format); static int wl1273_probe(struct snd_soc_codec *codec) { - struct wl1273_core **core = codec->dev->platform_data; + struct wl1273_core **core = + mfd_get_data(to_platform_device(codec->dev)); struct wl1273_priv *wl1273; int r; diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 3c3bc079167e..736b785e3756 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -22,6 +22,7 @@ #include <linux/regulator/consumer.h> #include <linux/mfd/wm8400-audio.h> #include <linux/mfd/wm8400-private.h> +#include <linux/mfd/core.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -1377,7 +1378,7 @@ static void wm8400_probe_deferred(struct work_struct *work) static int wm8400_codec_probe(struct snd_soc_codec *codec) { - struct wm8400 *wm8400 = dev_get_platdata(codec->dev); + struct wm8400 *wm8400 = mfd_get_data(to_platform_device(codec->dev)); struct wm8400_priv *priv; int ret; u16 reg; diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9d2afccc3a2d..13e05a302a92 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -205,7 +205,7 @@ static struct snd_soc_dai_driver davinci_vcif_dai = { static int davinci_vcif_probe(struct platform_device *pdev) { - struct davinci_vc *davinci_vc = platform_get_drvdata(pdev); + struct davinci_vc *davinci_vc = mfd_get_data(pdev); struct davinci_vcif_dev *davinci_vcif_dev; int ret; |