diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-16 18:55:20 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-16 18:55:20 -0800 |
commit | 2dfea3803dcf70983d14ce1dcbb3e97a7459a28b (patch) | |
tree | 59bffc7389ff554585f79d7cc06021790dc2b317 /drivers/mfd | |
parent | aed606e3bc1f10753254db308d3fd8c053c41328 (diff) | |
parent | 1881b68b8961a86d40c3c5c205e533515a2dc9c6 (diff) |
Merge tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
Pull MFS update from Samuel Ortiz:
"This is the MFD patch set for the 3.8 merge window.
We have several new drivers, most of the time coming with their sub
devices drivers:
- Austria Microsystem's AS3711
- Nano River's viperboard
- TI's TPS80031, AM335x TS/ADC,
- Realtek's MMC/memstick card reader
- Nokia's retu
We also got some notable cleanups and improvements:
- tps6586x got converted to IRQ domains.
- tps65910 and tps65090 moved to the regmap IRQ API.
- STMPE is now Device Tree aware.
- A general twl6040 and twl-core cleanup, with moves to the regmap
I/O and IRQ APIs and a conversion to the recently added PWM
framework.
- sta2x11 gained regmap support.
Then the rest is mostly tiny cleanups and fixes, among which we have
Mark's wm5xxx and wm8xxx patchset."
Far amount of annoying but largely trivial conflicts. Many due to
__devinit/exit removal, others due to one or two of the new drivers also
having come in through another tree.
* tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (119 commits)
mfd: tps6507x: Convert to devm_kzalloc
mfd: stmpe: Update DT support for stmpe driver
mfd: wm5102: Add readback of DSP status 3 register
mfd: arizona: Log if we fail to create the primary IRQ domain
mfd: tps80031: MFD_TPS80031 needs to select REGMAP_IRQ
mfd: tps80031: Add terminating entry for tps80031_id_table
mfd: sta2x11: Fix potential NULL pointer dereference in __sta2x11_mfd_mask()
mfd: wm5102: Add tuning for revision B
mfd: arizona: Defer patch initialistation until after first device boot
mfd: tps65910: Fix wrong ack_base register
mfd: tps65910: Remove unused data
mfd: stmpe: Get rid of irq_invert_polarity
mfd: ab8500-core: Fix invalid free of devm_ allocated data
mfd: wm5102: Mark DSP memory regions as volatile
mfd: wm5102: Correct default for LDO1_CONTROL_2
mfd: arizona: Register haptics devices
mfd: wm8994: Make current device behaviour the default
mfd: tps65090: MFD_TPS65090 needs to select REGMAP_IRQ
mfd: Fix stmpe.c build when OF is not enabled
mfd: jz4740-adc: Use devm_kzalloc
...
Diffstat (limited to 'drivers/mfd')
41 files changed, 3284 insertions, 1734 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b63987c6ed2..1c0abd4dfc4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -104,6 +104,17 @@ config MFD_TI_SSP To compile this driver as a module, choose M here: the module will be called ti-ssp. +config MFD_TI_AM335X_TSCADC + tristate "TI ADC / Touch Screen chip support" + select MFD_CORE + select REGMAP + select REGMAP_MMIO + help + If you say yes here you get support for Texas Instruments series + of Touch Screen /ADC chips. + To compile this driver as a module, choose M here: the + module will be called ti_am335x_tscadc. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM @@ -253,6 +264,20 @@ config MFD_TPS65912_SPI If you say yes here you get support for the TPS65912 series of PM chips with SPI interface. +config MFD_TPS80031 + bool "TI TPS80031/TPS80032 Power Management chips" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + If you say yes here you get support for the Texas Instruments + TPS80031/ TPS80032 Fully Integrated Power Management with Power + Path and Battery Charger. The device provides five configurable + step-down converters, 11 general purpose LDOs, USB OTG Module, + ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with + Power Path from USB, 32K clock generator. + config MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 @@ -309,10 +334,10 @@ config MFD_TWL4030_AUDIO config TWL6040_CORE bool "Support for TWL6040 audio codec" - depends on I2C=y && GENERIC_HARDIRQS + depends on I2C=y select MFD_CORE select REGMAP_I2C - select IRQ_DOMAIN + select REGMAP_IRQ default n help Say yes here if you want support for Texas Instruments TWL6040 audio @@ -990,6 +1015,7 @@ config MFD_TPS65090 depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ help If you say yes here you get support for the TPS65090 series of Power Management chips. @@ -1034,6 +1060,7 @@ config MFD_STA2X11 bool "STA2X11 multi function device support" depends on STA2X11 select MFD_CORE + select REGMAP_MMIO config MFD_SYSCON bool "System Controller Register R/W Based on Regmap" @@ -1053,6 +1080,38 @@ config MFD_PALMAS If you say yes here you get support for the Palmas series of PMIC chips from Texas Instruments. +config MFD_VIPERBOARD + tristate "Support for Nano River Technologies Viperboard" + select MFD_CORE + depends on USB + default n + help + Say yes here if you want support for Nano River Technologies + Viperboard. + There are mfd cell drivers available for i2c master, adc and + both gpios found on the board. The spi part does not yet + have a driver. + You need to select the mfd cell drivers separately. + The drivers do not support all features the board exposes. + +config MFD_RETU + tristate "Support for Retu multi-function device" + select MFD_CORE + depends on I2C + select REGMAP_IRQ + help + Retu is a multi-function device found on Nokia Internet Tablets + (770, N800 and N810). + +config MFD_AS3711 + bool "Support for AS3711" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y + help + Support for the AS3711 PMIC from AMS + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 69f260ae022..8b977f8045a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o +obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o obj-$(CONFIG_MFD_STMPE) += stmpe.o @@ -55,18 +56,19 @@ obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o -obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o +obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65912-objs := tps65912-core.o tps65912-irq.o obj-$(CONFIG_MFD_TPS65912) += tps65912.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o +obj-$(CONFIG_MFD_TPS80031) += tps80031.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_MFD_TWL4030_AUDIO) += twl4030-audio.o -obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o +obj-$(CONFIG_TWL6040_CORE) += twl6040.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o @@ -89,6 +91,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o +obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o @@ -137,8 +140,11 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o obj-$(CONFIG_MFD_PALMAS) += palmas.o +obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o +obj-$(CONFIG_MFD_RETU) += retu-mfd.o +obj-$(CONFIG_MFD_AS3711) += as3711.o diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 59da1650fb8..e1650badd10 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -586,38 +586,6 @@ int ab8500_suspend(struct ab8500 *ab8500) return 0; } -/* AB8500 GPIO Resources */ -static struct resource __devinitdata ab8500_gpio_resources[] = { - { - .name = "GPIO_INT6", - .start = AB8500_INT_GPIO6R, - .end = AB8500_INT_GPIO41F, - .flags = IORESOURCE_IRQ, - } -}; - -/* AB9540 GPIO Resources */ -static struct resource __devinitdata ab9540_gpio_resources[] = { - { - .name = "GPIO_INT6", - .start = AB8500_INT_GPIO6R, - .end = AB8500_INT_GPIO41F, - .flags = IORESOURCE_IRQ, - }, - { - .name = "GPIO_INT14", - .start = AB9540_INT_GPIO50R, - .end = AB9540_INT_GPIO54R, - .flags = IORESOURCE_IRQ, - }, - { - .name = "GPIO_INT15", - .start = AB9540_INT_GPIO50F, - .end = AB9540_INT_GPIO54F, - .flags = IORESOURCE_IRQ, - } -}; - static struct resource ab8500_gpadc_resources[] = { { .name = "HW_CONV_END", @@ -979,6 +947,10 @@ static struct mfd_cell abx500_common_devs[] = { .of_compatible = "stericsson,ab8500-regulator", }, { + .name = "abx500-clk", + .of_compatible = "stericsson,abx500-clk", + }, + { .name = "ab8500-gpadc", .of_compatible = "stericsson,ab8500-gpadc", .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), @@ -1080,8 +1052,6 @@ static struct mfd_cell ab8500_devs[] = { { .name = "ab8500-gpio", .of_compatible = "stericsson,ab8500-gpio", - .num_resources = ARRAY_SIZE(ab8500_gpio_resources), - .resources = ab8500_gpio_resources, }, { .name = "ab8500-usb", @@ -1098,8 +1068,6 @@ static struct mfd_cell ab8500_devs[] = { static struct mfd_cell ab9540_devs[] = { { .name = "ab8500-gpio", - .num_resources = ARRAY_SIZE(ab9540_gpio_resources), - .resources = ab9540_gpio_resources, }, { .name = "ab9540-usb", @@ -1284,7 +1252,7 @@ static int ab8500_probe(struct platform_device *pdev) int i; u8 value; - ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); + ab8500 = devm_kzalloc(&pdev->dev, sizeof *ab8500, GFP_KERNEL); if (!ab8500) return -ENOMEM; @@ -1294,10 +1262,8 @@ static int ab8500_probe(struct platform_device *pdev) ab8500->dev = &pdev->dev; resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!resource) { - ret = -ENODEV; - goto out_free_ab8500; - } + if (!resource) + return -ENODEV; ab8500->irq = resource->start; @@ -1320,7 +1286,7 @@ static int ab8500_probe(struct platform_device *pdev) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_IC_NAME_REG, &value); if (ret < 0) - goto out_free_ab8500; + return ret; ab8500->version = value; } @@ -1328,7 +1294,7 @@ static int ab8500_probe(struct platform_device *pdev) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_REV_REG, &value); if (ret < 0) - goto out_free_ab8500; + return ret; ab8500->chip_id = value; @@ -1345,14 +1311,13 @@ static int ab8500_probe(struct platform_device *pdev) ab8500->mask_size = AB8500_NUM_IRQ_REGS; ab8500->irq_reg_offset = ab8500_irq_regoffset; } - ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL); + ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL); if (!ab8500->mask) return -ENOMEM; - ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL); - if (!ab8500->oldmask) { - ret = -ENOMEM; - goto out_freemask; - } + ab8500->oldmask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL); + if (!ab8500->oldmask) + return -ENOMEM; + /* * ab8500 has switched off due to (SWITCH_OFF_STATUS): * 0x01 Swoff bit programming @@ -1406,37 +1371,37 @@ static int ab8500_probe(struct platform_device *pdev) ret = abx500_register_ops(ab8500->dev, &ab8500_ops); if (ret) - goto out_freeoldmask; + return ret; for (i = 0; i < ab8500->mask_size; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; ret = ab8500_irq_init(ab8500, np); if (ret) - goto out_freeoldmask; + return ret; /* Activate this feature only in ab9540 */ /* till tests are done on ab8500 1p2 or later*/ if (is_ab9540(ab8500)) { - ret = request_threaded_irq(ab8500->irq, NULL, - ab8500_hierarchical_irq, - IRQF_ONESHOT | IRQF_NO_SUSPEND, - "ab8500", ab8500); + ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL, + ab8500_hierarchical_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); } else { - ret = request_threaded_irq(ab8500->irq, NULL, - ab8500_irq, - IRQF_ONESHOT | IRQF_NO_SUSPEND, - "ab8500", ab8500); + ret = devm_request_threaded_irq(&pdev->dev, ab8500->irq, NULL, + ab8500_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); if (ret) - goto out_freeoldmask; + return ret; } ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, ARRAY_SIZE(abx500_common_devs), NULL, ab8500->irq_base, ab8500->domain); if (ret) - goto out_freeirq; + return ret; if (is_ab9540(ab8500)) ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, @@ -1447,14 +1412,14 @@ static int ab8500_probe(struct platform_device *pdev) ARRAY_SIZE(ab8500_devs), NULL, ab8500->irq_base, ab8500->domain); if (ret) - goto out_freeirq; + return ret; if (is_ab9540(ab8500) || is_ab8505(ab8500)) ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs, ARRAY_SIZE(ab9540_ab8505_devs), NULL, ab8500->irq_base, ab8500->domain); if (ret) - goto out_freeirq; + return ret; if (!no_bm) { /* Add battery management devices */ @@ -1475,17 +1440,6 @@ static int ab8500_probe(struct platform_device *pdev) dev_err(ab8500->dev, "error creating sysfs entries\n"); return ret; - -out_freeirq: - free_irq(ab8500->irq, ab8500); -out_freeoldmask: - kfree(ab8500->oldmask); -out_freemask: - kfree(ab8500->mask); -out_free_ab8500: - kfree(ab8500); - - return ret; } static int ab8500_remove(struct platform_device *pdev) @@ -1498,11 +1452,6 @@ static int ab8500_remove(struct platform_device *pdev) sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); mfd_remove_devices(ab8500->dev); - free_irq(ab8500->irq, ab8500); - - kfree(ab8500->oldmask); - kfree(ab8500->mask); - kfree(ab8500); return 0; } diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index c784f4602a7..bc8a3edb6bb 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -292,6 +292,7 @@ int arizona_dev_init(struct arizona *arizona) struct device *dev = arizona->dev; const char *type_name; unsigned int reg, val; + int (*apply_patch)(struct arizona *) = NULL; int ret, i; dev_set_drvdata(arizona->dev, arizona); @@ -391,7 +392,7 @@ int arizona_dev_init(struct arizona *arizona) arizona->type); arizona->type = WM5102; } - ret = wm5102_patch(arizona); + apply_patch = wm5102_patch; break; #endif #ifdef CONFIG_MFD_WM5110 @@ -402,7 +403,7 @@ int arizona_dev_init(struct arizona *arizona) arizona->type); arizona->type = WM5110; } - ret = wm5110_patch(arizona); + apply_patch = wm5110_patch; break; #endif default: @@ -412,9 +413,6 @@ int arizona_dev_init(struct arizona *arizona) dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); - if (ret != 0) - dev_err(arizona->dev, "Failed to apply patch: %d\n", ret); - /* If we have a /RESET GPIO we'll already be reset */ if (!arizona->pdata.reset) { regcache_mark_dirty(arizona->regmap); @@ -438,6 +436,15 @@ int arizona_dev_init(struct arizona *arizona) goto err_reset; } + if (apply_patch) { + ret = apply_patch(arizona); + if (ret != 0) { + dev_err(arizona->dev, "Failed to apply patch: %d\n", + ret); + goto err_reset; + } + } + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { if (!arizona->pdata.gpio_defaults[i]) continue; diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index b1b00917740..74713bf5371 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -224,6 +224,7 @@ int arizona_irq_init(struct arizona *arizona) arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops, arizona); if (!arizona->virq) { + dev_err(arizona->dev, "Failed to add core IRQ domain\n"); ret = -EINVAL; goto err; } diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c new file mode 100644 index 00000000000..e994c969112 --- /dev/null +++ b/drivers/mfd/as3711.c @@ -0,0 +1,217 @@ +/* + * AS3711 PMIC MFC driver + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/as3711.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +enum { + AS3711_REGULATOR, + AS3711_BACKLIGHT, +}; + +/* + * Ok to have it static: it is only used during probing and multiple I2C devices + * cannot be probed simultaneously. Just make sure to avoid stale data. + */ +static struct mfd_cell as3711_subdevs[] = { + [AS3711_REGULATOR] = {.name = "as3711-regulator",}, + [AS3711_BACKLIGHT] = {.name = "as3711-backlight",}, +}; + +static bool as3711_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AS3711_GPIO_SIGNAL_IN: + case AS3711_INTERRUPT_STATUS_1: + case AS3711_INTERRUPT_STATUS_2: + case AS3711_INTERRUPT_STATUS_3: + case AS3711_CHARGER_STATUS_1: + case AS3711_CHARGER_STATUS_2: + case AS3711_REG_STATUS: + return true; + } + return false; +} + +static bool as3711_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AS3711_INTERRUPT_STATUS_1: + case AS3711_INTERRUPT_STATUS_2: + case AS3711_INTERRUPT_STATUS_3: + return true; + } + return false; +} + +static bool as3711_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AS3711_SD_1_VOLTAGE: + case AS3711_SD_2_VOLTAGE: + case AS3711_SD_3_VOLTAGE: + case AS3711_SD_4_VOLTAGE: + case AS3711_LDO_1_VOLTAGE: + case AS3711_LDO_2_VOLTAGE: + case AS3711_LDO_3_VOLTAGE: + case AS3711_LDO_4_VOLTAGE: + case AS3711_LDO_5_VOLTAGE: + case AS3711_LDO_6_VOLTAGE: + case AS3711_LDO_7_VOLTAGE: + case AS3711_LDO_8_VOLTAGE: + case AS3711_SD_CONTROL: + case AS3711_GPIO_SIGNAL_OUT: + case AS3711_GPIO_SIGNAL_IN: + case AS3711_SD_CONTROL_1: + case AS3711_SD_CONTROL_2: + case AS3711_CURR_CONTROL: + case AS3711_CURR1_VALUE: + case AS3711_CURR2_VALUE: + case AS3711_CURR3_VALUE: + case AS3711_STEPUP_CONTROL_1: + case AS3711_STEPUP_CONTROL_2: + case AS3711_STEPUP_CONTROL_4: + case AS3711_STEPUP_CONTROL_5: + case AS3711_REG_STATUS: + case AS3711_INTERRUPT_STATUS_1: + case AS3711_INTERRUPT_STATUS_2: + case AS3711_INTERRUPT_STATUS_3: + case AS3711_CHARGER_STATUS_1: + case AS3711_CHARGER_STATUS_2: + case AS3711_ASIC_ID_1: + case AS3711_ASIC_ID_2: + return true; + } + return false; +} + +static const struct regmap_config as3711_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = as3711_volatile_reg, + .readable_reg = as3711_readable_reg, + .precious_reg = as3711_precious_reg, + .max_register = AS3711_MAX_REGS, + .num_reg_defaults_raw = AS3711_MAX_REGS, + .cache_type = REGCACHE_RBTREE, +}; + +static int as3711_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct as3711 *as3711; + struct as3711_platform_data *pdata = client->dev.platform_data; + unsigned int id1, id2; + int ret; + + if (!pdata) + dev_dbg(&client->dev, "Platform data not found\n"); + + as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL); + if (!as3711) { + dev_err(&client->dev, "Memory allocation failed\n"); + return -ENOMEM; + } + + as3711->dev = &client->dev; + i2c_set_clientdata(client, as3711); + + if (client->irq) + dev_notice(&client->dev, "IRQ not supported yet\n"); + + as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config); + if (IS_ERR(as3711->regmap)) { + ret = PTR_ERR(as3711->regmap); + dev_err(&client->dev, "regmap initialization failed: %d\n", ret); + return ret; + } + + ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_1, &id1); + if (!ret) + ret = regmap_read(as3711->regmap, AS3711_ASIC_ID_2, &id2); + if (ret < 0) { + dev_err(&client->dev, "regmap_read() failed: %d\n", ret); + return ret; + } + if (id1 != 0x8b) + return -ENODEV; + dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2); + + /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */ + if (pdata) { + as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator; + as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator); + as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight; + as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight); + } else { + as3711_subdevs[AS3711_REGULATOR].platform_data = NULL; + as3711_subdevs[AS3711_REGULATOR].pdata_size = 0; + as3711_subdevs[AS3711_BACKLIGHT].platform_data = NULL; + as3711_subdevs[AS3711_BACKLIGHT].pdata_size = 0; + } + + ret = mfd_add_devices(as3711->dev, -1, as3711_subdevs, + ARRAY_SIZE(as3711_subdevs), NULL, 0, NULL); + if (ret < 0) + dev_err(&client->dev, "add mfd devices failed: %d\n", ret); + + return ret; +} + +static int as3711_i2c_remove(struct i2c_client *client) +{ + struct as3711 *as3711 = i2c_get_clientdata(client); + + mfd_remove_devices(as3711->dev); + return 0; +} + +static const struct i2c_device_id as3711_i2c_id[] = { + {.name = "as3711", .driver_data = 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, as3711_i2c_id); + +static struct i2c_driver as3711_i2c_driver = { + .driver = { + .name = "as3711", + .owner = THIS_MODULE, + }, + .probe = as3711_i2c_probe, + .remove = as3711_i2c_remove, + .id_table = as3711_i2c_id, +}; + +static int __init as3711_i2c_init(void) +{ + return i2c_add_driver(&as3711_i2c_driver); +} +/* Initialise early */ +subsys_initcall(as3711_i2c_init); + +static void __exit as3711_i2c_exit(void) +{ + i2c_del_driver(&as3711_i2c_driver); +} +module_exit(as3711_i2c_exit); + +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_DESCRIPTION("AS3711 PMIC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 689b747416a..a3c9613f916 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -15,7 +15,6 @@ #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/module.h> @@ -24,16 +23,6 @@ #include <linux/mfd/da9052/pdata.h> #include <linux/mfd/da9052/reg.h> -#define DA9052_NUM_IRQ_REGS 4 -#define DA9052_IRQ_MASK_POS_1 0x01 -#define DA9052_IRQ_MASK_POS_2 0x02 -#define DA9052_IRQ_MASK_POS_3 0x04 -#define DA9052_IRQ_MASK_POS_4 0x08 -#define DA9052_IRQ_MASK_POS_5 0x10 -#define DA9052_IRQ_MASK_POS_6 0x20 -#define DA9052_IRQ_MASK_POS_7 0x40 -#define DA9052_IRQ_MASK_POS_8 0x80 - static bool da9052_reg_readable(struct device *dev, unsigned int reg) { switch (reg) { @@ -425,15 +414,6 @@ err: } EXPORT_SYMBOL_GPL(da9052_adc_manual_read); -static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data) -{ - struct da9052 *da9052 = irq_data; - - complete(&da9052->done); - - return IRQ_HANDLED; -} - int da9052_adc_read_temp(struct da9052 *da9052) { int tbat; @@ -447,74 +427,6 @@ int da9052_adc_read_temp(struct da9052 *da9052) } EXPORT_SYMBOL_GPL(da9052_adc_read_temp); -static struct resource da9052_rtc_resource = { - .name = "ALM", - .start = DA9052_IRQ_ALARM, - .end = DA9052_IRQ_ALARM, - .flags = IORESOURCE_IRQ, -}; - -static struct resource da9052_onkey_resource = { - .name = "ONKEY", - .start = DA9052_IRQ_NONKEY, - .end = DA9052_IRQ_NONKEY, - .flags = IORESOURCE_IRQ, -}; - -static struct resource da9052_bat_resources[] = { - { - .name = "BATT TEMP", - .start = DA9052_IRQ_TBAT, - .end = DA9052_IRQ_TBAT, - .flags = IORESOURCE_IRQ, - }, - { - .name = "DCIN DET", - .start = DA9052_IRQ_DCIN, - .end = DA9052_IRQ_DCIN, - .flags = IORESOURCE_IRQ, - }, - { - .name = "DCIN REM", - .start = DA9052_IRQ_DCINREM, - .end = DA9052_IRQ_DCINREM, - .flags = IORESOURCE_IRQ, - }, - { - .name = "VBUS DET", - .start = DA9052_IRQ_VBUS, - .end = DA9052_IRQ_VBUS, - .flags = IORESOURCE_IRQ, - }, - { - .name = "VBUS REM", - .start = DA9052_IRQ_VBUSREM, - .end = DA9052_IRQ_VBUSREM, - .flags = IORESOURCE_IRQ, - }, - { - .name = "CHG END", - .start = DA9052_IRQ_CHGEND, - .end = DA9052_IRQ_CHGEND, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct resource da9052_tsi_resources[] = { - { - .name = "PENDWN", - .start = DA9052_IRQ_PENDOWN, - .end = DA9052_IRQ_PENDOWN, - .flags = IORESOURCE_IRQ, - }, - { - .name = "TSIRDY", - .start = DA9052_IRQ_TSIREADY, - .end = DA9052_IRQ_TSIREADY, - .flags = IORESOURCE_IRQ, - }, -}; - static struct mfd_cell da9052_subdev_info[] = { { .name = "da9052-regulator", @@ -574,13 +486,9 @@ static struct mfd_cell da9052_subdev_info[] = { }, { .name = "da9052-onkey", - .resources = &da9052_onkey_resource, - .num_resources = 1, }, { .name = "da9052-rtc", - .resources = &da9052_rtc_resource, - .num_resources = 1, }, { .name = "da9052-gpio", @@ -602,160 +510,15 @@ static struct mfd_cell da9052_subdev_info[] = { }, { .name = "da9052-tsi", - .resources = da9052_tsi_resources, - .num_resources = ARRAY_SIZE(da9052_tsi_resources), }, { .name = "da9052-bat", - .resources = da9052_bat_resources, - .num_resources = ARRAY_SIZE(da9052_bat_resources), }, { .name = "da9052-watchdog", }, }; -static struct regmap_irq da9052_irqs[] = { - [DA9052_IRQ_DCIN] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_VBUS] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_DCINREM] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_VBUSREM] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_VDDLOW] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_ALARM] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_SEQRDY] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_COMP1V2] = { - .reg_offset = 0, - .mask = DA9052_IRQ_MASK_POS_8, - }, - [DA9052_IRQ_NONKEY] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_IDFLOAT] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_IDGND] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_CHGEND] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_TBAT] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_ADC_EOM] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_PENDOWN] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_TSIREADY] = { - .reg_offset = 1, - .mask = DA9052_IRQ_MASK_POS_8, - }, - [DA9052_IRQ_GPI0] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_GPI1] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_GPI2] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_GPI3] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_GPI4] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_GPI5] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_GPI6] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_GPI7] = { - .reg_offset = 2, - .mask = DA9052_IRQ_MASK_POS_8, - }, - [DA9052_IRQ_GPI8] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_1, - }, - [DA9052_IRQ_GPI9] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_2, - }, - [DA9052_IRQ_GPI10] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_3, - }, - [DA9052_IRQ_GPI11] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_4, - }, - [DA9052_IRQ_GPI12] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_5, - }, - [DA9052_IRQ_GPI13] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_6, - }, - [DA9052_IRQ_GPI14] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_7, - }, - [DA9052_IRQ_GPI15] = { - .reg_offset = 3, - .mask = DA9052_IRQ_MASK_POS_8, - }, -}; - -static struct regmap_irq_chip da9052_regmap_irq_chip = { - .name = "da9052_irq", - .status_base = DA9052_EVENT_A_REG, - .mask_base = DA9052_IRQ_MASK_A_REG, - .ack_base = DA9052_EVENT_A_REG, - .num_regs = DA9052_NUM_IRQ_REGS, - .irqs = da9052_irqs, - .num_irqs = ARRAY_SIZE(da9052_irqs), -}; - struct regmap_config da9052_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -782,45 +545,31 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id) da9052->chip_id = chip_id; - if (!pdata || !pdata->irq_base) - da9052->irq_base = -1; - else - da9052->irq_base = pdata->irq_base; - - ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - da9052->irq_base, &da9052_regmap_irq_chip, - &da9052->irq_data); - if (ret < 0) - goto regmap_err; - - da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data); - - ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "adc irq", da9052); - if (ret != 0) - dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret); + ret = da9052_irq_init(da9052); + if (ret != 0) { + dev_err(da9052->dev, "da9052_irq_init failed: %d\n", ret); + return ret; + } ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL); - if (ret) + if (ret) { + dev_err(da9052->dev, "mfd_add_devices failed: %d\n", ret); goto err; + } return 0; err: - free_irq(DA9052_IRQ_ADC_EOM, da9052); - mfd_remove_devices(da9052->dev); -regmap_err: + da9052_irq_exit(da9052); + return ret; } void da9052_device_exit(struct da9052 *da9052) { - free_irq(DA9052_IRQ_ADC_EOM, da9052); - regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); mfd_remove_devices(da9052->dev); + da9052_irq_exit(da9052); } MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c new file mode 100644 index 00000000000..57ae7841f53 --- /dev/null +++ b/drivers/mfd/da9052-irq.c @@ -0,0 +1,288 @@ +/* + * DA9052 interrupt support + * + * Author: Fabio Estevam <fabio.estevam@freescale.com> + * Based on arizona-irq.c, which is: + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.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. + */ + +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +#define DA9052_NUM_IRQ_REGS 4 +#define DA9052_IRQ_MASK_POS_1 0x01 +#define DA9052_IRQ_MASK_POS_2 0x02 +#define DA9052_IRQ_MASK_POS_3 0x04 +#define DA9052_IRQ_MASK_POS_4 0x08 +#define DA9052_IRQ_MASK_POS_5 0x10 +#define DA9052_IRQ_MASK_POS_6 0x20 +#define DA9052_IRQ_MASK_POS_7 0x40 +#define DA9052_IRQ_MASK_POS_8 0x80 + +static struct regmap_irq da9052_irqs[] = { + [DA9052_IRQ_DCIN] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_VBUS] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_DCINREM] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_VBUSREM] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_VDDLOW] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_ALARM] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_SEQRDY] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_COMP1V2] = { + .reg_offset = 0, + .mask = DA9052_IRQ_MASK_POS_8, + }, + [DA9052_IRQ_NONKEY] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_IDFLOAT] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_IDGND] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_CHGEND] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_TBAT] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_ADC_EOM] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_PENDOWN] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_TSIREADY] = { + .reg_offset = 1, + .mask = DA9052_IRQ_MASK_POS_8, + }, + [DA9052_IRQ_GPI0] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_GPI1] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_GPI2] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_GPI3] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_GPI4] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_GPI5] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_GPI6] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_GPI7] = { + .reg_offset = 2, + .mask = DA9052_IRQ_MASK_POS_8, + }, + [DA9052_IRQ_GPI8] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_1, + }, + [DA9052_IRQ_GPI9] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_2, + }, + [DA9052_IRQ_GPI10] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_3, + }, + [DA9052_IRQ_GPI11] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_4, + }, + [DA9052_IRQ_GPI12] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_5, + }, + [DA9052_IRQ_GPI13] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_6, + }, + [DA9052_IRQ_GPI14] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_7, + }, + [DA9052_IRQ_GPI15] = { + .reg_offset = 3, + .mask = DA9052_IRQ_MASK_POS_8, + }, +}; + +static struct regmap_irq_chip da9052_regmap_irq_chip = { + .name = "da9052_irq", + .status_base = DA9052_EVENT_A_REG, + .mask_base = DA9052_IRQ_MASK_A_REG, + .ack_base = DA9052_EVENT_A_REG, + .num_regs = DA9052_NUM_IRQ_REGS, + .irqs = da9052_irqs, + .num_irqs = ARRAY_SIZE(da9052_irqs), +}; + +static int da9052_map_irq(struct da9052 *da9052, int irq) +{ + return regmap_irq_get_virq(da9052->irq_data, irq); +} + +int da9052_enable_irq(struct da9052 *da9052, int irq) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + enable_irq(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(da9052_enable_irq); + +int da9052_disable_irq(struct da9052 *da9052, int irq) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + disable_irq(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(da9052_disable_irq); + +int da9052_disable_irq_nosync(struct da9052 *da9052, int irq) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + disable_irq_nosync(irq); + + return 0; +} +EXPORT_SYMBOL_GPL(da9052_disable_irq_nosync); + +int da9052_request_irq(struct da9052 *da9052, int irq, char *name, + irq_handler_t handler, void *data) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return irq; + + return request_threaded_irq(irq, NULL, handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + name, data); +} +EXPORT_SYMBOL_GPL(da9052_request_irq); + +void da9052_free_irq(struct da9052 *da9052, int irq, void *data) +{ + irq = da9052_map_irq(da9052, irq); + if (irq < 0) + return; + + free_irq(irq, data); +} +EXPORT_SYMBOL_GPL(da9052_free_irq); + +static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data) +{ + struct da9052 *da9052 = irq_data; + + complete(&da9052->done); + + return IRQ_HANDLED; +} + +int da9052_irq_init(struct da9052 *da9052) +{ + int ret; + + ret = regmap_add_irq_chip(da9052->regmap, da9052->chip_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + -1, &da9052_regmap_irq_chip, + &da9052->irq_data); + if (ret < 0) { + dev_err(da9052->dev, "regmap_add_irq_chip failed: %d\n", ret); + goto regmap_err; + } + + ret = da9052_request_irq(da9052, DA9052_IRQ_ADC_EOM, "adc-irq", + da9052_auxadc_irq, da9052); + + if (ret != 0) { + dev_err(da9052->dev, "DA9052_IRQ_ADC_EOM failed: %d\n", ret); + goto request_irq_err; + } + + return 0; + +request_irq_err: + regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); +regmap_err: + return ret; + +} + +int da9052_irq_exit(struct da9052 *da9052) +{ + da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM , da9052); + regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data); + + return 0; +} diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 29710565a08..dc8826d8d69 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2763,7 +2763,7 @@ static int db8500_irq_init(struct device_node *np) void __init db8500_prcmu_early_init(void) { - if (cpu_is_u8500v2()) { + if (cpu_is_u8500v2() || cpu_is_u9540()) { void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K); if (tcpm_base != NULL) { @@ -2781,7 +2781,11 @@ void __init db8500_prcmu_early_init(void) iounmap(tcpm_base); } - tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); + if (cpu_is_u9540()) + tcdm_base = ioremap_nocache(U8500_PRCMU_TCDM_BASE, + SZ_4K + SZ_8K) + SZ_8K; + else + tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE); } else { pr_err("prcmu: Unsupported chip version\n"); BUG(); diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 0b8b55bb9b1..e80587f1a79 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -211,7 +211,7 @@ static int jz4740_adc_probe(struct platform_device *pdev) int ret; int irq_base; - adc = kmalloc(sizeof(*adc), GFP_KERNEL); + adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL); if (!adc) { dev_err(&pdev->dev, "Failed to allocate driver structure\n"); return -ENOMEM; @@ -221,30 +221,27 @@ static int jz4740_adc_probe(struct platform_device *pdev) if (adc->irq < 0) { ret = adc->irq; dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); - goto err_free; + return ret; } irq_base = platform_get_irq(pdev, 1); if (irq_base < 0) { - ret = irq_base; - dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret); - goto err_free; + dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base); + return irq_base; } mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_base) { - ret = -ENOENT; dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); - goto err_free; + return -ENOENT; } /* Only request the shared registers for the MFD driver */ adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS, pdev->name); if (!adc->mem) { - ret = -EBUSY; dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - goto err_free; + return -EBUSY; } adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem)); @@ -301,9 +298,6 @@ err_iounmap: iounmap(adc->base); err_release_mem_region: release_mem_region(adc->mem->start, resource_size(adc->mem)); -err_free: - kfree(adc); - return ret; } @@ -325,8 +319,6 @@ static int jz4740_adc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - kfree(adc); - return 0; } diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 2ad24caa07d..d9d930302e9 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -734,7 +734,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev, pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { - dev_err(&dev->dev, "I/O space for ACPI uninitialized\n"); + dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); lpc_ich_cells[LPC_GPIO].num_resources--; goto gpe0_done; } @@ -760,7 +760,7 @@ gpe0_done: pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { - dev_err(&dev->dev, "I/O space for GPIO uninitialized\n"); + dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n"); ret = -ENODEV; goto gpio_done; } @@ -810,7 +810,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev, pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { - dev_err(&dev->dev, "I/O space for ACPI uninitialized\n"); + dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); ret = -ENODEV; goto wdt_done; } @@ -830,12 +830,15 @@ static int lpc_ich_init_wdt(struct pci_dev *dev, * we have to read RCBA from PCI Config space 0xf0 and use * it as base. GCS = RCBA + ICH6_GCS(0x3410). */ - if (lpc_chipset_info[id->driver_data].iTCO_version == 2) { + if (lpc_chipset_info[id->driver_data].iTCO_version == 1) { + /* Don't register iomem for TCO ver 1 */ + lpc_ich_cells[LPC_WDT].num_resources--; + } else { pci_read_config_dword(dev, RCBABASE, &base_addr_cfg); base_addr = base_addr_cfg & 0xffffc000; if (!(base_addr_cfg & 1)) { - pr_err("RCBA is disabled by hardware/BIOS, " - "device disabled\n"); + dev_notice(&dev->dev, "RCBA is disabled by " + "hardware/BIOS, device disabled\n"); ret = -ENODEV; goto wdt_done; } @@ -871,6 +874,7 @@ static int lpc_ich_probe(struct pci_dev *dev, * successfully. */ if (!cell_added) { + dev_warn(&dev->dev, "No MFD cells added\n"); lpc_ich_restore_config_space(dev); return -ENODEV; } diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 1aba0238f42..2a9b100c482 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -119,6 +119,11 @@ #define MC13XXX_REVISION_FAB (0x03 << 11) #define MC13XXX_REVISION_ICIDCODE (0x3f << 13) +#define MC34708_REVISION_REVMETAL (0x07 << 0) +#define MC34708_REVISION_REVFULL (0x07 << 3) +#define MC34708_REVISION_FIN (0x07 << 6) +#define MC34708_REVISION_FAB (0x07 << 9) + #define MC13XXX_ADC1 44 #define MC13XXX_ADC1_ADEN (1 << 0) #define MC13XXX_ADC1_RAND (1 << 1) @@ -410,62 +415,52 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data) return IRQ_RETVAL(handled); } -static const char *mc13xxx_chipname[] = { - [MC13XXX_ID_MC13783] = "mc13783", - [MC13XXX_ID_MC13892] = "mc13892", -}; - #define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask)) -static int mc13xxx_identify(struct mc13xxx *mc13xxx) +static void mc13xxx_print_revision(struct mc13xxx *mc13xxx, u32 revision) { - u32 icid; - u32 revision; - int ret; - - /* - * Get the generation ID from register 46, as apparently some older - * IC revisions only have this info at this location. Newer ICs seem to - * have both. - */ - ret = mc13xxx_reg_read(mc13xxx, 46, &icid); - if (ret) - return ret; + dev_info(mc13xxx->dev, "%s: rev: %d.%d, " + "fin: %d, fab: %d, icid: %d/%d\n", + mc13xxx->variant->name, + maskval(revision, MC13XXX_REVISION_REVFULL), + maskval(revision, MC13XXX_REVISION_REVMETAL), + maskval(revision, MC13XXX_REVISION_FIN), + maskval(revision, MC13XXX_REVISION_FAB), + maskval(revision, MC13XXX_REVISION_ICID), + maskval(revision, MC13XXX_REVISION_ICIDCODE)); +} - icid = (icid >> 6) & 0x7; +static void mc34708_print_revision(struct mc13xxx *mc13xxx, u32 revision) +{ + dev_info(mc13xxx->dev, "%s: rev %d.%d, fin: %d, fab: %d\n", + mc13xxx->variant->name, + maskval(revision, MC34708_REVISION_REVFULL), + maskval(revision, MC34708_REVISION_REVMETAL), + maskval(revision, MC34708_REVISION_FIN), + maskval(revision, MC34708_REVISION_FAB)); +} - switch (icid) { - case 2: - mc13xxx->ictype = MC13XXX_ID_MC13783; - break; - case 7: - mc13xxx->ictype = MC13XXX_ID_MC13892; - break; - default: - mc13xxx->ictype = MC13XXX_ID_INVALID; - break; - } +/* These are only exported for mc13xxx-i2c and mc13xxx-spi */ +struct mc13xxx_variant mc13xxx_variant_mc13783 = { + .name = "mc13783", + .print_revision = mc13xxx_print_revision, +}; +EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13783); - if (mc13xxx->ictype == MC13XXX_ID_MC13783 || - mc13xxx->ictype == MC13XXX_ID_MC13892) { - ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); - - dev_info(mc13xxx->dev, "%s: rev: %d.%d, " - "fin: %d, fab: %d, icid: %d/%d\n", - mc13xxx_chipname[mc13xxx->ictype], - maskval(revision, MC13XXX_REVISION_REVFULL), - maskval(revision, MC13XXX_REVISION_REVMETAL), - maskval(revision, MC13XXX_REVISION_FIN), - maskval(revision, MC13XXX_REVISION_FAB), - maskval(revision, MC13XXX_REVISION_ICID), - maskval(revision, MC13XXX_REVISION_ICIDCODE)); - } +struct mc13xxx_variant mc13xxx_variant_mc13892 = { + .name = "mc13892", + .print_revision = mc13xxx_print_revision, +}; +EXPORT_SYMBOL_GPL(mc13xxx_variant_mc13892); - return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0; -} +struct mc13xxx_variant mc13xxx_variant_mc34708 = { + .name = "mc34708", + .print_revision = mc34708_print_revision, +}; +EXPORT_SYMBOL_GPL(mc13xxx_variant_mc34708); static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx) { - return mc13xxx_chipname[mc13xxx->ictype]; + return mc13xxx->variant->name; } int mc13xxx_get_flags(struct mc13xxx *mc13xxx) @@ -653,13 +648,16 @@ int mc13xxx_common_init(struct mc13xxx *mc13xxx, struct mc13xxx_platform_data *pdata, int irq) { int ret; + u32 revision; mc13xxx_lock(mc13xxx); - ret = mc13xxx_identify(mc13xxx); + ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); if (ret) goto err_revision; + mc13xxx->variant->print_revision(mc13xxx, revision); + /* mask all irqs */ ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff); if (ret) diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index 7957999f30b..f745e27ee87 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -24,7 +24,10 @@ static const struct i2c_device_id mc13xxx_i2c_device_id[] = { { .name = "mc13892", - .driver_data = MC13XXX_ID_MC13892, + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892, + }, { + .name = "mc34708", + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708, }, { /* sentinel */ } @@ -34,7 +37,10 @@ MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id); static const struct of_device_id mc13xxx_dt_ids[] = { { .compatible = "fsl,mc13892", - .data = (void *) &mc13xxx_i2c_device_id[0], + .data = &mc13xxx_variant_mc13892, + }, { + .compatible = "fsl,mc34708", + .data = &mc13xxx_variant_mc34708, }, { /* sentinel */ } @@ -76,11 +82,15 @@ static int mc13xxx_i2c_probe(struct i2c_client *client, return ret; } - ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); + if (client->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(mc13xxx_dt_ids, &client->dev); + mc13xxx->variant = of_id->data; + } else { + mc13xxx->variant = (void *)id->driver_data; + } - if (ret == 0 && (id->driver_data != mc13xxx->ictype)) - dev_warn(mc13xxx->dev, - "device id doesn't match auto detection!\n"); + ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); return ret; } diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index cb32f69d80b..3032bae20b6 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -28,10 +28,13 @@ static const struct spi_device_id mc13xxx_device_id[] = { { .name = "mc13783", - .driver_data = MC13XXX_ID_MC13783, + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13783, }, { .name = "mc13892", - .driver_data = MC13XXX_ID_MC13892, + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc13892, + }, { + .name = "mc34708", + .driver_data = (kernel_ulong_t)&mc13xxx_variant_mc34708, }, { /* sentinel */ } @@ -39,8 +42,9 @@ static const struct spi_device_id mc13xxx_device_id[] = { MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); static const struct of_device_id mc13xxx_dt_ids[] = { - { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, }, - { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, }, + { .compatible = "fsl,mc13783", .data = &mc13xxx_variant_mc13783, }, + { .compatible = "fsl,mc13892", .data = &mc13xxx_variant_mc13892, }, + { .compatible = "fsl,mc34708", .data = &mc13xxx_variant_mc34708, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); @@ -144,19 +148,18 @@ static int mc13xxx_spi_probe(struct spi_device *spi) return ret; } - ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq); + if (spi->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(mc13xxx_dt_ids, &spi->dev); - if (ret) { - dev_set_drvdata(&spi->dev, NULL); + mc13xxx->variant = of_id->data; } else { - const struct spi_device_id *devid = - spi_get_device_id(spi); - if (!devid || devid->driver_data != mc13xxx->ictype) - dev_warn(mc13xxx->dev, - "device id doesn't match auto detection!\n"); + const struct spi_device_id *id_entry = spi_get_device_id(spi); + + mc13xxx->variant = (void *)id_entry->driver_data; } - return ret; + return mc13xxx_common_init(mc13xxx, pdata, spi->irq); } static int mc13xxx_spi_remove(struct spi_device *spi) diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h index bbba06feea0..460ec5c7b18 100644 --- a/drivers/mfd/mc13xxx.h +++ b/drivers/mfd/mc13xxx.h @@ -13,19 +13,25 @@ #include <linux/regmap.h> #include <linux/mfd/mc13xxx.h> -enum mc13xxx_id { - MC13XXX_ID_MC13783, - MC13XXX_ID_MC13892, - MC13XXX_ID_INVALID, +#define MC13XXX_NUMREGS 0x3f + +struct mc13xxx; + +struct mc13xxx_variant { + const char *name; + void (*print_revision)(struct mc13xxx *mc13xxx, u32 revision); }; -#define MC13XXX_NUMREGS 0x3f +extern struct mc13xxx_variant + mc13xxx_variant_mc13783, + mc13xxx_variant_mc13892, + mc13xxx_variant_mc34708; struct mc13xxx { struct regmap *regmap; struct device *dev; - enum mc13xxx_id ictype; + const struct mc13xxx_variant *variant; struct mutex lock; int irq; diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index f8b77711ad2..7604f4e5df4 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -21,6 +21,10 @@ #include <linux/irqdomain.h> #include <linux/of.h> +static struct device_type mfd_dev_type = { + .name = "mfd_device", +}; + int mfd_cell_enable(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); @@ -91,6 +95,7 @@ static int mfd_add_device(struct device *parent, int id, goto fail_device; pdev->dev.parent = parent; + pdev->dev.type = &mfd_dev_type; if (parent->of_node && cell->of_compatible) { for_each_child_of_node(parent->of_node, np) { @@ -204,10 +209,16 @@ EXPORT_SYMBOL(mfd_add_devices); static int mfd_remove_devices_fn(struct device *dev, void *c) { - struct platform_device *pdev = to_platform_device(dev); - const struct mfd_cell *cell = mfd_get_cell(pdev); + struct platform_device *pdev; + const struct mfd_cell *cell; atomic_t **usage_count = c; + if (dev->type != &mfd_dev_type) + return 0; + + pdev = to_platform_device(dev); + cell = mfd_get_cell(pdev); + /* find the base address of usage_count pointers (for freeing) */ if (!*usage_count || (cell->usage_count < *usage_count)) *usage_count = cell->usage_count; diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c index fe00cdd6f83..b41db596870 100644 --- a/drivers/mfd/rc5t583-irq.c +++ b/drivers/mfd/rc5t583-irq.c @@ -345,7 +345,7 @@ int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base) mutex_init(&rc5t583->irq_lock); /* Initailize all int register to 0 */ - for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) { + for (i = 0; i < RC5T583_MAX_INTERRUPT_EN_REGS; i++) { ret = rc5t583_write(rc5t583->dev, irq_en_add[i], rc5t583->irq_en_reg[i]); if (ret < 0) diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c new file mode 100644 index 00000000000..7ff4a37ab0c --- /dev/null +++ b/drivers/mfd/retu-mfd.c @@ -0,0 +1,264 @@ +/* + * Retu MFD driver + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Based on code written by Juha Yrjölä, David Weinehall and Mikko Ylinen. + * Rewritten by Aaro Koskinen. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/mfd/core.h> +#include <linux/mfd/retu.h> +#include <linux/interrupt.h> +#include <linux/moduleparam.h> + +/* Registers */ +#define RETU_REG_ASICR 0x00 /* ASIC ID and revision */ +#define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */ +#define RETU_REG_IDR 0x01 /* Interrupt ID */ +#define RETU_REG_IMR 0x02 /* Interrupt mask */ + +/* Interrupt sources */ +#define RETU_INT_PWR 0 /* Power button */ + +struct retu_dev { + struct regmap *regmap; + struct device *dev; + struct mutex mutex; + struct regmap_irq_chip_data *irq_data; +}; + +static struct resource retu_pwrbutton_res[] = { + { + .name = "retu-pwrbutton", + .start = RETU_INT_PWR, + .end = RETU_INT_PWR, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell retu_devs[] = { + { + .name = "retu-wdt" + }, + { + .name = "retu-pwrbutton", + .resources = retu_pwrbutton_res, + .num_resources = ARRAY_SIZE(retu_pwrbutton_res), + } +}; + +static struct regmap_irq retu_irqs[] = { + [RETU_INT_PWR] = { + .mask = 1 << RETU_INT_PWR, + } +}; + +static struct regmap_irq_chip retu_irq_chip = { + .name = "RETU", + .irqs = retu_irqs, + .num_irqs = ARRAY_SIZE(retu_irqs), + .num_regs = 1, + .status_base = RETU_REG_IDR, + .mask_base = RETU_REG_IMR, + .ack_base = RETU_REG_IDR, +}; + +/* Retu device registered for the power off. */ +static struct retu_dev *retu_pm_power_off; + +int retu_read(struct retu_dev *rdev, u8 reg) +{ + int ret; + int value; + + mutex_lock(&rdev->mutex); + ret = regmap_read(rdev->regmap, reg, &value); + mutex_unlock(&rdev->mutex); + + return ret ? ret : value; +} +EXPORT_SYMBOL_GPL(retu_read); + +int retu_write(struct retu_dev *rdev, u8 reg, u16 data) +{ + int ret; + + mutex_lock(&rdev->mutex); + ret = regmap_write(rdev->regmap, reg, data); + mutex_unlock(&rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(retu_write); + +static void retu_power_off(void) +{ + struct retu_dev *rdev = retu_pm_power_off; + int reg; + + mutex_lock(&retu_pm_power_off->mutex); + + /* Ignore power button state */ + regmap_read(rdev->regmap, RETU_REG_CC1, ®); + regmap_write(rdev->regmap, RETU_REG_CC1, reg | 2); + + /* Expire watchdog immediately */ + regmap_write(rdev->regmap, RETU_REG_WATCHDOG, 0); + + /* Wait for poweroff */ + for (;;) + cpu_relax(); + + mutex_unlock(&retu_pm_power_off->mutex); +} + +static int retu_regmap_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + int ret; + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + BUG_ON(reg_size != 1 || val_size != 2); + + ret = i2c_smbus_read_word_data(i2c, *(u8 const *)reg); + if (ret < 0) + return ret; + + *(u16 *)val = ret; + return 0; +} + +static int retu_regmap_write(void *context, const void *data, size_t count) +{ + u8 reg; + u16 val; + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + BUG_ON(count != sizeof(reg) + sizeof(val)); + memcpy(®, data, sizeof(reg)); + memcpy(&val, data + sizeof(reg), sizeof(val)); + return i2c_smbus_write_word_data(i2c, reg, val); +} + +static struct regmap_bus retu_bus = { + .read = retu_regmap_read, + .write = retu_regmap_write, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +static struct regmap_config retu_config = { + .reg_bits = 8, + .val_bits = 16, +}; + +static int __devinit retu_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct retu_dev *rdev; + int ret; + + rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL); + if (rdev == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rdev); + rdev->dev = &i2c->dev; + mutex_init(&rdev->mutex); + rdev->regmap = devm_regmap_init(&i2c->dev, &retu_bus, &i2c->dev, + &retu_config); + if (IS_ERR(rdev->regmap)) + return PTR_ERR(rdev->regmap); + + ret = retu_read(rdev, RETU_REG_ASICR); + if (ret < 0) { + dev_err(rdev->dev, "could not read Retu revision: %d\n", ret); + return ret; + } + + dev_info(rdev->dev, "Retu%s v%d.%d found\n", + (ret & RETU_REG_ASICR_VILMA) ? " & Vilma" : "", + (ret >> 4) & 0x7, ret & 0xf); + + /* Mask all RETU interrupts. */ + ret = retu_write(rdev, RETU_REG_IMR, 0xffff); + if (ret < 0) + return ret; + + ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1, + &retu_irq_chip, &rdev->irq_data); + if (ret < 0) + return ret; + + ret = mfd_add_devices(rdev->dev, -1, retu_devs, ARRAY_SIZE(retu_devs), + NULL, regmap_irq_chip_get_base(rdev->irq_data), + NULL); + if (ret < 0) { + regmap_del_irq_chip(i2c->irq, rdev->irq_data); + return ret; + } + + if (!pm_power_off) { + retu_pm_power_off = rdev; + pm_power_off = retu_power_off; + } + + return 0; +} + +static int __devexit retu_remove(struct i2c_client *i2c) +{ + struct retu_dev *rdev = i2c_get_clientdata(i2c); + + if (retu_pm_power_off == rdev) { + pm_power_off = NULL; + retu_pm_power_off = NULL; + } + mfd_remove_devices(rdev->dev); + regmap_del_irq_chip(i2c->irq, rdev->irq_data); + + return 0; +} + +static const struct i2c_device_id retu_id[] = { + { "retu-mfd", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, retu_id); + +static struct i2c_driver retu_driver = { + .driver = { + .name = "retu-mfd", + .owner = THIS_MODULE, + }, + .probe = retu_probe, + .remove = retu_remove, + .id_table = retu_id, +}; +module_i2c_driver(retu_driver); + +MODULE_DESCRIPTION("Retu MFD driver"); +MODULE_AUTHOR("Juha Yrjölä"); +MODULE_AUTHOR("David Weinehall"); +MODULE_AUTHOR("Mikko Ylinen"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 56d4377c62c..3a44efa2920 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -22,6 +22,7 @@ #include <linux/pci.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/highmem.h> #include <linux/interrupt.h> diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index c901fa50fea..0dd84e99081 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -24,67 +24,67 @@ static struct regmap_irq s2mps11_irqs[] = { [S2MPS11_IRQ_PWRONF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_PWRONF_MASK, }, [S2MPS11_IRQ_PWRONR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_PWRONR_MASK, }, [S2MPS11_IRQ_JIGONBF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_JIGONBF_MASK, }, [S2MPS11_IRQ_JIGONBR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_JIGONBR_MASK, }, [S2MPS11_IRQ_ACOKBF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_ACOKBF_MASK, }, [S2MPS11_IRQ_ACOKBR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_ACOKBR_MASK, }, [S2MPS11_IRQ_PWRON1S] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_PWRON1S_MASK, }, [S2MPS11_IRQ_MRB] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S2MPS11_IRQ_MRB_MASK, }, [S2MPS11_IRQ_RTC60S] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTC60S_MASK, }, [S2MPS11_IRQ_RTCA1] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTCA1_MASK, }, [S2MPS11_IRQ_RTCA2] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTCA2_MASK, }, [S2MPS11_IRQ_SMPL] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_SMPL_MASK, }, [S2MPS11_IRQ_RTC1S] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_RTC1S_MASK, }, [S2MPS11_IRQ_WTSR] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S2MPS11_IRQ_WTSR_MASK, }, [S2MPS11_IRQ_INT120C] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S2MPS11_IRQ_INT120C_MASK, }, [S2MPS11_IRQ_INT140C] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S2MPS11_IRQ_INT140C_MASK, }, }; @@ -92,146 +92,146 @@ static struct regmap_irq s2mps11_irqs[] = { static struct regmap_irq s5m8767_irqs[] = { [S5M8767_IRQ_PWRR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_PWRR_MASK, }, [S5M8767_IRQ_PWRF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_PWRF_MASK, }, [S5M8767_IRQ_PWR1S] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_PWR1S_MASK, }, [S5M8767_IRQ_JIGR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_JIGR_MASK, }, [S5M8767_IRQ_JIGF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_JIGF_MASK, }, [S5M8767_IRQ_LOWBAT2] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_LOWBAT2_MASK, }, [S5M8767_IRQ_LOWBAT1] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8767_IRQ_LOWBAT1_MASK, }, [S5M8767_IRQ_MRB] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_MRB_MASK, }, [S5M8767_IRQ_DVSOK2] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_DVSOK2_MASK, }, [S5M8767_IRQ_DVSOK3] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_DVSOK3_MASK, }, [S5M8767_IRQ_DVSOK4] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8767_IRQ_DVSOK4_MASK, }, [S5M8767_IRQ_RTC60S] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTC60S_MASK, }, [S5M8767_IRQ_RTCA1] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTCA1_MASK, }, [S5M8767_IRQ_RTCA2] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTCA2_MASK, }, [S5M8767_IRQ_SMPL] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_SMPL_MASK, }, [S5M8767_IRQ_RTC1S] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_RTC1S_MASK, }, [S5M8767_IRQ_WTSR] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8767_IRQ_WTSR_MASK, }, }; static struct regmap_irq s5m8763_irqs[] = { [S5M8763_IRQ_DCINF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_DCINF_MASK, }, [S5M8763_IRQ_DCINR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_DCINR_MASK, }, [S5M8763_IRQ_JIGF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_JIGF_MASK, }, [S5M8763_IRQ_JIGR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_JIGR_MASK, }, [S5M8763_IRQ_PWRONF] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_PWRONF_MASK, }, [S5M8763_IRQ_PWRONR] = { - .reg_offset = 1, + .reg_offset = 0, .mask = S5M8763_IRQ_PWRONR_MASK, }, [S5M8763_IRQ_WTSREVNT] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_WTSREVNT_MASK, }, [S5M8763_IRQ_SMPLEVNT] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_SMPLEVNT_MASK, }, [S5M8763_IRQ_ALARM1] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_ALARM1_MASK, }, [S5M8763_IRQ_ALARM0] = { - .reg_offset = 2, + .reg_offset = 1, .mask = S5M8763_IRQ_ALARM0_MASK, }, [S5M8763_IRQ_ONKEY1S] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_ONKEY1S_MASK, }, [S5M8763_IRQ_TOPOFFR] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_TOPOFFR_MASK, }, [S5M8763_IRQ_DCINOVPR] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_DCINOVPR_MASK, }, [S5M8763_IRQ_CHGRSTF] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_CHGRSTF_MASK, }, [S5M8763_IRQ_DONER] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_DONER_MASK, }, [S5M8763_IRQ_CHGFAULT] = { - .reg_offset = 3, + .reg_offset = 2, .mask = S5M8763_IRQ_CHGFAULT_MASK, }, [S5M8763_IRQ_LOBAT1] = { - .reg_offset = 4, + .reg_offset = 3, .mask = S5M8763_IRQ_LOBAT1_MASK, }, [S5M8763_IRQ_LOBAT2] = { - .reg_offset = 4, + .reg_offset = 3, .mask = S5M8763_IRQ_LOBAT2_MASK, }, }; diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index d6284cacd27..1225dcbcfcf 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2009-2011 Wind River Systems, Inc. - * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini) + * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini, Davide Ciminaghi) * * 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 @@ -27,21 +27,28 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/pci.h> -#include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/platform_device.h> #include <linux/mfd/core.h> #include <linux/mfd/sta2x11-mfd.h> +#include <linux/regmap.h> #include <asm/sta2x11.h> +static inline int __reg_within_range(unsigned int r, + unsigned int start, + unsigned int end) +{ + return ((r >= start) && (r <= end)); +} + /* This describes STA2X11 MFD chip for us, we may have several */ struct sta2x11_mfd { struct sta2x11_instance *instance; - spinlock_t lock; + struct regmap *regmap[sta2x11_n_mfd_plat_devs]; + spinlock_t lock[sta2x11_n_mfd_plat_devs]; struct list_head list; - void __iomem *sctl_regs; - void __iomem *apbreg_regs; + void __iomem *regs[sta2x11_n_mfd_plat_devs]; }; static LIST_HEAD(sta2x11_mfd_list); @@ -71,6 +78,7 @@ static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) { + int i; struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); struct sta2x11_instance *instance; @@ -83,7 +91,8 @@ static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) if (!mfd) return -ENOMEM; INIT_LIST_HEAD(&mfd->list); - spin_lock_init(&mfd->lock); + for (i = 0; i < ARRAY_SIZE(mfd->lock); i++) + spin_lock_init(&mfd->lock[i]); mfd->instance = instance; list_add(&mfd->list, &sta2x11_mfd_list); return 0; @@ -100,161 +109,276 @@ static int mfd_remove(struct pci_dev *pdev) return 0; } -/* These two functions are exported and are not expected to fail */ -u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +/* This function is exported and is not expected to fail */ +u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, + enum sta2x11_mfd_plat_dev index) { struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); u32 r; unsigned long flags; + void __iomem *regs; if (!mfd) { dev_warn(&pdev->dev, ": can't access sctl regs\n"); return 0; } - if (!mfd->sctl_regs) { + + regs = mfd->regs[index]; + if (!regs) { dev_warn(&pdev->dev, ": system ctl not initialized\n"); return 0; } - spin_lock_irqsave(&mfd->lock, flags); - r = readl(mfd->sctl_regs + reg); + spin_lock_irqsave(&mfd->lock[index], flags); + r = readl(regs + reg); r &= ~mask; r |= val; if (mask) - writel(r, mfd->sctl_regs + reg); - spin_unlock_irqrestore(&mfd->lock, flags); + writel(r, regs + reg); + spin_unlock_irqrestore(&mfd->lock[index], flags); return r; } -EXPORT_SYMBOL(sta2x11_sctl_mask); +EXPORT_SYMBOL(__sta2x11_mfd_mask); -u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +int sta2x11_mfd_get_regs_data(struct platform_device *dev, + enum sta2x11_mfd_plat_dev index, + void __iomem **regs, + spinlock_t **lock) { - struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); - u32 r; - unsigned long flags; + struct pci_dev *pdev = *(struct pci_dev **)(dev->dev.platform_data); + struct sta2x11_mfd *mfd; - if (!mfd) { - dev_warn(&pdev->dev, ": can't access apb regs\n"); - return 0; - } - if (!mfd->apbreg_regs) { - dev_warn(&pdev->dev, ": apb bridge not initialized\n"); - return 0; - } - spin_lock_irqsave(&mfd->lock, flags); - r = readl(mfd->apbreg_regs + reg); - r &= ~mask; - r |= val; - if (mask) - writel(r, mfd->apbreg_regs + reg); - spin_unlock_irqrestore(&mfd->lock, flags); - return r; + if (!pdev) + return -ENODEV; + mfd = sta2x11_mfd_find(pdev); + if (!mfd) + return -ENODEV; + if (index >= sta2x11_n_mfd_plat_devs) + return -ENODEV; + *regs = mfd->regs[index]; + *lock = &mfd->lock[index]; + pr_debug("%s %d *regs = %p\n", __func__, __LINE__, *regs); + return *regs ? 0 : -ENODEV; } -EXPORT_SYMBOL(sta2x11_apbreg_mask); - -/* Two debugfs files, for our registers (FIXME: one instance only) */ -#define REG(regname) {.name = #regname, .offset = SCTL_ ## regname} -static struct debugfs_reg32 sta2x11_sctl_regs[] = { - REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL), - REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0), - REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1), - REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3), - REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1), - REG(SCCLKSTAT2), REG(SCRSTSTA), -}; -#undef REG +EXPORT_SYMBOL(sta2x11_mfd_get_regs_data); -static struct debugfs_regset32 sctl_regset = { - .regs = sta2x11_sctl_regs, - .nregs = ARRAY_SIZE(sta2x11_sctl_regs), -}; +/* + * Special sta2x11-mfd regmap lock/unlock functions + */ + +static void sta2x11_regmap_lock(void *__lock) +{ + spinlock_t *lock = __lock; + spin_lock(lock); +} -#define REG(regname) {.name = #regname, .offset = regname} -static struct debugfs_reg32 sta2x11_apbreg_regs[] = { - REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC), - REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG), +static void sta2x11_regmap_unlock(void *__lock) +{ + spinlock_t *lock = __lock; + spin_unlock(lock); +} + +/* OTP (one time programmable registers do not require locking */ +static void sta2x11_regmap_nolock(void *__lock) +{ +} + +static const char *sta2x11_mfd_names[sta2x11_n_mfd_plat_devs] = { + [sta2x11_sctl] = STA2X11_MFD_SCTL_NAME, + [sta2x11_apbreg] = STA2X11_MFD_APBREG_NAME, + [sta2x11_apb_soc_regs] = STA2X11_MFD_APB_SOC_REGS_NAME, + [sta2x11_scr] = STA2X11_MFD_SCR_NAME, }; -#undef REG -static struct debugfs_regset32 apbreg_regset = { - .regs = sta2x11_apbreg_regs, - .nregs = ARRAY_SIZE(sta2x11_apbreg_regs), +static bool sta2x11_sctl_writeable_reg(struct device *dev, unsigned int reg) +{ + return !__reg_within_range(reg, SCTL_SCPCIECSBRST, SCTL_SCRSTSTA); +} + +static struct regmap_config sta2x11_sctl_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_lock, + .unlock = sta2x11_regmap_unlock, + .max_register = SCTL_SCRSTSTA, + .writeable_reg = sta2x11_sctl_writeable_reg, }; -static struct dentry *sta2x11_sctl_debugfs; -static struct dentry *sta2x11_apbreg_debugfs; +static bool sta2x11_scr_readable_reg(struct device *dev, unsigned int reg) +{ + return (reg == STA2X11_SECR_CR) || + __reg_within_range(reg, STA2X11_SECR_FVR0, STA2X11_SECR_FVR1); +} -/* Probe for the two platform devices */ -static int sta2x11_sctl_probe(struct platform_device *dev) +static bool sta2x11_scr_writeable_reg(struct device *dev, unsigned int reg) { - struct pci_dev **pdev; - struct sta2x11_mfd *mfd; - struct resource *res; + return false; +} - pdev = dev->dev.platform_data; - mfd = sta2x11_mfd_find(*pdev); - if (!mfd) - return -ENODEV; +static struct regmap_config sta2x11_scr_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_nolock, + .unlock = sta2x11_regmap_nolock, + .max_register = STA2X11_SECR_FVR1, + .readable_reg = sta2x11_scr_readable_reg, + .writeable_reg = sta2x11_scr_writeable_reg, +}; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENOMEM; +static bool sta2x11_apbreg_readable_reg(struct device *dev, unsigned int reg) +{ + /* Two blocks (CAN and MLB, SARAC) 0x100 bytes apart */ + if (reg >= APBREG_BSR_SARAC) + reg -= APBREG_BSR_SARAC; + switch (reg) { + case APBREG_BSR: + case APBREG_PAER: + case APBREG_PWAC: + case APBREG_PRAC: + case APBREG_PCG: + case APBREG_PUR: + case APBREG_EMU_PCG: + return true; + default: + return false; + } +} - if (!request_mem_region(res->start, resource_size(res), - "sta2x11-sctl")) - return -EBUSY; +static bool sta2x11_apbreg_writeable_reg(struct device *dev, unsigned int reg) +{ + if (reg >= APBREG_BSR_SARAC) + reg -= APBREG_BSR_SARAC; + if (!sta2x11_apbreg_readable_reg(dev, reg)) + return false; + return reg != APBREG_PAER; +} - mfd->sctl_regs = ioremap(res->start, resource_size(res)); - if (!mfd->sctl_regs) { - release_mem_region(res->start, resource_size(res)); - return -ENOMEM; +static struct regmap_config sta2x11_apbreg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_lock, + .unlock = sta2x11_regmap_unlock, + .max_register = APBREG_EMU_PCG_SARAC, + .readable_reg = sta2x11_apbreg_readable_reg, + .writeable_reg = sta2x11_apbreg_writeable_reg, +}; + +static bool sta2x11_apb_soc_regs_readable_reg(struct device *dev, + unsigned int reg) +{ + return reg <= PCIE_SoC_INT_ROUTER_STATUS3_REG || + __reg_within_range(reg, DMA_IP_CTRL_REG, SPARE3_RESERVED) || + __reg_within_range(reg, MASTER_LOCK_REG, + SYSTEM_CONFIG_STATUS_REG) || + reg == MSP_CLK_CTRL_REG || + __reg_within_range(reg, COMPENSATION_REG1, TEST_CTL_REG); +} + +static bool sta2x11_apb_soc_regs_writeable_reg(struct device *dev, + unsigned int reg) +{ + if (!sta2x11_apb_soc_regs_readable_reg(dev, reg)) + return false; + switch (reg) { + case PCIE_COMMON_CLOCK_CONFIG_0_4_0: + case SYSTEM_CONFIG_STATUS_REG: + case COMPENSATION_REG1: + case PCIE_SoC_INT_ROUTER_STATUS0_REG...PCIE_SoC_INT_ROUTER_STATUS3_REG: + case PCIE_PM_STATUS_0_PORT_0_4...PCIE_PM_STATUS_7_0_EP4: + return false; + default: + return true; } - sctl_regset.base = mfd->sctl_regs; - sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl", - S_IFREG | S_IRUGO, - NULL, &sctl_regset); - return 0; } -static int sta2x11_apbreg_probe(struct platform_device *dev) +static struct regmap_config sta2x11_apb_soc_regs_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .lock = sta2x11_regmap_lock, + .unlock = sta2x11_regmap_unlock, + .max_register = TEST_CTL_REG, + .readable_reg = sta2x11_apb_soc_regs_readable_reg, + .writeable_reg = sta2x11_apb_soc_regs_writeable_reg, +}; + +static struct regmap_config * +sta2x11_mfd_regmap_configs[sta2x11_n_mfd_plat_devs] = { + [sta2x11_sctl] = &sta2x11_sctl_regmap_config, + [sta2x11_apbreg] = &sta2x11_apbreg_regmap_config, + [sta2x11_apb_soc_regs] = &sta2x11_apb_soc_regs_regmap_config, + [sta2x11_scr] = &sta2x11_scr_regmap_config, +}; + +/* Probe for the four platform devices */ + +static int sta2x11_mfd_platform_probe(struct platform_device *dev, + enum sta2x11_mfd_plat_dev index) { struct pci_dev **pdev; struct sta2x11_mfd *mfd; struct resource *res; + const char *name = sta2x11_mfd_names[index]; + struct regmap_config *regmap_config = sta2x11_mfd_regmap_configs[index]; pdev = dev->dev.platform_data; - dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev); - dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev); - mfd = sta2x11_mfd_find(*pdev); if (!mfd) return -ENODEV; + if (!regmap_config) + return -ENODEV; res = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!res) return -ENOMEM; - if (!request_mem_region(res->start, resource_size(res), - "sta2x11-apbreg")) + if (!request_mem_region(res->start, resource_size(res), name)) return -EBUSY; - mfd->apbreg_regs = ioremap(res->start, resource_size(res)); - if (!mfd->apbreg_regs) { + mfd->regs[index] = ioremap(res->start, resource_size(res)); + if (!mfd->regs[index]) { release_mem_region(res->start, resource_size(res)); return -ENOMEM; } - dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs); + regmap_config->lock_arg = &mfd->lock; + /* + No caching, registers could be reached both via regmap and via + void __iomem * + */ + regmap_config->cache_type = REGCACHE_NONE; + mfd->regmap[index] = devm_regmap_init_mmio(&dev->dev, mfd->regs[index], + regmap_config); + WARN_ON(!mfd->regmap[index]); - apbreg_regset.base = mfd->apbreg_regs; - sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg", - S_IFREG | S_IRUGO, - NULL, &apbreg_regset); return 0; } -/* The two platform drivers */ +static int sta2x11_sctl_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_sctl); +} + +static int sta2x11_apbreg_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_apbreg); +} + +static int sta2x11_apb_soc_regs_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_apb_soc_regs); +} + +static int sta2x11_scr_probe(struct platform_device *dev) +{ + return sta2x11_mfd_platform_probe(dev, sta2x11_scr); +} + +/* The three platform drivers */ static struct platform_driver sta2x11_sctl_platform_driver = { .driver = { - .name = "sta2x11-sctl", + .name = STA2X11_MFD_SCTL_NAME, .owner = THIS_MODULE, }, .probe = sta2x11_sctl_probe, @@ -268,7 +392,7 @@ static int __init sta2x11_sctl_init(void) static struct platform_driver sta2x11_platform_driver = { .driver = { - .name = "sta2x11-apbreg", + .name = STA2X11_MFD_APBREG_NAME, .owner = THIS_MODULE, }, .probe = sta2x11_apbreg_probe, @@ -280,13 +404,44 @@ static int __init sta2x11_apbreg_init(void) return platform_driver_register(&sta2x11_platform_driver); } +static struct platform_driver sta2x11_apb_soc_regs_platform_driver = { + .driver = { + .name = STA2X11_MFD_APB_SOC_REGS_NAME, + .owner = THIS_MODULE, + }, + .probe = sta2x11_apb_soc_regs_probe, +}; + +static int __init sta2x11_apb_soc_regs_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_apb_soc_regs_platform_driver); +} + +static struct platform_driver sta2x11_scr_platform_driver = { + .driver = { + .name = STA2X11_MFD_SCR_NAME, + .owner = THIS_MODULE, + }, + .probe = sta2x11_scr_probe, +}; + +static int __init sta2x11_scr_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_scr_platform_driver); +} + + /* - * What follows is the PCI device that hosts the above two pdevs. + * What follows are the PCI devices that host the above pdevs. * Each logic block is 4kB and they are all consecutive: we use this info. */ -/* Bar 0 */ -enum bar0_cells { +/* Mfd 0 device */ + +/* Mfd 0, Bar 0 */ +enum mfd0_bar0_cells { STA2X11_GPIO_0 = 0, STA2X11_GPIO_1, STA2X11_GPIO_2, @@ -295,8 +450,8 @@ enum bar0_cells { STA2X11_SCR, STA2X11_TIME, }; -/* Bar 1 */ -enum bar1_cells { +/* Mfd 0 , Bar 1 */ +enum mfd0_bar1_cells { STA2X11_APBREG = 0, }; #define CELL_4K(_name, _cell) { \ @@ -307,40 +462,71 @@ enum bar1_cells { static const struct resource gpio_resources[] = { { - .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */ + /* 4 consecutive cells, 1 driver */ + .name = STA2X11_MFD_GPIO_NAME, .start = 0, .end = (4 * 4096) - 1, .flags = IORESOURCE_MEM, } }; static const struct resource sctl_resources[] = { - CELL_4K("sta2x11-sctl", STA2X11_SCTL), + CELL_4K(STA2X11_MFD_SCTL_NAME, STA2X11_SCTL), }; static const struct resource scr_resources[] = { - CELL_4K("sta2x11-scr", STA2X11_SCR), + CELL_4K(STA2X11_MFD_SCR_NAME, STA2X11_SCR), }; static const struct resource time_resources[] = { - CELL_4K("sta2x11-time", STA2X11_TIME), + CELL_4K(STA2X11_MFD_TIME_NAME, STA2X11_TIME), }; static const struct resource apbreg_resources[] = { - CELL_4K("sta2x11-apbreg", STA2X11_APBREG), + CELL_4K(STA2X11_MFD_APBREG_NAME, STA2X11_APBREG), }; #define DEV(_name, _r) \ { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } -static struct mfd_cell sta2x11_mfd_bar0[] = { - DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */ - DEV("sta2x11-sctl", sctl_resources), - DEV("sta2x11-scr", scr_resources), - DEV("sta2x11-time", time_resources), +static struct mfd_cell sta2x11_mfd0_bar0[] = { + /* offset 0: we add pdata later */ + DEV(STA2X11_MFD_GPIO_NAME, gpio_resources), + DEV(STA2X11_MFD_SCTL_NAME, sctl_resources), + DEV(STA2X11_MFD_SCR_NAME, scr_resources), + DEV(STA2X11_MFD_TIME_NAME, time_resources), }; -static struct mfd_cell sta2x11_mfd_bar1[] = { - DEV("sta2x11-apbreg", apbreg_resources), +static struct mfd_cell sta2x11_mfd0_bar1[] = { + DEV(STA2X11_MFD_APBREG_NAME, apbreg_resources), }; +/* Mfd 1 devices */ + +/* Mfd 1, Bar 0 */ +enum mfd1_bar0_cells { + STA2X11_VIC = 0, +}; + +/* Mfd 1, Bar 1 */ +enum mfd1_bar1_cells { + STA2X11_APB_SOC_REGS = 0, +}; + +static const __devinitconst struct resource vic_resources[] = { + CELL_4K(STA2X11_MFD_VIC_NAME, STA2X11_VIC), +}; + +static const __devinitconst struct resource apb_soc_regs_resources[] = { + CELL_4K(STA2X11_MFD_APB_SOC_REGS_NAME, STA2X11_APB_SOC_REGS), +}; + +static __devinitdata struct mfd_cell sta2x11_mfd1_bar0[] = { + DEV(STA2X11_MFD_VIC_NAME, vic_resources), +}; + +static __devinitdata struct mfd_cell sta2x11_mfd1_bar1[] = { + DEV(STA2X11_MFD_APB_SOC_REGS_NAME, apb_soc_regs_resources), +}; + + static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state) { pci_save_state(pdev); @@ -363,11 +549,63 @@ static int sta2x11_mfd_resume(struct pci_dev *pdev) return 0; } +struct sta2x11_mfd_bar_setup_data { + struct mfd_cell *cells; + int ncells; +}; + +struct sta2x11_mfd_setup_data { + struct sta2x11_mfd_bar_setup_data bars[2]; +}; + +#define STA2X11_MFD0 0 +#define STA2X11_MFD1 1 + +static struct sta2x11_mfd_setup_data mfd_setup_data[] = { + /* Mfd 0: gpio, sctl, scr, timers / apbregs */ + [STA2X11_MFD0] = { + .bars = { + [0] = { + .cells = sta2x11_mfd0_bar0, + .ncells = ARRAY_SIZE(sta2x11_mfd0_bar0), + }, + [1] = { + .cells = sta2x11_mfd0_bar1, + .ncells = ARRAY_SIZE(sta2x11_mfd0_bar1), + }, + }, + }, + /* Mfd 1: vic / apb-soc-regs */ + [STA2X11_MFD1] = { + .bars = { + [0] = { + .cells = sta2x11_mfd1_bar0, + .ncells = ARRAY_SIZE(sta2x11_mfd1_bar0), + }, + [1] = { + .cells = sta2x11_mfd1_bar1, + .ncells = ARRAY_SIZE(sta2x11_mfd1_bar1), + }, + }, + }, +}; + +static void sta2x11_mfd_setup(struct pci_dev *pdev, + struct sta2x11_mfd_setup_data *sd) +{ + int i, j; + for (i = 0; i < ARRAY_SIZE(sd->bars); i++) + for (j = 0; j < sd->bars[i].ncells; j++) { + sd->bars[i].cells[j].pdata_size = sizeof(pdev); + sd->bars[i].cells[j].platform_data = &pdev; + } +} + static int sta2x11_mfd_probe(struct pci_dev *pdev, - const struct pci_device_id *pci_id) + const struct pci_device_id *pci_id) { int err, i; - struct sta2x11_gpio_pdata *gpio_data; + struct sta2x11_mfd_setup_data *setup_data; dev_info(&pdev->dev, "%s\n", __func__); @@ -381,46 +619,29 @@ static int sta2x11_mfd_probe(struct pci_dev *pdev, if (err) dev_info(&pdev->dev, "Enable msi failed\n"); - /* Read gpio config data as pci device's platform data */ - gpio_data = dev_get_platdata(&pdev->dev); - if (!gpio_data) - dev_warn(&pdev->dev, "no gpio configuration\n"); - - dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__, - gpio_data, &gpio_data); - dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__, - pdev, &pdev); + setup_data = pci_id->device == PCI_DEVICE_ID_STMICRO_GPIO ? + &mfd_setup_data[STA2X11_MFD0] : + &mfd_setup_data[STA2X11_MFD1]; /* platform data is the pci device for all of them */ - for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) { - sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev); - sta2x11_mfd_bar0[i].platform_data = &pdev; - } - sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev); - sta2x11_mfd_bar1[0].platform_data = &pdev; + sta2x11_mfd_setup(pdev, setup_data); /* Record this pdev before mfd_add_devices: their probe looks for it */ - sta2x11_mfd_add(pdev, GFP_ATOMIC); - - - err = mfd_add_devices(&pdev->dev, -1, - sta2x11_mfd_bar0, - ARRAY_SIZE(sta2x11_mfd_bar0), - &pdev->resource[0], - 0, NULL); - if (err) { - dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err); - goto err_disable; - } - - err = mfd_add_devices(&pdev->dev, -1, - sta2x11_mfd_bar1, - ARRAY_SIZE(sta2x11_mfd_bar1), - &pdev->resource[1], - 0, NULL); - if (err) { - dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err); - goto err_disable; + if (!sta2x11_mfd_find(pdev)) + sta2x11_mfd_add(pdev, GFP_ATOMIC); + + /* Just 2 bars for all mfd's at present */ + for (i = 0; i < 2; i++) { + err = mfd_add_devices(&pdev->dev, -1, + setup_data->bars[i].cells, + setup_data->bars[i].ncells, + &pdev->resource[i], + 0, NULL); + if (err) { + dev_err(&pdev->dev, + "mfd_add_devices[%d] failed: %d\n", i, err); + goto err_disable; + } } return 0; @@ -434,6 +655,7 @@ err_disable: static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)}, + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIC)}, {0,}, }; @@ -459,6 +681,8 @@ static int __init sta2x11_mfd_init(void) */ subsys_initcall(sta2x11_apbreg_init); subsys_initcall(sta2x11_sctl_init); +subsys_initcall(sta2x11_apb_soc_regs_init); +subsys_initcall(sta2x11_scr_init); rootfs_initcall(sta2x11_mfd_init); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index 36df1877802..fd5fcb63068 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -82,11 +82,13 @@ static const struct i2c_device_id stmpe_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, stmpe_id); static struct i2c_driver stmpe_i2c_driver = { - .driver.name = "stmpe-i2c", - .driver.owner = THIS_MODULE, + .driver = { + .name = "stmpe-i2c", + .owner = THIS_MODULE, #ifdef CONFIG_PM - .driver.pm = &stmpe_dev_pm_ops, + .pm = &stmpe_dev_pm_ops, #endif + }, .probe = stmpe_i2c_probe, .remove = stmpe_i2c_remove, .id_table = stmpe_i2c_id, diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 79e88d1fd99..19636199d7a 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -7,11 +7,15 @@ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson */ +#include <linux/err.h> #include <linux/gpio.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/pm.h> #include <linux/slab.h> #include <linux/mfd/core.h> @@ -312,14 +316,10 @@ static struct mfd_cell stmpe_gpio_cell_noirq = { static struct resource stmpe_keypad_resources[] = { { .name = "KEYPAD", - .start = 0, - .end = 0, .flags = IORESOURCE_IRQ, }, { .name = "KEYPAD_OVER", - .start = 1, - .end = 1, .flags = IORESOURCE_IRQ, }, }; @@ -399,14 +399,10 @@ static struct stmpe_variant_info stmpe801_noirq = { static struct resource stmpe_ts_resources[] = { { .name = "TOUCH_DET", - .start = 0, - .end = 0, .flags = IORESOURCE_IRQ, }, { .name = "FIFO_TH", - .start = 1, - .end = 1, .flags = IORESOURCE_IRQ, }, }; @@ -528,12 +524,12 @@ static const u8 stmpe1601_regs[] = { static struct stmpe_variant_block stmpe1601_blocks[] = { { .cell = &stmpe_gpio_cell, - .irq = STMPE24XX_IRQ_GPIOC, + .irq = STMPE1601_IRQ_GPIOC, .block = STMPE_BLOCK_GPIO, }, { .cell = &stmpe_keypad_cell, - .irq = STMPE24XX_IRQ_KEYPAD, + .irq = STMPE1601_IRQ_KEYPAD, .block = STMPE_BLOCK_KEYPAD, }, }; @@ -767,7 +763,9 @@ static irqreturn_t stmpe_irq(int irq, void *data) int i; if (variant->id_val == STMPE801_ID) { - handle_nested_irq(stmpe->irq_base); + int base = irq_create_mapping(stmpe->domain, 0); + + handle_nested_irq(base); return IRQ_HANDLED; } @@ -788,8 +786,9 @@ static irqreturn_t stmpe_irq(int irq, void *data) while (status) { int bit = __ffs(status); int line = bank * 8 + bit; + int nestedirq = irq_create_mapping(stmpe->domain, line); - handle_nested_irq(stmpe->irq_base + line); + handle_nested_irq(nestedirq); status &= ~(1 << bit); } @@ -830,7 +829,7 @@ static void stmpe_irq_sync_unlock(struct irq_data *data) static void stmpe_irq_mask(struct irq_data *data) { struct stmpe *stmpe = irq_data_get_irq_chip_data(data); - int offset = data->irq - stmpe->irq_base; + int offset = data->hwirq; int regoffset = offset / 8; int mask = 1 << (offset % 8); @@ -840,7 +839,7 @@ static void stmpe_irq_mask(struct irq_data *data) static void stmpe_irq_unmask(struct irq_data *data) { struct stmpe *stmpe = irq_data_get_irq_chip_data(data); - int offset = data->irq - stmpe->irq_base; + int offset = data->hwirq; int regoffset = offset / 8; int mask = 1 << (offset % 8); @@ -855,43 +854,59 @@ static struct irq_chip stmpe_irq_chip = { .irq_unmask = stmpe_irq_unmask, }; -static int __devinit stmpe_irq_init(struct stmpe *stmpe) +static int stmpe_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { + struct stmpe *stmpe = d->host_data; struct irq_chip *chip = NULL; - int num_irqs = stmpe->variant->num_irqs; - int base = stmpe->irq_base; - int irq; if (stmpe->variant->id_val != STMPE801_ID) chip = &stmpe_irq_chip; - for (irq = base; irq < base + num_irqs; irq++) { - irq_set_chip_data(irq, stmpe); - irq_set_chip_and_handler(irq, chip, handle_edge_irq); - irq_set_nested_thread(irq, 1); + irq_set_chip_data(virq, stmpe); + irq_set_chip_and_handler(virq, chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); #ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); + set_irq_flags(virq, IRQF_VALID); #else - irq_set_noprobe(irq); + irq_set_noprobe(virq); #endif - } return 0; } -static void stmpe_irq_remove(struct stmpe *stmpe) +static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq) { - int num_irqs = stmpe->variant->num_irqs; - int base = stmpe->irq_base; - int irq; - - for (irq = base; irq < base + num_irqs; irq++) { #ifdef CONFIG_ARM - set_irq_flags(irq, 0); + set_irq_flags(virq, 0); #endif - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_chip_data(irq, NULL); + irq_set_chip_and_handler(virq, NULL, NULL); + irq_set_chip_data(virq, NULL); +} + +static struct irq_domain_ops stmpe_irq_ops = { + .map = stmpe_irq_map, + .unmap = stmpe_irq_unmap, + .xlate = irq_domain_xlate_twocell, +}; + +static int __devinit stmpe_irq_init(struct stmpe *stmpe, + struct device_node *np) +{ + int base = 0; + int num_irqs = stmpe->variant->num_irqs; + + if (!np) + base = stmpe->irq_base; + + stmpe->domain = irq_domain_add_simple(np, num_irqs, base, + &stmpe_irq_ops, stmpe); + if (!stmpe->domain) { + dev_err(stmpe->dev, "Failed to create irqdomain\n"); + return -ENOSYS; } + + return 0; } static int __devinit stmpe_chip_init(struct stmpe *stmpe) @@ -942,13 +957,6 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe) else icr |= STMPE_ICR_LSB_HIGH; } - - if (stmpe->pdata->irq_invert_polarity) { - if (id == STMPE801_ID) - icr ^= STMPE801_REG_SYS_CTRL_INT_HI; - else - icr ^= STMPE_ICR_LSB_HIGH; - } } if (stmpe->pdata->autosleep) { @@ -961,10 +969,10 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe) } static int __devinit stmpe_add_device(struct stmpe *stmpe, - struct mfd_cell *cell, int irq) + struct mfd_cell *cell) { return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1, - NULL, stmpe->irq_base + irq, NULL); + NULL, stmpe->irq_base, stmpe->domain); } static int __devinit stmpe_devices_init(struct stmpe *stmpe) @@ -972,7 +980,7 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) struct stmpe_variant_info *variant = stmpe->variant; unsigned int platform_blocks = stmpe->pdata->blocks; int ret = -EINVAL; - int i; + int i, j; for (i = 0; i < variant->num_blocks; i++) { struct stmpe_variant_block *block = &variant->blocks[i]; @@ -980,8 +988,17 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) if (!(platform_blocks & block->block)) continue; + for (j = 0; j < block->cell->num_resources; j++) { + struct resource *res = + (struct resource *) &block->cell->resources[j]; + + /* Dynamically fill in a variant's IRQ. */ + if (res->flags & IORESOURCE_IRQ) + res->start = res->end = block->irq + j; + } + platform_blocks &= ~block->block; - ret = stmpe_add_device(stmpe, block->cell, block->irq); + ret = stmpe_add_device(stmpe, block->cell); if (ret) return ret; } @@ -994,17 +1011,56 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe) return ret; } +void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, + struct device_node *np) +{ + struct device_node *child; + + pdata->id = -1; + pdata->irq_trigger = IRQF_TRIGGER_NONE; + + of_property_read_u32(np, "st,autosleep-timeout", + &pdata->autosleep_timeout); + + pdata->autosleep = (pdata->autosleep_timeout) ? true : false; + + for_each_child_of_node(np, child) { + if (!strcmp(child->name, "stmpe_gpio")) { + pdata->blocks |= STMPE_BLOCK_GPIO; + } else if (!strcmp(child->name, "stmpe_keypad")) { + pdata->blocks |= STMPE_BLOCK_KEYPAD; + } else if (!strcmp(child->name, "stmpe_touchscreen")) { + pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN; + } else if (!strcmp(child->name, "stmpe_adc")) { + pdata->blocks |= STMPE_BLOCK_ADC; + } else if (!strcmp(child->name, "stmpe_pwm")) { + pdata->blocks |= STMPE_BLOCK_PWM; + } else if (!strcmp(child->name, "stmpe_rotator")) { + pdata->blocks |= STMPE_BLOCK_ROTATOR; + } + } +} + /* Called from client specific probe routines */ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) { struct stmpe_platform_data *pdata = dev_get_platdata(ci->dev); + struct device_node *np = ci->dev->of_node; struct stmpe *stmpe; int ret; - if (!pdata) - return -EINVAL; + if (!pdata) { + if (!np) + return -EINVAL; + + pdata = devm_kzalloc(ci->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + stmpe_of_probe(pdata, np); + } - stmpe = kzalloc(sizeof(struct stmpe), GFP_KERNEL); + stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL); if (!stmpe) return -ENOMEM; @@ -1026,11 +1082,12 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) ci->init(stmpe); if (pdata->irq_over_gpio) { - ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "stmpe"); + ret = devm_gpio_request_one(ci->dev, pdata->irq_gpio, + GPIOF_DIR_IN, "stmpe"); if (ret) { dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n", ret); - goto out_free; + return ret; } stmpe->irq = gpio_to_irq(pdata->irq_gpio); @@ -1047,51 +1104,40 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) dev_err(stmpe->dev, "%s does not support no-irq mode!\n", stmpe->variant->name); - ret = -ENODEV; - goto free_gpio; + return -ENODEV; } stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; + } else if (pdata->irq_trigger == IRQF_TRIGGER_NONE) { + pdata->irq_trigger = + irqd_get_trigger_type(irq_get_irq_data(stmpe->irq)); } ret = stmpe_chip_init(stmpe); if (ret) - goto free_gpio; + return ret; if (stmpe->irq >= 0) { - ret = stmpe_irq_init(stmpe); + ret = stmpe_irq_init(stmpe, np); if (ret) - goto free_gpio; + return ret; - ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq, - pdata->irq_trigger | IRQF_ONESHOT, + ret = devm_request_threaded_irq(ci->dev, stmpe->irq, NULL, + stmpe_irq, pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe); if (ret) { dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret); - goto out_removeirq; + return ret; } } ret = stmpe_devices_init(stmpe); - if (ret) { - dev_err(stmpe->dev, "failed to add children\n"); - goto out_removedevs; - } - - return 0; + if (!ret) + return 0; -out_removedevs: + dev_err(stmpe->dev, "failed to add children\n"); mfd_remove_devices(stmpe->dev); - if (stmpe->irq >= 0) - free_irq(stmpe->irq, stmpe); -out_removeirq: - if (stmpe->irq >= 0) - stmpe_irq_remove(stmpe); -free_gpio: - if (pdata->irq_over_gpio) - gpio_free(pdata->irq_gpio); -out_free: - kfree(stmpe); + return ret; } @@ -1099,16 +1145,6 @@ int stmpe_remove(struct stmpe *stmpe) { mfd_remove_devices(stmpe->dev); - if (stmpe->irq >= 0) { - free_irq(stmpe->irq, stmpe); - stmpe_irq_remove(stmpe); - } - - if (stmpe->pdata->irq_over_gpio) - gpio_free(stmpe->pdata->irq_gpio); - - kfree(stmpe); - return 0; } diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c new file mode 100644 index 00000000000..8ca3bf023fb --- /dev/null +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -0,0 +1,274 @@ +/* + * TI Touch Screen / ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/regmap.h> +#include <linux/mfd/core.h> +#include <linux/pm_runtime.h> + +#include <linux/mfd/ti_am335x_tscadc.h> +#include <linux/input/ti_am335x_tsc.h> +#include <linux/platform_data/ti_am335x_adc.h> + +static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) +{ + unsigned int val; + + regmap_read(tsadc->regmap_tscadc, reg, &val); + return val; +} + +static void tscadc_writel(struct ti_tscadc_dev *tsadc, unsigned int reg, + unsigned int val) +{ + regmap_write(tsadc->regmap_tscadc, reg, val); +} + +static const struct regmap_config tscadc_regmap_config = { + .name = "ti_tscadc", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +static void tscadc_idle_config(struct ti_tscadc_dev *config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | STEPCONFIG_INM_ADCREFM | + STEPCONFIG_INP_ADCREFM | STEPCONFIG_YPN; + + tscadc_writel(config, REG_IDLECONFIG, idleconfig); +} + +static int __devinit ti_tscadc_probe(struct platform_device *pdev) +{ + struct ti_tscadc_dev *tscadc; + struct resource *res; + struct clk *clk; + struct mfd_tscadc_board *pdata = pdev->dev.platform_data; + struct mfd_cell *cell; + int err, ctrl; + int clk_value, clock_rate; + int tsc_wires, adc_channels = 0, total_channels; + + if (!pdata) { + dev_err(&pdev->dev, "Could not find platform data\n"); + return -EINVAL; + } + + if (pdata->adc_init) + adc_channels = pdata->adc_init->adc_channels; + + tsc_wires = pdata->tsc_init->wires; + total_channels = tsc_wires + adc_channels; + + if (total_channels > 8) { + dev_err(&pdev->dev, "Number of i/p channels more than 8\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + tscadc = devm_kzalloc(&pdev->dev, + sizeof(struct ti_tscadc_dev), GFP_KERNEL); + if (!tscadc) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + return -ENOMEM; + } + tscadc->dev = &pdev->dev; + + err = platform_get_irq(pdev, 0); + if (err < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + goto ret; + } else + tscadc->irq = err; + + res = devm_request_mem_region(&pdev->dev, + res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + return -EBUSY; + } + + tscadc->tscadc_base = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!tscadc->tscadc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + return -ENOMEM; + } + + tscadc->regmap_tscadc = devm_regmap_init_mmio(&pdev->dev, + tscadc->tscadc_base, &tscadc_regmap_config); + if (IS_ERR(tscadc->regmap_tscadc)) { + dev_err(&pdev->dev, "regmap init failed\n"); + err = PTR_ERR(tscadc->regmap_tscadc); + goto ret; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + /* + * The TSC_ADC_Subsystem has 2 clock domains + * OCP_CLK and ADC_CLK. + * The ADC clock is expected to run at target of 3MHz, + * and expected to capture 12-bit data at a rate of 200 KSPS. + * The TSC_ADC_SS controller design assumes the OCP clock is + * at least 6x faster than the ADC clock. + */ + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + clock_rate = clk_get_rate(clk); + clk_put(clk); + clk_value = clock_rate / ADC_CLK; + if (clk_value < MAX_CLK_DIV) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + err = -EINVAL; + goto err_disable_clk; + } + /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ + clk_value = clk_value - 1; + tscadc_writel(tscadc, REG_CLKDIV, clk_value); + + /* Set the control register bits */ + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID | + CNTRLREG_4WIRE; + tscadc_writel(tscadc, REG_CTRL, ctrl); + + /* Set register bits for Idle Config Mode */ + tscadc_idle_config(tscadc); + + /* Enable the TSC module enable bit */ + ctrl = tscadc_readl(tscadc, REG_CTRL); + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(tscadc, REG_CTRL, ctrl); + + /* TSC Cell */ + cell = &tscadc->cells[TSC_CELL]; + cell->name = "tsc"; + cell->platform_data = tscadc; + cell->pdata_size = sizeof(*tscadc); + + /* ADC Cell */ + cell = &tscadc->cells[ADC_CELL]; + cell->name = "tiadc"; + cell->platform_data = tscadc; + cell->pdata_size = sizeof(*tscadc); + + err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, + TSCADC_CELLS, NULL, 0, NULL); + if (err < 0) + goto err_disable_clk; + + device_init_wakeup(&pdev->dev, true); + platform_set_drvdata(pdev, tscadc); + + return 0; + +err_disable_clk: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +ret: + return err; +} + +static int __devexit ti_tscadc_remove(struct platform_device *pdev) +{ + struct ti_tscadc_dev *tscadc = platform_get_drvdata(pdev); + + tscadc_writel(tscadc, REG_SE, 0x00); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + mfd_remove_devices(tscadc->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int tscadc_suspend(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev); + + tscadc_writel(tscadc_dev, REG_SE, 0x00); + pm_runtime_put_sync(dev); + + return 0; +} + +static int tscadc_resume(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev_get_drvdata(dev); + unsigned int restore, ctrl; + + pm_runtime_get_sync(dev); + + /* context restore */ + ctrl = CNTRLREG_STEPCONFIGWRT | CNTRLREG_TSCENB | + CNTRLREG_STEPID | CNTRLREG_4WIRE; + tscadc_writel(tscadc_dev, REG_CTRL, ctrl); + tscadc_idle_config(tscadc_dev); + tscadc_writel(tscadc_dev, REG_SE, STPENB_STEPENB); + restore = tscadc_readl(tscadc_dev, REG_CTRL); + tscadc_writel(tscadc_dev, REG_CTRL, + (restore | CNTRLREG_TSCSSENB)); + + return 0; +} + +static const struct dev_pm_ops tscadc_pm_ops = { + .suspend = tscadc_suspend, + .resume = tscadc_resume, +}; +#define TSCADC_PM_OPS (&tscadc_pm_ops) +#else +#define TSCADC_PM_OPS NULL +#endif + +static struct platform_driver ti_tscadc_driver = { + .driver = { + .name = "ti_tscadc", + .owner = THIS_MODULE, + .pm = TSCADC_PM_OPS, + }, + .probe = ti_tscadc_probe, + .remove = __devexit_p(ti_tscadc_remove), + +}; + +module_platform_driver(ti_tscadc_driver); + +MODULE_DESCRIPTION("TI touchscreen / ADC MFD controller driver"); +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index 1b203499c74..409afa23d5d 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -86,9 +86,9 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct tps6507x_dev *tps6507x; - int ret = 0; - tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL); + tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev), + GFP_KERNEL); if (tps6507x == NULL) return -ENOMEM; @@ -98,19 +98,8 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c, tps6507x->read_dev = tps6507x_i2c_read_device; tps6507x->write_dev = tps6507x_i2c_write_device; - ret = mfd_add_devices(tps6507x->dev, -1, - tps6507x_devs, ARRAY_SIZE(tps6507x_devs), - NULL, 0, NULL); - - if (ret < 0) - goto err; - - return ret; - -err: - mfd_remove_devices(tps6507x->dev); - kfree(tps6507x); - return ret; + return mfd_add_devices(tps6507x->dev, -1, tps6507x_devs, + ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL); } static int tps6507x_i2c_remove(struct i2c_client *i2c) @@ -118,8 +107,6 @@ static int tps6507x_i2c_remove(struct i2c_client *i2c) struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c); mfd_remove_devices(tps6507x->dev); - kfree(tps6507x); - return 0; } diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 382a857b0dd..8d12a8e00d9 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -25,7 +25,6 @@ #include <linux/i2c.h> #include <linux/mfd/core.h> #include <linux/mfd/tps65090.h> -#include <linux/regmap.h> #include <linux/err.h> #define NUM_INT_REG 2 @@ -39,204 +38,102 @@ #define TPS65090_INT_MSK 0x2 #define TPS65090_INT_MSK2 0x3 -struct tps65090_irq_data { - u8 mask_reg; - u8 mask_pos; -}; - -#define TPS65090_IRQ(_reg, _mask_pos) \ - { \ - .mask_reg = (_reg), \ - .mask_pos = (_mask_pos), \ - } - -static const struct tps65090_irq_data tps65090_irqs[] = { - [0] = TPS65090_IRQ(0, 0), - [1] = TPS65090_IRQ(0, 1), - [2] = TPS65090_IRQ(0, 2), - [3] = TPS65090_IRQ(0, 3), - [4] = TPS65090_IRQ(0, 4), - [5] = TPS65090_IRQ(0, 5), - [6] = TPS65090_IRQ(0, 6), - [7] = TPS65090_IRQ(0, 7), - [8] = TPS65090_IRQ(1, 0), - [9] = TPS65090_IRQ(1, 1), - [10] = TPS65090_IRQ(1, 2), - [11] = TPS65090_IRQ(1, 3), - [12] = TPS65090_IRQ(1, 4), - [13] = TPS65090_IRQ(1, 5), - [14] = TPS65090_IRQ(1, 6), - [15] = TPS65090_IRQ(1, 7), -}; +#define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1 +#define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2 +#define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3 +#define TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE 4 +#define TPS65090_INT1_MASK_CHARGING_COMPLETE 5 +#define TPS65090_INT1_MASK_OVERLOAD_DCDC1 6 +#define TPS65090_INT1_MASK_OVERLOAD_DCDC2 7 +#define TPS65090_INT2_MASK_OVERLOAD_DCDC3 0 +#define TPS65090_INT2_MASK_OVERLOAD_FET1 1 +#define TPS65090_INT2_MASK_OVERLOAD_FET2 2 +#define TPS65090_INT2_MASK_OVERLOAD_FET3 3 +#define TPS65090_INT2_MASK_OVERLOAD_FET4 4 +#define TPS65090_INT2_MASK_OVERLOAD_FET5 5 +#define TPS65090_INT2_MASK_OVERLOAD_FET6 6 +#define TPS65090_INT2_MASK_OVERLOAD_FET7 7 static struct mfd_cell tps65090s[] = { { .name = "tps65090-pmic", }, { - .name = "tps65090-regulator", + .name = "tps65090-charger", }, }; -int tps65090_write(struct device *dev, int reg, uint8_t val) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_write(tps->rmap, reg, val); -} -EXPORT_SYMBOL_GPL(tps65090_write); - -int tps65090_read(struct device *dev, int reg, uint8_t *val) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - unsigned int temp_val; - int ret; - ret = regmap_read(tps->rmap, reg, &temp_val); - if (!ret) - *val = temp_val; - return ret; -} -EXPORT_SYMBOL_GPL(tps65090_read); - -int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u); -} -EXPORT_SYMBOL_GPL(tps65090_set_bits); - -int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u); -} -EXPORT_SYMBOL_GPL(tps65090_clr_bits); - -static void tps65090_irq_lock(struct irq_data *data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); - - mutex_lock(&tps65090->irq_lock); -} - -static void tps65090_irq_mask(struct irq_data *irq_data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->hwirq; - const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - - tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), - data->mask_pos); -} - -static void tps65090_irq_unmask(struct irq_data *irq_data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps65090->irq_base; - const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - - tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), - data->mask_pos); -} - -static void tps65090_irq_sync_unlock(struct irq_data *data) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); - - mutex_unlock(&tps65090->irq_lock); -} - -static irqreturn_t tps65090_irq(int irq, void *data) -{ - struct tps65090 *tps65090 = data; - int ret = 0; - u8 status, mask; - unsigned long int acks = 0; - int i; - - for (i = 0; i < NUM_INT_REG; i++) { - ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask); - if (ret < 0) { - dev_err(tps65090->dev, - "failed to read mask reg [addr:%d]\n", - TPS65090_INT_MSK + i); - return IRQ_NONE; - } - ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i, - &status); - if (ret < 0) { - dev_err(tps65090->dev, - "failed to read status reg [addr:%d]\n", - TPS65090_INT_STS + i); - return IRQ_NONE; - } - if (status) { - /* Ack only those interrupts which are not masked */ - status &= (~mask); - ret = tps65090_write(tps65090->dev, - TPS65090_INT_STS + i, status); - if (ret < 0) { - dev_err(tps65090->dev, - "failed to write interrupt status\n"); - return IRQ_NONE; - } - acks |= (status << (i * 8)); - } - } - - for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs)) - handle_nested_irq(tps65090->irq_base + i); - return acks ? IRQ_HANDLED : IRQ_NONE; -} - -static int tps65090_irq_init(struct tps65090 *tps65090, int irq, - int irq_base) -{ - int i, ret; - - if (!irq_base) { - dev_err(tps65090->dev, "IRQ base not set\n"); - return -EINVAL; - } - - mutex_init(&tps65090->irq_lock); - - for (i = 0; i < NUM_INT_REG; i++) - tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF); - - for (i = 0; i < NUM_INT_REG; i++) - tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff); - - tps65090->irq_base = irq_base; - tps65090->irq_chip.name = "tps65090"; - tps65090->irq_chip.irq_mask = tps65090_irq_mask; - tps65090->irq_chip.irq_unmask = tps65090_irq_unmask; - tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock; - tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock; - - for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) { - int __irq = i + tps65090->irq_base; - irq_set_chip_data(__irq, tps65090); - irq_set_chip_and_handler(__irq, &tps65090->irq_chip, - handle_simple_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#endif - } - - ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT, - "tps65090", tps65090); - if (!ret) { - device_init_wakeup(tps65090->dev, 1); - enable_irq_wake(irq); - } +static const struct regmap_irq tps65090_irqs[] = { + /* INT1 IRQs*/ + [TPS65090_IRQ_VAC_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE, + }, + [TPS65090_IRQ_VSYS_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE, + }, + [TPS65090_IRQ_BAT_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE, + }, + [TPS65090_IRQ_CHARGING_STATUS_CHANGE] = { + .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE, + }, + [TPS65090_IRQ_CHARGING_COMPLETE] = { + .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE, + }, + [TPS65090_IRQ_OVERLOAD_DCDC1] = { + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1, + }, + [TPS65090_IRQ_OVERLOAD_DCDC2] = { + .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2, + }, + /* INT2 IRQs*/ + [TPS65090_IRQ_OVERLOAD_DCDC3] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3, + }, + [TPS65090_IRQ_OVERLOAD_FET1] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET1, + }, + [TPS65090_IRQ_OVERLOAD_FET2] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET2, + }, + [TPS65090_IRQ_OVERLOAD_FET3] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET3, + }, + [TPS65090_IRQ_OVERLOAD_FET4] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET4, + }, + [TPS65090_IRQ_OVERLOAD_FET5] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET5, + }, + [TPS65090_IRQ_OVERLOAD_FET6] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET6, + }, + [TPS65090_IRQ_OVERLOAD_FET7] = { + .reg_offset = 1, + .mask = TPS65090_INT2_MASK_OVERLOAD_FET7, + }, +}; - return ret; -} +static struct regmap_irq_chip tps65090_irq_chip = { + .name = "tps65090", + .irqs = tps65090_irqs, + .num_irqs = ARRAY_SIZE(tps65090_irqs), + .num_regs = NUM_INT_REG, + .status_base = TPS65090_INT_STS, + .mask_base = TPS65090_INT_MSK, + .mask_invert = true, +}; static bool is_volatile_reg(struct device *dev, unsigned int reg) { - if (reg == TPS65090_INT_STS) + if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2)) return true; else return false; @@ -263,36 +160,36 @@ static int tps65090_i2c_probe(struct i2c_client *client, return -EINVAL; } - tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090), - GFP_KERNEL); - if (tps65090 == NULL) + tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL); + if (!tps65090) { + dev_err(&client->dev, "mem alloc for tps65090 failed\n"); return -ENOMEM; + } - tps65090->client = client; tps65090->dev = &client->dev; i2c_set_clientdata(client, tps65090); - mutex_init(&tps65090->lock); - - if (client->irq) { - ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base); - if (ret) { - dev_err(&client->dev, "IRQ init failed with err: %d\n", - ret); - goto err_exit; - } - } - - tps65090->rmap = devm_regmap_init_i2c(tps65090->client, - &tps65090_regmap_config); + tps65090->rmap = devm_regmap_init_i2c(client, &tps65090_regmap_config); if (IS_ERR(tps65090->rmap)) { ret = PTR_ERR(tps65090->rmap); dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); - goto err_irq_exit; + return ret; + } + + if (client->irq) { + ret = regmap_add_irq_chip(tps65090->rmap, client->irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, pdata->irq_base, + &tps65090_irq_chip, &tps65090->irq_data); + if (ret) { + dev_err(&client->dev, + "IRQ init failed with err: %d\n", ret); + return ret; + } } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, - ARRAY_SIZE(tps65090s), NULL, 0, NULL); + ARRAY_SIZE(tps65090s), NULL, + regmap_irq_chip_get_base(tps65090->irq_data), NULL); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); @@ -303,8 +200,7 @@ static int tps65090_i2c_probe(struct i2c_client *client, err_irq_exit: if (client->irq) - free_irq(client->irq, tps65090); -err_exit: + regmap_del_irq_chip(client->irq, tps65090->irq_data); return ret; } @@ -314,7 +210,7 @@ static int tps65090_i2c_remove(struct i2c_client *client) mfd_remove_devices(tps65090->dev); if (client->irq) - free_irq(client->irq, tps65090); + regmap_del_irq_chip(client->irq, tps65090->irq_data); return 0; } diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index e14e252e347..b8f48647661 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -160,6 +160,7 @@ static int tps65217_probe(struct i2c_client *client, unsigned int version; unsigned int chip_id = ids->driver_data; const struct of_device_id *match; + bool status_off = false; int ret; if (client->dev.of_node) { @@ -170,6 +171,8 @@ static int tps65217_probe(struct i2c_client *client, return -EINVAL; } chip_id = (unsigned int)match->data; + status_off = of_property_read_bool(client->dev.of_node, + "ti,pmic-shutdown-controller"); } if (!chip_id) { @@ -207,6 +210,15 @@ static int tps65217_probe(struct i2c_client *client, return ret; } + /* Set the PMIC to shutdown on PWR_EN toggle */ + if (status_off) { + ret = tps65217_set_bits(tps, TPS65217_REG_STATUS, + TPS65217_STATUS_OFF, TPS65217_STATUS_OFF, + TPS65217_PROTECT_NONE); + if (ret) + dev_warn(tps->dev, "unable to set the status OFF\n"); + } + dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", (version & TPS65217_CHIPID_CHIP_MASK) >> 4, version & TPS65217_CHIPID_REV_MASK); diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 87ba7ada3bb..721b9186a5d 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -17,12 +17,14 @@ #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/mfd/core.h> @@ -92,6 +94,14 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = { [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), }; +static struct resource tps6586x_rtc_resources[] = { + { + .start = TPS6586X_INT_RTC_ALM1, + .end = TPS6586X_INT_RTC_ALM1, + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell tps6586x_cell[] = { { .name = "tps6586x-gpio", @@ -101,6 +111,8 @@ static struct mfd_cell tps6586x_cell[] = { }, { .name = "tps6586x-rtc", + .num_resources = ARRAY_SIZE(tps6586x_rtc_resources), + .resources = &tps6586x_rtc_resources[0], }, { .name = "tps6586x-onkey", @@ -117,6 +129,7 @@ struct tps6586x { int irq_base; u32 irq_en; u8 mask_reg[5]; + struct irq_domain *irq_domain; }; static inline struct tps6586x *dev_to_tps6586x(struct device *dev) @@ -185,6 +198,14 @@ int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask) } EXPORT_SYMBOL_GPL(tps6586x_update); +int tps6586x_irq_get_virq(struct device *dev, int irq) +{ + struct tps6586x *tps6586x = dev_to_tps6586x(dev); + + return irq_create_mapping(tps6586x->irq_domain, irq); +} +EXPORT_SYMBOL_GPL(tps6586x_irq_get_virq); + static int __remove_subdev(struct device *dev, void *unused) { platform_device_unregister(to_platform_device(dev)); @@ -206,7 +227,7 @@ static void tps6586x_irq_lock(struct irq_data *data) static void tps6586x_irq_enable(struct irq_data *irq_data) { struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps6586x->irq_base; + unsigned int __irq = irq_data->hwirq; const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; @@ -217,7 +238,7 @@ static void tps6586x_irq_disable(struct irq_data *irq_data) { struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps6586x->irq_base; + unsigned int __irq = irq_data->hwirq; const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; @@ -240,6 +261,39 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data) mutex_unlock(&tps6586x->irq_lock); } +static struct irq_chip tps6586x_irq_chip = { + .name = "tps6586x", + .irq_bus_lock = tps6586x_irq_lock, + .irq_bus_sync_unlock = tps6586x_irq_sync_unlock, + .irq_disable = tps6586x_irq_disable, + .irq_enable = tps6586x_irq_enable, +}; + +static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct tps6586x *tps6586x = h->host_data; + + irq_set_chip_data(virq, tps6586x); + irq_set_chip_and_handler(virq, &tps6586x_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops tps6586x_domain_ops = { + .map = tps6586x_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + static irqreturn_t tps6586x_irq(int irq, void *data) { struct tps6586x *tps6586x = data; @@ -260,7 +314,8 @@ static irqreturn_t tps6586x_irq(int irq, void *data) int i = __ffs(acks); if (tps6586x->irq_en & (1 << i)) - handle_nested_irq(tps6586x->irq_base + i); + handle_nested_irq( + irq_find_mapping(tps6586x->irq_domain, i)); acks &= ~(1 << i); } @@ -273,11 +328,8 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq, { int i, ret; u8 tmp[4]; - - if (!irq_base) { - dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n"); - return -EINVAL; - } + int new_irq_base; + int irq_num = ARRAY_SIZE(tps6586x_irqs); mutex_init(&tps6586x->irq_lock); for (i = 0; i < 5; i++) { @@ -287,25 +339,24 @@ static int tps6586x_irq_init(struct tps6586x *tps6586x, int irq, tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp); - tps6586x->irq_base = irq_base; - - tps6586x->irq_chip.name = "tps6586x"; - tps6586x->irq_chip.irq_enable = tps6586x_irq_enable; - tps6586x->irq_chip.irq_disable = tps6586x_irq_disable; - tps6586x->irq_chip.irq_bus_lock = tps6586x_irq_lock; - tps6586x->irq_chip.irq_bus_sync_unlock = tps6586x_irq_sync_unlock; - - for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) { - int __irq = i + tps6586x->irq_base; - irq_set_chip_data(__irq, tps6586x); - irq_set_chip_and_handler(__irq, &tps6586x->irq_chip, - handle_simple_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#endif + if (irq_base > 0) { + new_irq_base = irq_alloc_descs(irq_base, 0, irq_num, -1); + if (new_irq_base < 0) { + dev_err(tps6586x->dev, + "Failed to alloc IRQs: %d\n", new_irq_base); + return new_irq_base; + } + } else { + new_irq_base = 0; } + tps6586x->irq_domain = irq_domain_add_simple(tps6586x->dev->of_node, + irq_num, new_irq_base, &tps6586x_domain_ops, + tps6586x); + if (!tps6586x->irq_domain) { + dev_err(tps6586x->dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT, "tps6586x", tps6586x); @@ -461,7 +512,7 @@ static int tps6586x_i2c_probe(struct i2c_client *client, ret = mfd_add_devices(tps6586x->dev, -1, tps6586x_cell, ARRAY_SIZE(tps6586x_cell), - NULL, 0, NULL); + NULL, 0, tps6586x->irq_domain); if (ret < 0) { dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); goto err_mfd_add; diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c deleted file mode 100644 index 09aab3e4776..00000000000 --- a/drivers/mfd/tps65910-irq.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * tps65910-irq.c -- TI TPS6591x - * - * Copyright 2010 Texas Instruments Inc. - * - * Author: Graeme Gregory <gg@slimlogic.co.uk> - * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/bug.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/irqdomain.h> -#include <linux/gpio.h> -#include <linux/mfd/tps65910.h> - -/* - * This is a threaded IRQ handler so can access I2C/SPI. Since all - * interrupts are clear on read the IRQ line will be reasserted and - * the physical IRQ will be handled again if another interrupt is - * asserted while we run - in the normal course of events this is a - * rare occurrence so we save I2C/SPI reads. We're also assuming that - * it's rare to get lots of interrupts firing simultaneously so try to - * minimise I/O. - */ -static irqreturn_t tps65910_irq(int irq, void *irq_data) -{ - struct tps65910 *tps65910 = irq_data; - unsigned int reg; - u32 irq_sts; - u32 irq_mask; - int i; - - tps65910_reg_read(tps65910, TPS65910_INT_STS, ®); - irq_sts = reg; - tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®); - irq_sts |= reg << 8; - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®); - irq_sts |= reg << 16; - } - - tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); - irq_mask = reg; - tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); - irq_mask |= reg << 8; - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); - irq_mask |= reg << 16; - } - - irq_sts &= ~irq_mask; - - if (!irq_sts) - return IRQ_NONE; - - for (i = 0; i < tps65910->irq_num; i++) { - - if (!(irq_sts & (1 << i))) - continue; - - handle_nested_irq(irq_find_mapping(tps65910->domain, i)); - } - - /* Write the STS register back to clear IRQs we handled */ - reg = irq_sts & 0xFF; - irq_sts >>= 8; - tps65910_reg_write(tps65910, TPS65910_INT_STS, reg); - reg = irq_sts & 0xFF; - tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg); - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - reg = irq_sts >> 8; - tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg); - } - - return IRQ_HANDLED; -} - -static void tps65910_irq_lock(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - - mutex_lock(&tps65910->irq_lock); -} - -static void tps65910_irq_sync_unlock(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - u32 reg_mask; - unsigned int reg; - - tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); - reg_mask = reg; - tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); - reg_mask |= reg << 8; - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); - reg_mask |= reg << 16; - } - - if (tps65910->irq_mask != reg_mask) { - reg = tps65910->irq_mask & 0xFF; - tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg); - reg = tps65910->irq_mask >> 8 & 0xFF; - tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg); - switch (tps65910_chip_id(tps65910)) { - case TPS65911: - reg = tps65910->irq_mask >> 16; - tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg); - } - } - mutex_unlock(&tps65910->irq_lock); -} - -static void tps65910_irq_enable(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - - tps65910->irq_mask &= ~(1 << data->hwirq); -} - -static void tps65910_irq_disable(struct irq_data *data) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - - tps65910->irq_mask |= (1 << data->hwirq); -} - -#ifdef CONFIG_PM_SLEEP -static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable) -{ - struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - return irq_set_irq_wake(tps65910->chip_irq, enable); -} -#else -#define tps65910_irq_set_wake NULL -#endif - -static struct irq_chip tps65910_irq_chip = { - .name = "tps65910", - .irq_bus_lock = tps65910_irq_lock, - .irq_bus_sync_unlock = tps65910_irq_sync_unlock, - .irq_disable = tps65910_irq_disable, - .irq_enable = tps65910_irq_enable, - .irq_set_wake = tps65910_irq_set_wake, -}; - -static int tps65910_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct tps65910 *tps65910 = h->host_data; - - irq_set_chip_data(virq, tps65910); - irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq); - irq_set_nested_thread(virq, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(virq, IRQF_VALID); -#else - irq_set_noprobe(virq); -#endif - - return 0; -} - -static struct irq_domain_ops tps65910_domain_ops = { - .map = tps65910_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -int tps65910_irq_init(struct tps65910 *tps65910, int irq, - struct tps65910_platform_data *pdata) -{ - int ret; - int flags = IRQF_ONESHOT; - - if (!irq) { - dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); - return -EINVAL; - } - - if (!pdata) { - dev_warn(tps65910->dev, "No interrupt support, no pdata\n"); - return -EINVAL; - } - - switch (tps65910_chip_id(tps65910)) { - case TPS65910: - tps65910->irq_num = TPS65910_NUM_IRQ; - break; - case TPS65911: - tps65910->irq_num = TPS65911_NUM_IRQ; - break; - } - - if (pdata->irq_base > 0) { - pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0, - tps65910->irq_num, -1); - if (pdata->irq_base < 0) { - dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n", - pdata->irq_base); - return pdata->irq_base; - } - } - - tps65910->irq_mask = 0xFFFFFF; - - mutex_init(&tps65910->irq_lock); - tps65910->chip_irq = irq; - tps65910->irq_base = pdata->irq_base; - - if (pdata->irq_base > 0) - tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node, - tps65910->irq_num, - pdata->irq_base, - 0, - &tps65910_domain_ops, tps65910); - else - tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node, - tps65910->irq_num, - &tps65910_domain_ops, tps65910); - - if (!tps65910->domain) { - dev_err(tps65910->dev, "Failed to create IRQ domain\n"); - return -ENOMEM; - } - - ret = request_threaded_irq(irq, NULL, tps65910_irq, flags, - "tps65910", tps65910); - - irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); - - if (ret != 0) - dev_err(tps65910->dev, "Failed to request IRQ: %d\n", ret); - - return ret; -} - -int tps65910_irq_exit(struct tps65910 *tps65910) -{ - if (tps65910->chip_irq) - free_irq(tps65910->chip_irq, tps65910); - return 0; -} diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index ce054654f5b..d7927720483 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -19,6 +19,9 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/mfd/core.h> #include <linux/regmap.h> #include <linux/mfd/tps65910.h> @@ -50,6 +53,219 @@ static struct mfd_cell tps65910s[] = { }; +static const struct regmap_irq tps65911_irqs[] = { + /* INT_STS */ + [TPS65911_IRQ_PWRHOLD_F] = { + .mask = INT_MSK_PWRHOLD_F_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_VBAT_VMHI] = { + .mask = INT_MSK_VMBHI_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRON] = { + .mask = INT_MSK_PWRON_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRON_LP] = { + .mask = INT_MSK_PWRON_LP_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_PWRHOLD_R] = { + .mask = INT_MSK_PWRHOLD_R_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_HOTDIE] = { + .mask = INT_MSK_HOTDIE_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_RTC_ALARM] = { + .mask = INT_MSK_RTC_ALARM_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65911_IRQ_RTC_PERIOD] = { + .mask = INT_MSK_RTC_PERIOD_IT_MSK_MASK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [TPS65911_IRQ_GPIO0_R] = { + .mask = INT_MSK2_GPIO0_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO0_F] = { + .mask = INT_MSK2_GPIO0_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO1_R] = { + .mask = INT_MSK2_GPIO1_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO1_F] = { + .mask = INT_MSK2_GPIO1_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO2_R] = { + .mask = INT_MSK2_GPIO2_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO2_F] = { + .mask = INT_MSK2_GPIO2_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO3_R] = { + .mask = INT_MSK2_GPIO3_R_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65911_IRQ_GPIO3_F] = { + .mask = INT_MSK2_GPIO3_F_IT_MSK_MASK, + .reg_offset = 1, + }, + + /* INT_STS2 */ + [TPS65911_IRQ_GPIO4_R] = { + .mask = INT_MSK3_GPIO4_R_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO4_F] = { + .mask = INT_MSK3_GPIO4_F_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO5_R] = { + .mask = INT_MSK3_GPIO5_R_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_GPIO5_F] = { + .mask = INT_MSK3_GPIO5_F_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_WTCHDG] = { + .mask = INT_MSK3_WTCHDG_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_VMBCH2_H] = { + .mask = INT_MSK3_VMBCH2_H_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_VMBCH2_L] = { + .mask = INT_MSK3_VMBCH2_L_IT_MSK_MASK, + .reg_offset = 2, + }, + [TPS65911_IRQ_PWRDN] = { + .mask = INT_MSK3_PWRDN_IT_MSK_MASK, + .reg_offset = 2, + }, +}; + +static const struct regmap_irq tps65910_irqs[] = { + /* INT_STS */ + [TPS65910_IRQ_VBAT_VMBDCH] = { + .mask = TPS65910_INT_MSK_VMBDCH_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_VBAT_VMHI] = { + .mask = TPS65910_INT_MSK_VMBHI_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRON] = { + .mask = TPS65910_INT_MSK_PWRON_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRON_LP] = { + .mask = TPS65910_INT_MSK_PWRON_LP_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_PWRHOLD] = { + .mask = TPS65910_INT_MSK_PWRHOLD_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_HOTDIE] = { + .mask = TPS65910_INT_MSK_HOTDIE_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_RTC_ALARM] = { + .mask = TPS65910_INT_MSK_RTC_ALARM_IT_MSK_MASK, + .reg_offset = 0, + }, + [TPS65910_IRQ_RTC_PERIOD] = { + .mask = TPS65910_INT_MSK_RTC_PERIOD_IT_MSK_MASK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [TPS65910_IRQ_GPIO_R] = { + .mask = TPS65910_INT_MSK2_GPIO0_F_IT_MSK_MASK, + .reg_offset = 1, + }, + [TPS65910_IRQ_GPIO_F] = { + .mask = TPS65910_INT_MSK2_GPIO0_R_IT_MSK_MASK, + .reg_offset = 1, + }, +}; + +static struct regmap_irq_chip tps65911_irq_chip = { + .name = "tps65910", + .irqs = tps65911_irqs, + .num_irqs = ARRAY_SIZE(tps65911_irqs), + .num_regs = 3, + .irq_reg_stride = 2, + .status_base = TPS65910_INT_STS, + .mask_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_STS, +}; + +static struct regmap_irq_chip tps65910_irq_chip = { + .name = "tps65910", + .irqs = tps65910_irqs, + .num_irqs = ARRAY_SIZE(tps65910_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .status_base = TPS65910_INT_STS, + .mask_base = TPS65910_INT_MSK, + .ack_base = TPS65910_INT_STS, +}; + +static int tps65910_irq_init(struct tps65910 *tps65910, int irq, + struct tps65910_platform_data *pdata) +{ + int ret = 0; + static struct regmap_irq_chip *tps6591x_irqs_chip; + + if (!irq) { + dev_warn(tps65910->dev, "No interrupt support, no core IRQ\n"); + return -EINVAL; + } + + if (!pdata) { + dev_warn(tps65910->dev, "No interrupt support, no pdata\n"); + return -EINVAL; + } + + switch (tps65910_chip_id(tps65910)) { + case TPS65910: + tps6591x_irqs_chip = &tps65910_irq_chip; + break; + case TPS65911: + tps6591x_irqs_chip = &tps65911_irq_chip; + break; + } + + tps65910->chip_irq = irq; + ret = regmap_add_irq_chip(tps65910->regmap, tps65910->chip_irq, + IRQF_ONESHOT, pdata->irq_base, + tps6591x_irqs_chip, &tps65910->irq_data); + if (ret < 0) + dev_warn(tps65910->dev, "Failed to add irq_chip %d\n", ret); + return ret; +} + +static int tps65910_irq_exit(struct tps65910 *tps65910) +{ + if (tps65910->chip_irq > 0) + regmap_del_irq_chip(tps65910->chip_irq, tps65910->irq_data); + return 0; +} + static bool is_volatile_reg(struct device *dev, unsigned int reg) { struct tps65910 *tps65910 = dev_get_drvdata(dev); @@ -270,7 +486,6 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, tps65910->dev = &i2c->dev; tps65910->i2c_client = i2c; tps65910->id = chip_id; - mutex_init(&tps65910->io_mutex); tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { @@ -279,14 +494,6 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, return ret; } - ret = mfd_add_devices(tps65910->dev, -1, - tps65910s, ARRAY_SIZE(tps65910s), - NULL, 0, NULL); - if (ret < 0) { - dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); - return ret; - } - init_data->irq = pmic_plat_data->irq; init_data->irq_base = pmic_plat_data->irq_base; @@ -299,6 +506,15 @@ static int tps65910_i2c_probe(struct i2c_client *i2c, pm_power_off = tps65910_power_off; } + ret = mfd_add_devices(tps65910->dev, -1, + tps65910s, ARRAY_SIZE(tps65910s), + NULL, 0, + regmap_irq_get_domain(tps65910->irq_data)); + if (ret < 0) { + dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret); + return ret; + } + return ret; } diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c new file mode 100644 index 00000000000..10b51f7dfff --- /dev/null +++ b/drivers/mfd/tps80031.c @@ -0,0 +1,574 @@ +/* + * tps80031.c -- TI TPS80031/TPS80032 mfd core driver. + * + * MFD core driver for TI TPS80031/TPS80032 Fully Integrated + * Power Management with Power Path and Battery Charger + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; 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 + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps80031.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +static struct resource tps80031_rtc_resources[] = { + { + .start = TPS80031_INT_RTC_ALARM, + .end = TPS80031_INT_RTC_ALARM, + .flags = IORESOURCE_IRQ, + }, +}; + +/* TPS80031 sub mfd devices */ +static struct mfd_cell tps80031_cell[] = { + { + .name = "tps80031-pmic", + }, + { + .name = "tps80031-clock", + }, + { + .name = "tps80031-rtc", + .num_resources = ARRAY_SIZE(tps80031_rtc_resources), + .resources = tps80031_rtc_resources, + }, + { + .name = "tps80031-gpadc", + }, + { + .name = "tps80031-fuel-gauge", + }, + { + .name = "tps80031-charger", + }, +}; + +static int tps80031_slave_address[TPS80031_NUM_SLAVES] = { + TPS80031_I2C_ID0_ADDR, + TPS80031_I2C_ID1_ADDR, + TPS80031_I2C_ID2_ADDR, + TPS80031_I2C_ID3_ADDR, +}; + +struct tps80031_pupd_data { + u8 reg; + u8 pullup_bit; + u8 pulldown_bit; +}; + +#define TPS80031_IRQ(_reg, _mask) \ + { \ + .reg_offset = (TPS80031_INT_MSK_LINE_##_reg) - \ + TPS80031_INT_MSK_LINE_A, \ + .mask = BIT(_mask), \ + } + +static const struct regmap_irq tps80031_main_irqs[] = { + [TPS80031_INT_PWRON] = TPS80031_IRQ(A, 0), + [TPS80031_INT_RPWRON] = TPS80031_IRQ(A, 1), + [TPS80031_INT_SYS_VLOW] = TPS80031_IRQ(A, 2), + [TPS80031_INT_RTC_ALARM] = TPS80031_IRQ(A, 3), + [TPS80031_INT_RTC_PERIOD] = TPS80031_IRQ(A, 4), + [TPS80031_INT_HOT_DIE] = TPS80031_IRQ(A, 5), + [TPS80031_INT_VXX_SHORT] = TPS80031_IRQ(A, 6), + [TPS80031_INT_SPDURATION] = TPS80031_IRQ(A, 7), + [TPS80031_INT_WATCHDOG] = TPS80031_IRQ(B, 0), + [TPS80031_INT_BAT] = TPS80031_IRQ(B, 1), + [TPS80031_INT_SIM] = TPS80031_IRQ(B, 2), + [TPS80031_INT_MMC] = TPS80031_IRQ(B, 3), + [TPS80031_INT_RES] = TPS80031_IRQ(B, 4), + [TPS80031_INT_GPADC_RT] = TPS80031_IRQ(B, 5), + [TPS80031_INT_GPADC_SW2_EOC] = TPS80031_IRQ(B, 6), + [TPS80031_INT_CC_AUTOCAL] = TPS80031_IRQ(B, 7), + [TPS80031_INT_ID_WKUP] = TPS80031_IRQ(C, 0), + [TPS80031_INT_VBUSS_WKUP] = TPS80031_IRQ(C, 1), + [TPS80031_INT_ID] = TPS80031_IRQ(C, 2), + [TPS80031_INT_VBUS] = TPS80031_IRQ(C, 3), + [TPS80031_INT_CHRG_CTRL] = TPS80031_IRQ(C, 4), + [TPS80031_INT_EXT_CHRG] = TPS80031_IRQ(C, 5), + [TPS80031_INT_INT_CHRG] = TPS80031_IRQ(C, 6), + [TPS80031_INT_RES2] = TPS80031_IRQ(C, 7), +}; + +static struct regmap_irq_chip tps80031_irq_chip = { + .name = "tps80031", + .irqs = tps80031_main_irqs, + .num_irqs = ARRAY_SIZE(tps80031_main_irqs), + .num_regs = 3, + .status_base = TPS80031_INT_STS_A, + .mask_base = TPS80031_INT_MSK_LINE_A, +}; + +#define PUPD_DATA(_reg, _pulldown_bit, _pullup_bit) \ + { \ + .reg = TPS80031_CFG_INPUT_PUPD##_reg, \ + .pulldown_bit = _pulldown_bit, \ + .pullup_bit = _pullup_bit, \ + } + +static const struct tps80031_pupd_data tps80031_pupds[] = { + [TPS80031_PREQ1] = PUPD_DATA(1, BIT(0), BIT(1)), + [TPS80031_PREQ2A] = PUPD_DATA(1, BIT(2), BIT(3)), + [TPS80031_PREQ2B] = PUPD_DATA(1, BIT(4), BIT(5)), + [TPS80031_PREQ2C] = PUPD_DATA(1, BIT(6), BIT(7)), + [TPS80031_PREQ3] = PUPD_DATA(2, BIT(0), BIT(1)), + [TPS80031_NRES_WARM] = PUPD_DATA(2, 0, BIT(2)), + [TPS80031_PWM_FORCE] = PUPD_DATA(2, BIT(5), 0), + [TPS80031_CHRG_EXT_CHRG_STATZ] = PUPD_DATA(2, 0, BIT(6)), + [TPS80031_SIM] = PUPD_DATA(3, BIT(0), BIT(1)), + [TPS80031_MMC] = PUPD_DATA(3, BIT(2), BIT(3)), + [TPS80031_GPADC_START] = PUPD_DATA(3, BIT(4), 0), + [TPS80031_DVSI2C_SCL] = PUPD_DATA(4, 0, BIT(0)), + [TPS80031_DVSI2C_SDA] = PUPD_DATA(4, 0, BIT(1)), + [TPS80031_CTLI2C_SCL] = PUPD_DATA(4, 0, BIT(2)), + [TPS80031_CTLI2C_SDA] = PUPD_DATA(4, 0, BIT(3)), +}; +static struct tps80031 *tps80031_power_off_dev; + +int tps80031_ext_power_req_config(struct device *dev, + unsigned long ext_ctrl_flag, int preq_bit, + int state_reg_add, int trans_reg_add) +{ + u8 res_ass_reg = 0; + int preq_mask_bit = 0; + int ret; + + if (!(ext_ctrl_flag & TPS80031_EXT_PWR_REQ)) + return 0; + + if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ1) { + res_ass_reg = TPS80031_PREQ1_RES_ASS_A + (preq_bit >> 3); + preq_mask_bit = 5; + } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ2) { + res_ass_reg = TPS80031_PREQ2_RES_ASS_A + (preq_bit >> 3); + preq_mask_bit = 6; + } else if (ext_ctrl_flag & TPS80031_PWR_REQ_INPUT_PREQ3) { + res_ass_reg = TPS80031_PREQ3_RES_ASS_A + (preq_bit >> 3); + preq_mask_bit = 7; + } + + /* Configure REQ_ASS registers */ + ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, res_ass_reg, + BIT(preq_bit & 0x7)); + if (ret < 0) { + dev_err(dev, "reg 0x%02x setbit failed, err = %d\n", + res_ass_reg, ret); + return ret; + } + + /* Unmask the PREQ */ + ret = tps80031_clr_bits(dev, TPS80031_SLAVE_ID1, + TPS80031_PHOENIX_MSK_TRANSITION, BIT(preq_mask_bit)); + if (ret < 0) { + dev_err(dev, "reg 0x%02x clrbit failed, err = %d\n", + TPS80031_PHOENIX_MSK_TRANSITION, ret); + return ret; + } + + /* Switch regulator control to resource now */ + if (ext_ctrl_flag & (TPS80031_PWR_REQ_INPUT_PREQ2 | + TPS80031_PWR_REQ_INPUT_PREQ3)) { + ret = tps80031_update(dev, TPS80031_SLAVE_ID1, state_reg_add, + 0x0, TPS80031_STATE_MASK); + if (ret < 0) + dev_err(dev, "reg 0x%02x update failed, err = %d\n", + state_reg_add, ret); + } else { + ret = tps80031_update(dev, TPS80031_SLAVE_ID1, trans_reg_add, + TPS80031_TRANS_SLEEP_OFF, + TPS80031_TRANS_SLEEP_MASK); + if (ret < 0) + dev_err(dev, "reg 0x%02x update failed, err = %d\n", + trans_reg_add, ret); + } + return ret; +} +EXPORT_SYMBOL_GPL(tps80031_ext_power_req_config); + +static void tps80031_power_off(void) +{ + dev_info(tps80031_power_off_dev->dev, "switching off PMU\n"); + tps80031_write(tps80031_power_off_dev->dev, TPS80031_SLAVE_ID1, + TPS80031_PHOENIX_DEV_ON, TPS80031_DEVOFF); +} + +static void tps80031_pupd_init(struct tps80031 *tps80031, + struct tps80031_platform_data *pdata) +{ + struct tps80031_pupd_init_data *pupd_init_data = pdata->pupd_init_data; + int data_size = pdata->pupd_init_data_size; + int i; + + for (i = 0; i < data_size; ++i) { + struct tps80031_pupd_init_data *pupd_init = &pupd_init_data[i]; + const struct tps80031_pupd_data *pupd = + &tps80031_pupds[pupd_init->input_pin]; + u8 update_value = 0; + u8 update_mask = pupd->pulldown_bit | pupd->pullup_bit; + + if (pupd_init->setting == TPS80031_PUPD_PULLDOWN) + update_value = pupd->pulldown_bit; + else if (pupd_init->setting == TPS80031_PUPD_PULLUP) + update_value = pupd->pullup_bit; + + tps80031_update(tps80031->dev, TPS80031_SLAVE_ID1, pupd->reg, + update_value, update_mask); + } +} + +static int tps80031_init_ext_control(struct tps80031 *tps80031, + struct tps80031_platform_data *pdata) +{ + struct device *dev = tps80031->dev; + int ret; + int i; + + /* Clear all external control for this rail */ + for (i = 0; i < 9; ++i) { + ret = tps80031_write(dev, TPS80031_SLAVE_ID1, + TPS80031_PREQ1_RES_ASS_A + i, 0); + if (ret < 0) { + dev_err(dev, "reg 0x%02x write failed, err = %d\n", + TPS80031_PREQ1_RES_ASS_A + i, ret); + return ret; + } + } + + /* Mask the PREQ */ + ret = tps80031_set_bits(dev, TPS80031_SLAVE_ID1, + TPS80031_PHOENIX_MSK_TRANSITION, 0x7 << 5); + if (ret < 0) { + dev_err(dev, "reg 0x%02x set_bits failed, err = %d\n", + TPS80031_PHOENIX_MSK_TRANSITION, ret); + return ret; + } + return ret; +} + +static int __devinit tps80031_irq_init(struct tps80031 *tps80031, int irq, + int irq_base) +{ + struct device *dev = tps80031->dev; + int i, ret; + + /* + * The MASK register used for updating status register when + * interrupt occurs and LINE register used to pass the status + * to actual interrupt line. As per datasheet: + * When INT_MSK_LINE [i] is set to 1, the associated interrupt + * number i is INT line masked, which means that no interrupt is + * generated on the INT line. + * When INT_MSK_LINE [i] is set to 0, the associated interrupt + * number i is line enabled: An interrupt is generated on the + * INT line. + * In any case, the INT_STS [i] status bit may or may not be updated, + * only linked to the INT_MSK_STS [i] configuration register bit. + * + * When INT_MSK_STS [i] is set to 1, the associated interrupt number + * i is status masked, which means that no interrupt is stored in + * the INT_STS[i] status bit. Note that no interrupt number i is + * generated on the INT line, even if the INT_MSK_LINE [i] register + * bit is set to 0. + * When INT_MSK_STS [i] is set to 0, the associated interrupt number i + * is status enabled: An interrupt status is updated in the INT_STS [i] + * register. The interrupt may or may not be generated on the INT line, + * depending on the INT_MSK_LINE [i] configuration register bit. + */ + for (i = 0; i < 3; i++) + tps80031_write(dev, TPS80031_SLAVE_ID2, + TPS80031_INT_MSK_STS_A + i, 0x00); + + ret = regmap_add_irq_chip(tps80031->regmap[TPS80031_SLAVE_ID2], irq, + IRQF_ONESHOT, irq_base, + &tps80031_irq_chip, &tps80031->irq_data); + if (ret < 0) { + dev_err(dev, "add irq failed, err = %d\n", ret); + return ret; + } + return ret; +} + +static bool rd_wr_reg_id0(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_SMPS1_CFG_FORCE ... TPS80031_SMPS2_CFG_VOLTAGE: + return true; + default: + return false; + } +} + +static bool rd_wr_reg_id1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_SECONDS_REG ... TPS80031_RTC_RESET_STATUS_REG: + case TPS80031_VALIDITY0 ... TPS80031_VALIDITY7: + case TPS80031_PHOENIX_START_CONDITION ... TPS80031_KEY_PRESS_DUR_CFG: + case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE: + case TPS80031_BROADCAST_ADDR_ALL ... TPS80031_BROADCAST_ADDR_CLK_RST: + case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE: + case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE: + case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C: + case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING: + case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD: + case TPS80031_BACKUP_REG: + return true; + default: + return false; + } +} + +static bool is_volatile_reg_id1(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_SMPS4_CFG_TRANS ... TPS80031_SMPS3_CFG_VOLTAGE: + case TPS80031_VANA_CFG_TRANS ... TPS80031_LDO7_CFG_VOLTAGE: + case TPS80031_REGEN1_CFG_TRANS ... TPS80031_TMP_CFG_STATE: + case TPS80031_PREQ1_RES_ASS_A ... TPS80031_PREQ3_RES_ASS_C: + case TPS80031_SMPS_OFFSET ... TPS80031_BATDEBOUNCING: + case TPS80031_CFG_INPUT_PUPD1 ... TPS80031_CFG_SMPS_PD: + return true; + default: + return false; + } +} + +static bool rd_wr_reg_id2(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_USB_VENDOR_ID_LSB ... TPS80031_USB_OTG_REVISION: + case TPS80031_GPADC_CTRL ... TPS80031_CTRL_P1: + case TPS80031_RTCH0_LSB ... TPS80031_GPCH0_MSB: + case TPS80031_TOGGLE1 ... TPS80031_VIBMODE: + case TPS80031_PWM1ON ... TPS80031_PWM2OFF: + case TPS80031_FG_REG_00 ... TPS80031_FG_REG_11: + case TPS80031_INT_STS_A ... TPS80031_INT_MSK_STS_C: + case TPS80031_CONTROLLER_CTRL2 ... TPS80031_LED_PWM_CTRL2: + return true; + default: + return false; + } +} + +static bool rd_wr_reg_id3(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS80031_GPADC_TRIM0 ... TPS80031_GPADC_TRIM18: + return true; + default: + return false; + } +} + +static const struct regmap_config tps80031_regmap_configs[] = { + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id0, + .readable_reg = rd_wr_reg_id0, + .max_register = TPS80031_MAX_REGISTER, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id1, + .readable_reg = rd_wr_reg_id1, + .volatile_reg = is_volatile_reg_id1, + .max_register = TPS80031_MAX_REGISTER, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id2, + .readable_reg = rd_wr_reg_id2, + .max_register = TPS80031_MAX_REGISTER, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id3, + .readable_reg = rd_wr_reg_id3, + .max_register = TPS80031_MAX_REGISTER, + }, +}; + +static int __devinit tps80031_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps80031_platform_data *pdata = client->dev.platform_data; + struct tps80031 *tps80031; + int ret; + uint8_t es_version; + uint8_t ep_ver; + int i; + + if (!pdata) { + dev_err(&client->dev, "tps80031 requires platform data\n"); + return -EINVAL; + } + + tps80031 = devm_kzalloc(&client->dev, sizeof(*tps80031), GFP_KERNEL); + if (!tps80031) { + dev_err(&client->dev, "Malloc failed for tps80031\n"); + return -ENOMEM; + } + + for (i = 0; i < TPS80031_NUM_SLAVES; i++) { + if (tps80031_slave_address[i] == client->addr) + tps80031->clients[i] = client; + else + tps80031->clients[i] = i2c_new_dummy(client->adapter, + tps80031_slave_address[i]); + if (!tps80031->clients[i]) { + dev_err(&client->dev, "can't attach client %d\n", i); + ret = -ENOMEM; + goto fail_client_reg; + } + + i2c_set_clientdata(tps80031->clients[i], tps80031); + tps80031->regmap[i] = devm_regmap_init_i2c(tps80031->clients[i], + &tps80031_regmap_configs[i]); + if (IS_ERR(tps80031->regmap[i])) { + ret = PTR_ERR(tps80031->regmap[i]); + dev_err(&client->dev, + "regmap %d init failed, err %d\n", i, ret); + goto fail_client_reg; + } + } + + ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3, + TPS80031_JTAGVERNUM, &es_version); + if (ret < 0) { + dev_err(&client->dev, + "Silicon version number read failed: %d\n", ret); + goto fail_client_reg; + } + + ret = tps80031_read(&client->dev, TPS80031_SLAVE_ID3, + TPS80031_EPROM_REV, &ep_ver); + if (ret < 0) { + dev_err(&client->dev, + "Silicon eeprom version read failed: %d\n", ret); + goto fail_client_reg; + } + + dev_info(&client->dev, "ES version 0x%02x and EPROM version 0x%02x\n", + es_version, ep_ver); + tps80031->es_version = es_version; + tps80031->dev = &client->dev; + i2c_set_clientdata(client, tps80031); + tps80031->chip_info = id->driver_data; + + ret = tps80031_irq_init(tps80031, client->irq, pdata->irq_base); + if (ret) { + dev_err(&client->dev, "IRQ init failed: %d\n", ret); + goto fail_client_reg; + } + + tps80031_pupd_init(tps80031, pdata); + + tps80031_init_ext_control(tps80031, pdata); + + ret = mfd_add_devices(tps80031->dev, -1, + tps80031_cell, ARRAY_SIZE(tps80031_cell), + NULL, 0, + regmap_irq_get_domain(tps80031->irq_data)); + if (ret < 0) { + dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); + goto fail_mfd_add; + } + + if (pdata->use_power_off && !pm_power_off) { + tps80031_power_off_dev = tps80031; + pm_power_off = tps80031_power_off; + } + return 0; + +fail_mfd_add: + regmap_del_irq_chip(client->irq, tps80031->irq_data); + +fail_client_reg: + for (i = 0; i < TPS80031_NUM_SLAVES; i++) { + if (tps80031->clients[i] && (tps80031->clients[i] != client)) + i2c_unregister_device(tps80031->clients[i]); + } + return ret; +} + +static int __devexit tps80031_remove(struct i2c_client *client) +{ + struct tps80031 *tps80031 = i2c_get_clientdata(client); + int i; + + if (tps80031_power_off_dev == tps80031) { + tps80031_power_off_dev = NULL; + pm_power_off = NULL; + } + + mfd_remove_devices(tps80031->dev); + + regmap_del_irq_chip(client->irq, tps80031->irq_data); + + for (i = 0; i < TPS80031_NUM_SLAVES; i++) { + if (tps80031->clients[i] != client) + i2c_unregister_device(tps80031->clients[i]); + } + return 0; +} + +static const struct i2c_device_id tps80031_id_table[] = { + { "tps80031", TPS80031 }, + { "tps80032", TPS80032 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps80031_id_table); + +static struct i2c_driver tps80031_driver = { + .driver = { + .name = "tps80031", + .owner = THIS_MODULE, + }, + .probe = tps80031_probe, + .remove = __devexit_p(tps80031_remove), + .id_table = tps80031_id_table, +}; + +static int __init tps80031_init(void) +{ + return i2c_add_driver(&tps80031_driver); +} +subsys_initcall(tps80031_init); + +static void __exit tps80031_exit(void) +{ + i2c_del_driver(&tps80031_driver); +} +module_exit(tps80031_exit); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("TPS80031 core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 11b76c0109f..4f3baadd003 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -32,6 +32,7 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/device.h> @@ -65,9 +66,6 @@ /* Triton Core internal information (BEGIN) */ -/* Last - for index max*/ -#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG - #define TWL_NUM_SLAVES 4 #define SUB_CHIP_ID0 0 @@ -171,13 +169,7 @@ EXPORT_SYMBOL(twl_rev); /* Structure for each TWL4030/TWL6030 Slave */ struct twl_client { struct i2c_client *client; - u8 address; - - /* max numb of i2c_msg required is for read =2 */ - struct i2c_msg xfer_msg[2]; - - /* To lock access to xfer_msg */ - struct mutex xfer_lock; + struct regmap *regmap; }; static struct twl_client twl_modules[TWL_NUM_SLAVES]; @@ -189,7 +181,7 @@ struct twl_mapping { }; static struct twl_mapping *twl_map; -static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { +static struct twl_mapping twl4030_map[] = { /* * NOTE: don't change this table without updating the * <linux/i2c/twl.h> defines for TWL4030_MODULE_* @@ -197,34 +189,62 @@ static struct twl_mapping twl4030_map[TWL4030_MODULE_LAST + 1] = { */ { 0, TWL4030_BASEADD_USB }, - { 1, TWL4030_BASEADD_AUDIO_VOICE }, { 1, TWL4030_BASEADD_GPIO }, { 1, TWL4030_BASEADD_INTBR }, { 1, TWL4030_BASEADD_PIH }, - { 1, TWL4030_BASEADD_TEST }, + { 1, TWL4030_BASEADD_TEST }, { 2, TWL4030_BASEADD_KEYPAD }, { 2, TWL4030_BASEADD_MADC }, { 2, TWL4030_BASEADD_INTERRUPTS }, { 2, TWL4030_BASEADD_LED }, + { 2, TWL4030_BASEADD_MAIN_CHARGE }, { 2, TWL4030_BASEADD_PRECHARGE }, { 2, TWL4030_BASEADD_PWM0 }, { 2, TWL4030_BASEADD_PWM1 }, { 2, TWL4030_BASEADD_PWMA }, + { 2, TWL4030_BASEADD_PWMB }, { 2, TWL5031_BASEADD_ACCESSORY }, { 2, TWL5031_BASEADD_INTERRUPTS }, - { 3, TWL4030_BASEADD_BACKUP }, { 3, TWL4030_BASEADD_INT }, + { 3, TWL4030_BASEADD_PM_MASTER }, { 3, TWL4030_BASEADD_PM_RECEIVER }, { 3, TWL4030_BASEADD_RTC }, { 3, TWL4030_BASEADD_SECURED_REG }, }; +static struct regmap_config twl4030_regmap_config[4] = { + { + /* Address 0x48 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x49 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x4a */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x4b */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, +}; + static struct twl_mapping twl6030_map[] = { /* * NOTE: don't change this table without updating the @@ -254,14 +274,35 @@ static struct twl_mapping twl6030_map[] = { { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, + { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER }, { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, }; +static struct regmap_config twl6030_regmap_config[3] = { + { + /* Address 0x48 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x49 */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, + { + /* Address 0x4a */ + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + }, +}; + /*----------------------------------------------------------------------*/ /* Exported Functions */ @@ -283,9 +324,8 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int ret; int sid; struct twl_client *twl; - struct i2c_msg *msg; - if (unlikely(mod_no > TWL_MODULE_LAST)) { + if (unlikely(mod_no >= TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -301,32 +341,14 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } twl = &twl_modules[sid]; - mutex_lock(&twl->xfer_lock); - /* - * [MSG1]: fill the register address data - * fill the data Tx buffer - */ - msg = &twl->xfer_msg[0]; - msg->addr = twl->address; - msg->len = num_bytes + 1; - msg->flags = 0; - msg->buf = value; - /* over write the first byte of buffer with the register address */ - *value = twl_map[mod_no].base + reg; - ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 1); - mutex_unlock(&twl->xfer_lock); - - /* i2c_transfer returns number of messages transferred */ - if (ret != 1) { - pr_err("%s: i2c_write failed to transfer all messages\n", - DRIVER_NAME); - if (ret < 0) - return ret; - else - return -EIO; - } else { - return 0; - } + ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, + value, num_bytes); + + if (ret) + pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", + DRIVER_NAME, mod_no, reg, num_bytes); + + return ret; } EXPORT_SYMBOL(twl_i2c_write); @@ -342,12 +364,10 @@ EXPORT_SYMBOL(twl_i2c_write); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { int ret; - u8 val; int sid; struct twl_client *twl; - struct i2c_msg *msg; - if (unlikely(mod_no > TWL_MODULE_LAST)) { + if (unlikely(mod_no >= TWL_MODULE_LAST)) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -363,34 +383,14 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } twl = &twl_modules[sid]; - mutex_lock(&twl->xfer_lock); - /* [MSG1] fill the register address data */ - msg = &twl->xfer_msg[0]; - msg->addr = twl->address; - msg->len = 1; - msg->flags = 0; /* Read the register value */ - val = twl_map[mod_no].base + reg; - msg->buf = &val; - /* [MSG2] fill the data rx buffer */ - msg = &twl->xfer_msg[1]; - msg->addr = twl->address; - msg->flags = I2C_M_RD; /* Read the register value */ - msg->len = num_bytes; /* only n bytes */ - msg->buf = value; - ret = i2c_transfer(twl->client->adapter, twl->xfer_msg, 2); - mutex_unlock(&twl->xfer_lock); - - /* i2c_transfer returns number of messages transferred */ - if (ret != 2) { - pr_err("%s: i2c_read failed to transfer all messages\n", - DRIVER_NAME); - if (ret < 0) - return ret; - else - return -EIO; - } else { - return 0; - } + ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg, + value, num_bytes); + + if (ret) + pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", + DRIVER_NAME, mod_no, reg, num_bytes); + + return ret; } EXPORT_SYMBOL(twl_i2c_read); @@ -404,12 +404,7 @@ EXPORT_SYMBOL(twl_i2c_read); */ int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg) { - - /* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */ - u8 temp_buffer[2] = { 0 }; - /* offset 1 contains the data */ - temp_buffer[1] = value; - return twl_i2c_write(mod_no, temp_buffer, reg, 1); + return twl_i2c_write(mod_no, &value, reg, 1); } EXPORT_SYMBOL(twl_i2c_write_u8); @@ -646,8 +641,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, return PTR_ERR(child); } - if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc) { - child = add_child(2, "twl4030_madc", + if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc && + twl_class_is_4030()) { + child = add_child(SUB_CHIP_ID2, "twl4030_madc", pdata->madc, sizeof(*pdata->madc), true, irq_base + MADC_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -663,15 +659,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, * HW security concerns, and "least privilege". */ sub_chip_id = twl_map[TWL_MODULE_RTC].sid; - child = add_child(sub_chip_id, "twl_rtc", - NULL, 0, + child = add_child(sub_chip_id, "twl_rtc", NULL, 0, true, irq_base + RTC_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } - if (IS_ENABLED(CONFIG_PWM_TWL6030) && twl_class_is_6030()) { - child = add_child(SUB_CHIP_ID1, "twl6030-pwm", NULL, 0, + if (IS_ENABLED(CONFIG_PWM_TWL)) { + child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0, + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + if (IS_ENABLED(CONFIG_PWM_TWL_LED)) { + child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); @@ -723,9 +725,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } - child = add_child(0, "twl4030_usb", - pdata->usb, sizeof(*pdata->usb), - true, + child = add_child(SUB_CHIP_ID0, "twl4030_usb", + pdata->usb, sizeof(*pdata->usb), true, /* irq0 = USB_PRES, irq1 = USB */ irq_base + USB_PRES_INTR_OFFSET, irq_base + USB_INTR_OFFSET); @@ -773,9 +774,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, pdata->usb->features = features; - child = add_child(0, "twl6030_usb", - pdata->usb, sizeof(*pdata->usb), - true, + child = add_child(SUB_CHIP_ID0, "twl6030_usb", + pdata->usb, sizeof(*pdata->usb), true, /* irq1 = VBUS_PRES, irq0 = USB ID */ irq_base + USBOTG_INTR_OFFSET, irq_base + USB_PRES_INTR_OFFSET); @@ -799,22 +799,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) { - child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); + child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0, + false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) { - child = add_child(1, "twl4030_pwrbutton", - NULL, 0, true, irq_base + 8 + 0, 0); + child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0, + true, irq_base + 8 + 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio && twl_class_is_4030()) { - sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl4030-audio", + child = add_child(SUB_CHIP_ID1, "twl4030-audio", pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) @@ -1054,7 +1054,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci && !(features & (TPS_SUBSET | TWL5031))) { - child = add_child(3, "twl4030_bci", + child = add_child(SUB_CHIP_ID3, "twl4030_bci", pdata->bci, sizeof(*pdata->bci), false, /* irq0 = CHG_PRES, irq1 = BCI */ irq_base + BCI_PRES_INTR_OFFSET, @@ -1077,8 +1077,8 @@ static inline int __init protect_pm_master(void) { int e = 0; - e = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); + e = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); return e; } @@ -1086,12 +1086,10 @@ static inline int __init unprotect_pm_master(void) { int e = 0; - e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); - e |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); + e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); + e |= twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); return e; } @@ -1176,6 +1174,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) struct twl4030_platform_data *pdata = client->dev.platform_data; struct device_node *node = client->dev.of_node; struct platform_device *pdev; + struct regmap_config *twl_regmap_config; int irq_base = 0; int status; unsigned i, num_slaves; @@ -1229,22 +1228,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; + twl_regmap_config = twl6030_regmap_config; num_slaves = TWL_NUM_SLAVES - 1; } else { twl_id = TWL4030_CLASS_ID; twl_map = &twl4030_map[0]; + twl_regmap_config = twl4030_regmap_config; num_slaves = TWL_NUM_SLAVES; } for (i = 0; i < num_slaves; i++) { struct twl_client *twl = &twl_modules[i]; - twl->address = client->addr + i; if (i == 0) { twl->client = client; } else { twl->client = i2c_new_dummy(client->adapter, - twl->address); + client->addr + i); if (!twl->client) { dev_err(&client->dev, "can't attach client %d\n", i); @@ -1252,7 +1252,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) goto fail; } } - mutex_init(&twl->xfer_lock); + + twl->regmap = devm_regmap_init_i2c(twl->client, + &twl_regmap_config[i]); + if (IS_ERR(twl->regmap)) { + status = PTR_ERR(twl->regmap); + dev_err(&client->dev, + "Failed to allocate regmap %d, err: %d\n", i, + status); + goto fail; + } } inuse = true; diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index cdd1173ed4e..a5f9888aa19 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -295,8 +295,8 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid) irqreturn_t ret; u8 pih_isr; - ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr, - REG_PIH_ISR_P1); + ret = twl_i2c_read_u8(TWL_MODULE_PIH, &pih_isr, + REG_PIH_ISR_P1); if (ret) { pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret); return IRQ_NONE; @@ -501,7 +501,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) } imr; /* byte[0] gets overwritten as we write ... */ - imr.word = cpu_to_le32(agent->imr << 8); + imr.word = cpu_to_le32(agent->imr); agent->imr_change_pending = false; /* write the whole mask ... simpler than subsetting it */ @@ -526,7 +526,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) * any processor on the other IRQ line, EDR registers are * shared. */ - status = twl_i2c_read(sih->module, bytes + 1, + status = twl_i2c_read(sih->module, bytes, sih->edr_offset, sih->bytes_edr); if (status) { pr_err("twl4030: %s, %s --> %d\n", __func__, @@ -538,7 +538,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data) while (edge_change) { int i = fls(edge_change) - 1; struct irq_data *idata; - int byte = 1 + (i >> 2); + int byte = i >> 2; int off = (i & 0x3) * 2; unsigned int type; diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index a39dcf3e213..88ff9dc8330 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -173,7 +173,7 @@ static int twl4030battery_temperature(int raw_volt) 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, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, REG_BCICTL2); if (ret < 0) return ret; @@ -196,7 +196,7 @@ static int twl4030battery_current(int raw_volt) int ret; u8 val; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCI_BCICTL1); if (ret) return ret; @@ -635,7 +635,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, int ret; u8 regval; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ®val, TWL4030_BCI_BCICTL1); if (ret) { dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", @@ -646,7 +646,7 @@ static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, 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, + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", @@ -668,7 +668,7 @@ 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, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ®val, TWL4030_MADC_CTRL1); if (ret) { dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", @@ -725,7 +725,7 @@ static int twl4030_madc_probe(struct platform_device *pdev) if (ret < 0) goto err_current_generator; - ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, ®val, TWL4030_BCI_BCICTL1); if (ret) { dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", @@ -733,7 +733,7 @@ static int twl4030_madc_probe(struct platform_device *pdev) goto err_i2c; } regval |= TWL4030_BCI_MESBAT; - ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, regval, TWL4030_BCI_BCICTL1); if (ret) { dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index a5332063183..4dae241e501 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -128,12 +128,10 @@ static int twl4030_write_script_byte(u8 address, u8 byte) { int err; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_MEMORY_ADDRESS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_MEMORY_ADDRESS); if (err) goto out; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte, - R_MEMORY_DATA); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, byte, R_MEMORY_DATA); out: return err; } @@ -189,19 +187,16 @@ static int twl4030_config_wakeup3_sequence(u8 address) u8 data; /* Set SLEEP to ACTIVE SEQ address for P3 */ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_S2A3); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_S2A3); if (err) goto out; /* P3 LVL_WAKEUP should be on LEVEL */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_P3_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P3_SW_EVENTS); if (err) goto out; data |= LVL_WAKEUP; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, - R_P3_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P3_SW_EVENTS); out: if (err) pr_err("TWL4030 wakeup sequence for P3 config error\n"); @@ -214,43 +209,38 @@ static int twl4030_config_wakeup12_sequence(u8 address) u8 data; /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_S2A12); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_S2A12); if (err) goto out; /* P1/P2 LVL_WAKEUP should be on LEVEL */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_P1_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P1_SW_EVENTS); if (err) goto out; data |= LVL_WAKEUP; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, - R_P1_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P1_SW_EVENTS); if (err) goto out; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_P2_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, R_P2_SW_EVENTS); if (err) goto out; data |= LVL_WAKEUP; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, - R_P2_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, R_P2_SW_EVENTS); if (err) goto out; if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) { /* Disabling AC charger effect on sleep-active transitions */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, - R_CFG_P1_TRANSITION); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data, + R_CFG_P1_TRANSITION); if (err) goto out; data &= ~(1<<1); - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data , - R_CFG_P1_TRANSITION); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data, + R_CFG_P1_TRANSITION); if (err) goto out; } @@ -267,8 +257,7 @@ static int twl4030_config_sleep_sequence(u8 address) int err; /* Set ACTIVE to SLEEP SEQ address in T2 memory*/ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_A2S); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_A2S); if (err) pr_err("TWL4030 sleep sequence config error\n"); @@ -282,42 +271,35 @@ static int twl4030_config_warmreset_sequence(u8 address) u8 rd_data; /* Set WARM RESET SEQ address for P1 */ - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, - R_SEQ_ADD_WARM); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, address, R_SEQ_ADD_WARM); if (err) goto out; /* P1/P2/P3 enable WARMRESET */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, - R_P1_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P1_SW_EVENTS); if (err) goto out; rd_data |= ENABLE_WARMRESET; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, - R_P1_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P1_SW_EVENTS); if (err) goto out; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, - R_P2_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P2_SW_EVENTS); if (err) goto out; rd_data |= ENABLE_WARMRESET; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, - R_P2_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P2_SW_EVENTS); if (err) goto out; - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, - R_P3_SW_EVENTS); + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &rd_data, R_P3_SW_EVENTS); if (err) goto out; rd_data |= ENABLE_WARMRESET; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, - R_P3_SW_EVENTS); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, rd_data, R_P3_SW_EVENTS); out: if (err) pr_err("TWL4030 warmreset seq config error\n"); @@ -341,7 +323,7 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) rconfig_addr = res_config_addrs[rconfig->resource]; /* Set resource group */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp, + err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &grp, rconfig_addr + DEV_GRP_OFFSET); if (err) { pr_err("TWL4030 Resource %d group could not be read\n", @@ -352,7 +334,7 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) { grp &= ~DEV_GRP_MASK; grp |= rconfig->devgroup << DEV_GRP_SHIFT; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, grp, rconfig_addr + DEV_GRP_OFFSET); if (err < 0) { pr_err("TWL4030 failed to program devgroup\n"); @@ -361,7 +343,7 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) } /* Set resource types */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type, + err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &type, rconfig_addr + TYPE_OFFSET); if (err < 0) { pr_err("TWL4030 Resource %d type could not be read\n", @@ -379,7 +361,7 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) type |= rconfig->type2 << TYPE2_SHIFT; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, type, rconfig_addr + TYPE_OFFSET); if (err < 0) { pr_err("TWL4030 failed to program resource type\n"); @@ -387,7 +369,7 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) } /* Set remap states */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap, + err = twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &remap, rconfig_addr + REMAP_OFFSET); if (err < 0) { pr_err("TWL4030 Resource %d remap could not be read\n", @@ -405,7 +387,7 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig) remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + err = twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, remap, rconfig_addr + REMAP_OFFSET); if (err < 0) { @@ -463,49 +445,47 @@ int twl4030_remove_script(u8 flags) { int err = 0; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) { pr_err("twl4030: unable to unlock PROTECT_KEY\n"); return err; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) { pr_err("twl4030: unable to unlock PROTECT_KEY\n"); return err; } if (flags & TWL4030_WRST_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_WARM); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_WARM); if (err) return err; } if (flags & TWL4030_WAKEUP12_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_S2A12); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_S2A12); if (err) return err; } if (flags & TWL4030_WAKEUP3_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_S2A3); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_S2A3); if (err) return err; } if (flags & TWL4030_SLEEP_SCRIPT) { - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, - R_SEQ_ADD_A2S); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, END_OF_SCRIPT, + R_SEQ_ADD_A2S); if (err) return err; } - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) pr_err("TWL4030 Unable to relock registers\n"); @@ -521,7 +501,7 @@ void twl4030_power_off(void) { int err; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, PWR_DEVOFF, + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, PWR_DEVOFF, TWL4030_PM_MASTER_P1_SW_EVENTS); if (err) pr_err("TWL4030 Unable to power off\n"); @@ -534,15 +514,13 @@ void twl4030_power_init(struct twl4030_power_data *twl4030_scripts) struct twl4030_resconfig *resconfig; u8 val, address = twl4030_start_script_address; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) goto unlock; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, - TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) goto unlock; @@ -567,14 +545,14 @@ void twl4030_power_init(struct twl4030_power_data *twl4030_scripts) /* Board has to be wired properly to use this feature */ if (twl4030_scripts->use_poweroff && !pm_power_off) { /* Default for SEQ_OFFSYNC is set, lets ensure this */ - err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val, + err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val, TWL4030_PM_MASTER_CFG_P123_TRANSITION); if (err) { pr_warning("TWL4030 Unable to read registers\n"); } else if (!(val & SEQ_OFFSYNC)) { val |= SEQ_OFFSYNC; - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val, + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val, TWL4030_PM_MASTER_CFG_P123_TRANSITION); if (err) { pr_err("TWL4030 Unable to setup SEQ_OFFSYNC\n"); @@ -586,8 +564,8 @@ void twl4030_power_init(struct twl4030_power_data *twl4030_scripts) } relock: - err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); + err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); if (err) pr_err("TWL4030 Unable to relock registers\n"); return; diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index b76902f1e44..277a8dba42d 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -355,7 +355,7 @@ int twl6030_init_irq(struct device *dev, int irq_num) static struct irq_chip twl6030_irq_chip; int status = 0; int i; - u8 mask[4]; + u8 mask[3]; nr_irqs = TWL6030_NR_IRQS; @@ -370,9 +370,9 @@ int twl6030_init_irq(struct device *dev, int irq_num) irq_end = irq_base + nr_irqs; + mask[0] = 0xFF; mask[1] = 0xFF; mask[2] = 0xFF; - mask[3] = 0xFF; /* mask all int lines */ twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3); diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c deleted file mode 100644 index 4b42543da22..00000000000 --- a/drivers/mfd/twl6040-irq.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Interrupt controller support for TWL6040 - * - * Author: Misael Lopez Cruz <misael.lopez@ti.com> - * - * Copyright: (C) 2011 Texas Instruments, Inc. - * - * 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/kernel.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/irq.h> -#include <linux/of.h> -#include <linux/irqdomain.h> -#include <linux/interrupt.h> -#include <linux/mfd/core.h> -#include <linux/mfd/twl6040.h> - -struct twl6040_irq_data { - int mask; - int status; -}; - -static struct twl6040_irq_data twl6040_irqs[] = { - { - .mask = TWL6040_THMSK, - .status = TWL6040_THINT, - }, - { - .mask = TWL6040_PLUGMSK, - .status = TWL6040_PLUGINT | TWL6040_UNPLUGINT, - }, - { - .mask = TWL6040_HOOKMSK, - .status = TWL6040_HOOKINT, - }, - { - .mask = TWL6040_HFMSK, - .status = TWL6040_HFINT, - }, - { - .mask = TWL6040_VIBMSK, - .status = TWL6040_VIBINT, - }, - { - .mask = TWL6040_READYMSK, - .status = TWL6040_READYINT, - }, -}; - -static inline -struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040, - int irq) -{ - return &twl6040_irqs[irq - twl6040->irq_base]; -} - -static void twl6040_irq_lock(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - - mutex_lock(&twl6040->irq_mutex); -} - -static void twl6040_irq_sync_unlock(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - - /* write back to hardware any change in irq mask */ - if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) { - twl6040->irq_masks_cache = twl6040->irq_masks_cur; - twl6040_reg_write(twl6040, TWL6040_REG_INTMR, - twl6040->irq_masks_cur); - } - - mutex_unlock(&twl6040->irq_mutex); -} - -static void twl6040_irq_enable(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, - data->irq); - - twl6040->irq_masks_cur &= ~irq_data->mask; -} - -static void twl6040_irq_disable(struct irq_data *data) -{ - struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); - struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, - data->irq); - - twl6040->irq_masks_cur |= irq_data->mask; -} - -static struct irq_chip twl6040_irq_chip = { - .name = "twl6040", - .irq_bus_lock = twl6040_irq_lock, - .irq_bus_sync_unlock = twl6040_irq_sync_unlock, - .irq_enable = twl6040_irq_enable, - .irq_disable = twl6040_irq_disable, -}; - -static irqreturn_t twl6040_irq_thread(int irq, void *data) -{ - struct twl6040 *twl6040 = data; - u8 intid; - int i; - - intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); - - /* apply masking and report (backwards to handle READYINT first) */ - for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) { - if (twl6040->irq_masks_cur & twl6040_irqs[i].mask) - intid &= ~twl6040_irqs[i].status; - if (intid & twl6040_irqs[i].status) - handle_nested_irq(twl6040->irq_base + i); - } - - /* ack unmasked irqs */ - twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid); - - return IRQ_HANDLED; -} - -int twl6040_irq_init(struct twl6040 *twl6040) -{ - struct device_node *node = twl6040->dev->of_node; - int i, nr_irqs, irq_base, ret; - u8 val; - - mutex_init(&twl6040->irq_mutex); - - /* mask the individual interrupt sources */ - twl6040->irq_masks_cur = TWL6040_ALLINT_MSK; - twl6040->irq_masks_cache = TWL6040_ALLINT_MSK; - twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK); - - nr_irqs = ARRAY_SIZE(twl6040_irqs); - - irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); - if (IS_ERR_VALUE(irq_base)) { - dev_err(twl6040->dev, "Fail to allocate IRQ descs\n"); - return irq_base; - } - twl6040->irq_base = irq_base; - - irq_domain_add_legacy(node, ARRAY_SIZE(twl6040_irqs), irq_base, 0, - &irq_domain_simple_ops, NULL); - - /* Register them with genirq */ - for (i = irq_base; i < irq_base + nr_irqs; i++) { - irq_set_chip_data(i, twl6040); - irq_set_chip_and_handler(i, &twl6040_irq_chip, - handle_level_irq); - irq_set_nested_thread(i, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(i, IRQF_VALID); -#else - irq_set_noprobe(i); -#endif - } - - ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread, - IRQF_ONESHOT, "twl6040", twl6040); - if (ret) { - dev_err(twl6040->dev, "failed to request IRQ %d: %d\n", - twl6040->irq, ret); - return ret; - } - - /* reset interrupts */ - val = twl6040_reg_read(twl6040, TWL6040_REG_INTID); - - /* interrupts cleared on write */ - twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE); - - return 0; -} -EXPORT_SYMBOL(twl6040_irq_init); - -void twl6040_irq_exit(struct twl6040 *twl6040) -{ - free_irq(twl6040->irq, twl6040); -} -EXPORT_SYMBOL(twl6040_irq_exit); diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040.c index 3f2a1cf02fc..583be76e36a 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040.c @@ -37,7 +37,6 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/regmap.h> -#include <linux/err.h> #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h> #include <linux/regulator/consumer.h> @@ -104,7 +103,7 @@ int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) EXPORT_SYMBOL(twl6040_clear_bits); /* twl6040 codec manual power-up sequence */ -static int twl6040_power_up(struct twl6040 *twl6040) +static int twl6040_power_up_manual(struct twl6040 *twl6040) { u8 ldoctl, ncpctl, lppllctl; int ret; @@ -158,11 +157,12 @@ ncp_err: ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + dev_err(twl6040->dev, "manual power-up failed\n"); return ret; } /* twl6040 manual power-down sequence */ -static void twl6040_power_down(struct twl6040 *twl6040) +static void twl6040_power_down_manual(struct twl6040 *twl6040) { u8 ncpctl, ldoctl, lppllctl; @@ -192,45 +192,48 @@ static void twl6040_power_down(struct twl6040 *twl6040) twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); } -static irqreturn_t twl6040_naudint_handler(int irq, void *data) +static irqreturn_t twl6040_readyint_handler(int irq, void *data) { struct twl6040 *twl6040 = data; - u8 intid, status; - intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + complete(&twl6040->ready); - if (intid & TWL6040_READYINT) - complete(&twl6040->ready); + return IRQ_HANDLED; +} - if (intid & TWL6040_THINT) { - status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); - if (status & TWL6040_TSHUTDET) { - dev_warn(twl6040->dev, - "Thermal shutdown, powering-off"); - twl6040_power(twl6040, 0); - } else { - dev_warn(twl6040->dev, - "Leaving thermal shutdown, powering-on"); - twl6040_power(twl6040, 1); - } +static irqreturn_t twl6040_thint_handler(int irq, void *data) +{ + struct twl6040 *twl6040 = data; + u8 status; + + status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); + if (status & TWL6040_TSHUTDET) { + dev_warn(twl6040->dev, "Thermal shutdown, powering-off"); + twl6040_power(twl6040, 0); + } else { + dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on"); + twl6040_power(twl6040, 1); } return IRQ_HANDLED; } -static int twl6040_power_up_completion(struct twl6040 *twl6040, - int naudint) +static int twl6040_power_up_automatic(struct twl6040 *twl6040) { int time_left; - u8 intid; + + gpio_set_value(twl6040->audpwron, 1); time_left = wait_for_completion_timeout(&twl6040->ready, msecs_to_jiffies(144)); if (!time_left) { + u8 intid; + + dev_warn(twl6040->dev, "timeout waiting for READYINT\n"); intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { - dev_err(twl6040->dev, - "timeout waiting for READYINT\n"); + dev_err(twl6040->dev, "automatic power-up failed\n"); + gpio_set_value(twl6040->audpwron, 0); return -ETIMEDOUT; } } @@ -240,8 +243,6 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040, int twl6040_power(struct twl6040 *twl6040, int on) { - int audpwron = twl6040->audpwron; - int naudint = twl6040->irq; int ret = 0; mutex_lock(&twl6040->mutex); @@ -251,23 +252,17 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (twl6040->power_count++) goto out; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 1); - /* wait for power-up completion */ - ret = twl6040_power_up_completion(twl6040, naudint); + if (gpio_is_valid(twl6040->audpwron)) { + /* use automatic power-up sequence */ + ret = twl6040_power_up_automatic(twl6040); if (ret) { - dev_err(twl6040->dev, - "automatic power-down failed\n"); twl6040->power_count = 0; goto out; } } else { /* use manual power-up sequence */ - ret = twl6040_power_up(twl6040); + ret = twl6040_power_up_manual(twl6040); if (ret) { - dev_err(twl6040->dev, - "manual power-up failed\n"); twl6040->power_count = 0; goto out; } @@ -288,15 +283,15 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (--twl6040->power_count) goto out; - if (gpio_is_valid(audpwron)) { + if (gpio_is_valid(twl6040->audpwron)) { /* use AUDPWRON line */ - gpio_set_value(audpwron, 0); + gpio_set_value(twl6040->audpwron, 0); /* power-down sequence latency */ usleep_range(500, 700); } else { /* use manual power-down sequence */ - twl6040_power_down(twl6040); + twl6040_power_down_manual(twl6040); } twl6040->sysclk = 0; twl6040->mclk = 0; @@ -503,6 +498,25 @@ static struct regmap_config twl6040_regmap_config = { .readable_reg = twl6040_readable_reg, }; +static const struct regmap_irq twl6040_irqs[] = { + { .reg_offset = 0, .mask = TWL6040_THINT, }, + { .reg_offset = 0, .mask = TWL6040_PLUGINT | TWL6040_UNPLUGINT, }, + { .reg_offset = 0, .mask = TWL6040_HOOKINT, }, + { .reg_offset = 0, .mask = TWL6040_HFINT, }, + { .reg_offset = 0, .mask = TWL6040_VIBINT, }, + { .reg_offset = 0, .mask = TWL6040_READYINT, }, +}; + +static struct regmap_irq_chip twl6040_irq_chip = { + .name = "twl6040", + .irqs = twl6040_irqs, + .num_irqs = ARRAY_SIZE(twl6040_irqs), + + .num_regs = 1, + .status_base = TWL6040_REG_INTID, + .mask_base = TWL6040_REG_INTMR, +}; + static int __devinit twl6040_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -578,18 +592,31 @@ static int __devinit twl6040_probe(struct i2c_client *client, goto gpio_err; } - /* codec interrupt */ - ret = twl6040_irq_init(twl6040); - if (ret) + ret = regmap_add_irq_chip(twl6040->regmap, twl6040->irq, + IRQF_ONESHOT, 0, &twl6040_irq_chip, + &twl6040->irq_data); + if (ret < 0) goto irq_init_err; - ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, - NULL, twl6040_naudint_handler, IRQF_ONESHOT, + twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data, + TWL6040_IRQ_READY); + twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data, + TWL6040_IRQ_TH); + + ret = request_threaded_irq(twl6040->irq_ready, NULL, + twl6040_readyint_handler, IRQF_ONESHOT, "twl6040_irq_ready", twl6040); if (ret) { - dev_err(twl6040->dev, "READY IRQ request failed: %d\n", - ret); - goto irq_err; + dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret); + goto readyirq_err; + } + + ret = request_threaded_irq(twl6040->irq_th, NULL, + twl6040_thint_handler, IRQF_ONESHOT, + "twl6040_irq_th", twl6040); + if (ret) { + dev_err(twl6040->dev, "Thermal IRQ request failed: %d\n", ret); + goto thirq_err; } /* dual-access registers controlled by I2C only */ @@ -601,7 +628,7 @@ static int __devinit twl6040_probe(struct i2c_client *client, * The ASoC codec can work without pdata, pass the platform_data only if * it has been provided. */ - irq = twl6040->irq_base + TWL6040_IRQ_PLUG; + irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG); cell = &twl6040->cells[children]; cell->name = "twl6040-codec"; twl6040_codec_rsrc[0].start = irq; @@ -615,7 +642,7 @@ static int __devinit twl6040_probe(struct i2c_client *client, children++; if (twl6040_has_vibra(pdata, node)) { - irq = twl6040->irq_base + TWL6040_IRQ_VIB; + irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB); cell = &twl6040->cells[children]; cell->name = "twl6040-vibra"; @@ -654,9 +681,11 @@ static int __devinit twl6040_probe(struct i2c_client *client, return 0; mfd_err: - free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); -irq_err: - twl6040_irq_exit(twl6040); + free_irq(twl6040->irq_th, twl6040); +thirq_err: + free_irq(twl6040->irq_ready, twl6040); +readyirq_err: + regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); irq_init_err: if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); @@ -680,8 +709,9 @@ static int __devexit twl6040_remove(struct i2c_client *client) if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); - free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); - twl6040_irq_exit(twl6040); + free_irq(twl6040->irq_ready, twl6040); + free_irq(twl6040->irq_th, twl6040); + regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); mfd_remove_devices(&client->dev); i2c_set_clientdata(client, NULL); diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c new file mode 100644 index 00000000000..af2a6703f34 --- /dev/null +++ b/drivers/mfd/viperboard.c @@ -0,0 +1,137 @@ +/* + * Nano River Technologies viperboard driver + * + * This is the core driver for the viperboard. There are cell drivers + * available for I2C, ADC and both GPIOs. SPI is not yet supported. + * The drivers do not support all features the board exposes. See user + * manual of the viperboard. + * + * (C) 2012 by Lemonage GmbH + * Author: Lars Poeschel <poeschel@lemonage.de> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mutex.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/viperboard.h> + +#include <linux/usb.h> + + +static const struct usb_device_id vprbrd_table[] = { + { USB_DEVICE(0x2058, 0x1005) }, /* Nano River Technologies */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vprbrd_table); + +static struct mfd_cell vprbrd_devs[] = { + { + .name = "viperboard-gpio", + }, + { + .name = "viperboard-i2c", + }, + { + .name = "viperboard-adc", + }, +}; + +static int vprbrd_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct vprbrd *vb; + + u16 version = 0; + int pipe, ret; + + /* allocate memory for our device state and initialize it */ + vb = kzalloc(sizeof(*vb), GFP_KERNEL); + if (vb == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + return -ENOMEM; + } + + mutex_init(&vb->lock); + + vb->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, vb); + dev_set_drvdata(&vb->pdev.dev, vb); + + /* get version information, major first, minor then */ + pipe = usb_rcvctrlpipe(vb->usb_dev, 0); + ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1, + VPRBRD_USB_TIMEOUT_MS); + if (ret == 1) + version = vb->buf[0]; + + ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1, + VPRBRD_USB_TIMEOUT_MS); + if (ret == 1) { + version <<= 8; + version = version | vb->buf[0]; + } + + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d\n", + version >> 8, version & 0xff, + vb->usb_dev->bus->busnum, vb->usb_dev->devnum); + + ret = mfd_add_devices(&interface->dev, -1, vprbrd_devs, + ARRAY_SIZE(vprbrd_devs), NULL, 0, NULL); + if (ret != 0) { + dev_err(&interface->dev, "Failed to add mfd devices to core."); + goto error; + } + + return 0; + +error: + if (vb) { + usb_put_dev(vb->usb_dev); + kfree(vb); + } + + return ret; +} + +static void vprbrd_disconnect(struct usb_interface *interface) +{ + struct vprbrd *vb = usb_get_intfdata(interface); + + mfd_remove_devices(&interface->dev); + usb_set_intfdata(interface, NULL); + usb_put_dev(vb->usb_dev); + kfree(vb); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver vprbrd_driver = { + .name = "viperboard", + .probe = vprbrd_probe, + .disconnect = vprbrd_disconnect, + .id_table = vprbrd_table, +}; + +module_usb_driver(vprbrd_driver); + +MODULE_DESCRIPTION("Nano River Technologies viperboard mfd core driver"); +MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 3141c4a173a..088872ab633 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -56,6 +56,18 @@ static const struct reg_default wm5102_reva_patch[] = { { 0x80, 0x0000 }, }; +static const struct reg_default wm5102_revb_patch[] = { + { 0x80, 0x0003 }, + { 0x081, 0xE022 }, + { 0x410, 0x6080 }, + { 0x418, 0x6080 }, + { 0x420, 0x6080 }, + { 0x428, 0xC000 }, + { 0x441, 0x8014 }, + { 0x458, 0x000b }, + { 0x80, 0x0000 }, +}; + /* We use a function so we can use ARRAY_SIZE() */ int wm5102_patch(struct arizona *arizona) { @@ -65,7 +77,9 @@ int wm5102_patch(struct arizona *arizona) wm5102_reva_patch, ARRAY_SIZE(wm5102_reva_patch)); default: - return 0; + return regmap_register_patch(arizona->regmap, + wm5102_revb_patch, + ARRAY_SIZE(wm5102_revb_patch)); } } @@ -291,6 +305,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ { 0x00000210, 0x00D4 }, /* R528 - LDO1 Control 1 */ + { 0x00000212, 0x0001 }, /* R530 - LDO1 Control 2 */ { 0x00000213, 0x0344 }, /* R531 - LDO2 Control 1 */ { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ @@ -1056,6 +1071,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_5: case ARIZONA_FLL1_CONTROL_6: case ARIZONA_FLL1_LOOP_FILTER_TEST_1: + case ARIZONA_FLL1_NCO_TEST_0: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: case ARIZONA_FLL1_SYNCHRONISER_3: @@ -1071,6 +1087,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_5: case ARIZONA_FLL2_CONTROL_6: case ARIZONA_FLL2_LOOP_FILTER_TEST_1: + case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: case ARIZONA_FLL2_SYNCHRONISER_3: @@ -1805,6 +1822,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_CLOCKING_1: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP1_STATUS_3: return true; default: return false; @@ -1813,15 +1831,23 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) static bool wm5102_volatile_register(struct device *dev, unsigned int reg) { + if (reg > 0xffff) + return true; + switch (reg) { case ARIZONA_SOFTWARE_RESET: case ARIZONA_DEVICE_REVISION: case ARIZONA_OUTPUT_STATUS_1: + case ARIZONA_RAW_OUTPUT_STATUS_1: + case ARIZONA_SLIMBUS_RX_PORT_STATUS: + case ARIZONA_SLIMBUS_TX_PORT_STATUS: case ARIZONA_SAMPLE_RATE_1_STATUS: case ARIZONA_SAMPLE_RATE_2_STATUS: case ARIZONA_SAMPLE_RATE_3_STATUS: case ARIZONA_HAPTICS_STATUS: case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS: + case ARIZONA_FLL1_NCO_TEST_0: + case ARIZONA_FLL2_NCO_TEST_0: case ARIZONA_FX_CTRL2: case ARIZONA_INTERRUPT_STATUS_1: case ARIZONA_INTERRUPT_STATUS_2: @@ -1847,6 +1873,7 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_AOD_IRQ_RAW_STATUS: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP1_STATUS_3: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_MIC_DETECT_3: return true; @@ -1855,12 +1882,14 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) } } +#define WM5102_MAX_REGISTER 0x1a8fff + const struct regmap_config wm5102_spi_regmap = { .reg_bits = 32, .pad_bits = 16, .val_bits = 16, - .max_register = ARIZONA_DSP1_STATUS_2, + .max_register = WM5102_MAX_REGISTER, .readable_reg = wm5102_readable_register, .volatile_reg = wm5102_volatile_register, @@ -1874,7 +1903,7 @@ const struct regmap_config wm5102_i2c_regmap = { .reg_bits = 32, .val_bits = 16, - .max_register = ARIZONA_DSP1_STATUS_2, + .max_register = WM5102_MAX_REGISTER, .readable_reg = wm5102_readable_register, .volatile_reg = wm5102_volatile_register, diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index bcb226ff9d2..57c488d42d3 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -535,11 +535,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) break; case 2: case 3: + default: regmap_patch = wm8994_revc_patch; patch_regs = ARRAY_SIZE(wm8994_revc_patch); break; - default: - break; } break; @@ -558,17 +557,9 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) /* Revision C did not change the relevant layer */ if (wm8994->revision > 1) wm8994->revision++; - switch (wm8994->revision) { - case 0: - case 1: - case 2: - case 3: - regmap_patch = wm1811_reva_patch; - patch_regs = ARRAY_SIZE(wm1811_reva_patch); - break; - default: - break; - } + + regmap_patch = wm1811_reva_patch; + patch_regs = ARRAY_SIZE(wm1811_reva_patch); break; default: |