diff options
author | Jiri Kosina <jkosina@suse.cz> | 2012-06-29 14:45:58 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-06-29 14:45:58 +0200 |
commit | 59f91e5dd0504dc0ebfaa0b6f3a55e6931f96266 (patch) | |
tree | b913718405d44a921905ac71044fbde410256865 /drivers/mfd | |
parent | 57bdfdd80077addf518a9b90c4a66890efc4f70e (diff) | |
parent | 89abfab133ef1f5902abafb744df72793213ac19 (diff) |
Merge branch 'master' into for-next
Conflicts:
include/linux/mmzone.h
Synced with Linus' tree so that trivial patch can be applied
on top of up-to-date code properly.
Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
Diffstat (limited to 'drivers/mfd')
48 files changed, 4397 insertions, 1142 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f4b4dad7739..e129c820df7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -106,6 +106,19 @@ config UCB1400_CORE To compile this driver as a module, choose M here: the module will be called ucb1400_core. +config MFD_LM3533 + tristate "LM3533 Lighting Power chip" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + Say yes here to enable support for National Semiconductor / TI + LM3533 Lighting Power chips. + + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the LED, + backlight or ambient-light-sensor functionality of the device. + config TPS6105X tristate "TPS61050/61052 Boost Converters" depends on I2C @@ -177,8 +190,8 @@ config MFD_TPS65910 bool "TPS65910 Power Management chip" depends on I2C=y && GPIOLIB select MFD_CORE - select GPIO_TPS65910 select REGMAP_I2C + select IRQ_DOMAIN help if you say yes here you get support for the TPS65910 series of Power Management chips. @@ -409,6 +422,19 @@ config PMIC_ADP5520 individual components like LCD backlight, LEDs, GPIOs and Kepad under the corresponding menus. +config MFD_MAX77693 + bool "Maxim Semiconductor MAX77693 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + Say yes here to support for Maxim Semiconductor MAX77693. + This is a companion Power Management IC with Flash, Haptic, Charger, + and MUIC(Micro USB Interface Controller) controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX8925 bool "Maxim Semiconductor MAX8925 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -454,9 +480,9 @@ config MFD_S5M_CORE of the device config MFD_WM8400 - tristate "Support Wolfson Microelectronics WM8400" + bool "Support Wolfson Microelectronics WM8400" select MFD_CORE - depends on I2C + depends on I2C=y select REGMAP_I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio @@ -473,6 +499,7 @@ config MFD_WM831X_I2C select MFD_CORE select MFD_WM831X select REGMAP_I2C + select IRQ_DOMAIN depends on I2C=y && GENERIC_HARDIRQS help Support for the Wolfson Microelecronics WM831x and WM832x PMICs @@ -485,6 +512,7 @@ config MFD_WM831X_SPI select MFD_CORE select MFD_WM831X select REGMAP_SPI + select IRQ_DOMAIN depends on SPI_MASTER && GENERIC_HARDIRQS help Support for the Wolfson Microelecronics WM831x and WM832x PMICs @@ -597,17 +625,32 @@ config MFD_MC13783 tristate config MFD_MC13XXX - tristate "Support Freescale MC13783 and MC13892" - depends on SPI_MASTER + tristate + depends on SPI_MASTER || I2C select MFD_CORE select MFD_MC13783 help - Support for the Freescale (Atlas) PMIC and audio CODECs - MC13783 and MC13892. - This driver provides common support for accessing the device, + Enable support for the Freescale MC13783 and MC13892 PMICs. + This driver provides common support for accessing the device, additional drivers must be enabled in order to use the functionality of the device. +config MFD_MC13XXX_SPI + tristate "Freescale MC13783 and MC13892 SPI interface" + depends on SPI_MASTER + select REGMAP_SPI + select MFD_MC13XXX + help + Select this if your MC13xxx is connected via an SPI bus. + +config MFD_MC13XXX_I2C + tristate "Freescale MC13892 I2C interface" + depends on I2C + select REGMAP_I2C + select MFD_MC13XXX + help + Select this if your MC13xxx is connected via an I2C bus. + config ABX500_CORE bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" default y if ARCH_U300 || ARCH_U8500 @@ -651,7 +694,7 @@ config EZX_PCAP config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" - depends on GENERIC_HARDIRQS && ABX500_CORE + depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU select MFD_CORE help Select this option to enable access to AB8500 power management @@ -722,6 +765,16 @@ config LPC_SCH LPC bridge function of the Intel SCH provides support for System Management Bus and General Purpose I/O. +config LPC_ICH + tristate "Intel ICH LPC" + depends on PCI + select MFD_CORE + help + The LPC bridge function of the Intel ICH provides support for + many functional units. This driver provides needed support for + other drivers to control these functions, currently GPIO and + watchdog. + config MFD_RDC321X tristate "Support for RDC-R321x southbridge" select MFD_CORE @@ -854,6 +907,11 @@ config MFD_RC5T583 Additional drivers must be enabled in order to use the different functionality of the device. +config MFD_STA2X11 + bool "STA2X11 multi function device support" + depends on STA2X11 + select MFD_CORE + config MFD_ANATOP bool "Support for Freescale i.MX on-chip ANATOP controller" depends on SOC_IMX6Q diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 43672b87805..75f6ed68a4b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ 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_STA2X11) += sta2x11-mfd.o obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o @@ -54,6 +55,8 @@ obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o +obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o +obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o obj-$(CONFIG_MFD_CORE) += mfd-core.o @@ -75,6 +78,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o +obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o @@ -87,15 +91,15 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o -obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o -# ab8500-i2c need to come after db8500-prcmu (which provides the channel) -obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o +# ab8500-core need to come after db8500-prcmu (which provides the channel) +obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o +obj-$(CONFIG_LPC_ICH) += lpc_ich.o obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 1f08704f7ae..dac0e299860 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -18,7 +18,10 @@ #include <linux/mfd/core.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> +#include <linux/mfd/dbx500-prcmu.h> #include <linux/regulator/ab8500.h> +#include <linux/of.h> +#include <linux/of_device.h> /* * Interrupt register offsets @@ -91,12 +94,24 @@ #define AB8500_IT_MASK23_REG 0x56 #define AB8500_IT_MASK24_REG 0x57 +/* + * latch hierarchy registers + */ +#define AB8500_IT_LATCHHIER1_REG 0x60 +#define AB8500_IT_LATCHHIER2_REG 0x61 +#define AB8500_IT_LATCHHIER3_REG 0x62 + +#define AB8500_IT_LATCHHIER_NUM 3 + #define AB8500_REV_REG 0x80 #define AB8500_IC_NAME_REG 0x82 #define AB8500_SWITCH_OFF_STATUS 0x00 #define AB8500_TURN_ON_STATUS 0x00 +static bool no_bm; /* No battery management */ +module_param(no_bm, bool, S_IRUGO); + #define AB9540_MODEM_CTRL2_REG 0x23 #define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2) @@ -125,6 +140,41 @@ static const char ab8500_version_str[][7] = { [AB8500_VERSION_AB8540] = "AB8540", }; +static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) +{ + int ret; + + ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1); + if (ret < 0) + dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); + return ret; +} + +static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, + u8 data) +{ + int ret; + + ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data, + &mask, 1); + if (ret < 0) + dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); + return ret; +} + +static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr) +{ + int ret; + u8 data; + + ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1); + if (ret < 0) { + dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); + return ret; + } + return (int)data; +} + static int ab8500_get_chip_id(struct device *dev) { struct ab8500 *ab8500; @@ -161,9 +211,13 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank, static int ab8500_set_register(struct device *dev, u8 bank, u8 reg, u8 value) { + int ret; struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return set_register_interruptible(ab8500, bank, reg, value); + atomic_inc(&ab8500->transfer_ongoing); + ret = set_register_interruptible(ab8500, bank, reg, value); + atomic_dec(&ab8500->transfer_ongoing); + return ret; } static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, @@ -192,9 +246,13 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, static int ab8500_get_register(struct device *dev, u8 bank, u8 reg, u8 *value) { + int ret; struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab8500, bank, reg, value); + atomic_inc(&ab8500->transfer_ongoing); + ret = get_register_interruptible(ab8500, bank, reg, value); + atomic_dec(&ab8500->transfer_ongoing); + return ret; } static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank, @@ -241,11 +299,14 @@ out: static int ab8500_mask_and_set_register(struct device *dev, u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { + int ret; struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab8500, bank, reg, - bitmask, bitvalues); - + atomic_inc(&ab8500->transfer_ongoing); + ret= mask_and_set_register_interruptible(ab8500, bank, reg, + bitmask, bitvalues); + atomic_dec(&ab8500->transfer_ongoing); + return ret; } static struct abx500_ops ab8500_ops = { @@ -264,6 +325,7 @@ static void ab8500_irq_lock(struct irq_data *data) struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); mutex_lock(&ab8500->irq_lock); + atomic_inc(&ab8500->transfer_ongoing); } static void ab8500_irq_sync_unlock(struct irq_data *data) @@ -292,7 +354,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i]; set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new); } - + atomic_dec(&ab8500->transfer_ongoing); mutex_unlock(&ab8500->irq_lock); } @@ -325,6 +387,90 @@ static struct irq_chip ab8500_irq_chip = { .irq_unmask = ab8500_irq_unmask, }; +static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, + int latch_offset, u8 latch_val) +{ + int int_bit = __ffs(latch_val); + int line, i; + + do { + int_bit = __ffs(latch_val); + + for (i = 0; i < ab8500->mask_size; i++) + if (ab8500->irq_reg_offset[i] == latch_offset) + break; + + if (i >= ab8500->mask_size) { + dev_err(ab8500->dev, "Register offset 0x%2x not declared\n", + latch_offset); + return -ENXIO; + } + + line = (i << 3) + int_bit; + latch_val &= ~(1 << int_bit); + + handle_nested_irq(ab8500->irq_base + line); + } while (latch_val); + + return 0; +} + +static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500, + int hier_offset, u8 hier_val) +{ + int latch_bit, status; + u8 latch_offset, latch_val; + + do { + latch_bit = __ffs(hier_val); + latch_offset = (hier_offset << 3) + latch_bit; + + /* Fix inconsistent ITFromLatch25 bit mapping... */ + if (unlikely(latch_offset == 17)) + latch_offset = 24; + + status = get_register_interruptible(ab8500, + AB8500_INTERRUPT, + AB8500_IT_LATCH1_REG + latch_offset, + &latch_val); + if (status < 0 || latch_val == 0) + goto discard; + + status = ab8500_handle_hierarchical_line(ab8500, + latch_offset, latch_val); + if (status < 0) + return status; +discard: + hier_val &= ~(1 << latch_bit); + } while (hier_val); + + return 0; +} + +static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev) +{ + struct ab8500 *ab8500 = dev; + u8 i; + + dev_vdbg(ab8500->dev, "interrupt\n"); + + /* Hierarchical interrupt version */ + for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) { + int status; + u8 hier_val; + + status = get_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_LATCHHIER1_REG + i, &hier_val); + if (status < 0 || hier_val == 0) + continue; + + status = ab8500_handle_hierarchical_latch(ab8500, i, hier_val); + if (status < 0) + break; + } + return IRQ_HANDLED; +} + static irqreturn_t ab8500_irq(int irq, void *dev) { struct ab8500 *ab8500 = dev; @@ -332,6 +478,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev) dev_vdbg(ab8500->dev, "interrupt\n"); + atomic_inc(&ab8500->transfer_ongoing); + for (i = 0; i < ab8500->mask_size; i++) { int regoffset = ab8500->irq_reg_offset[i]; int status; @@ -355,9 +503,10 @@ static irqreturn_t ab8500_irq(int irq, void *dev) handle_nested_irq(ab8500->irq_base + line); value &= ~(1 << bit); + } while (value); } - + atomic_dec(&ab8500->transfer_ongoing); return IRQ_HANDLED; } @@ -411,6 +560,14 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) } } +int ab8500_suspend(struct ab8500 *ab8500) +{ + if (atomic_read(&ab8500->transfer_ongoing)) + return -EINVAL; + else + return 0; +} + /* AB8500 GPIO Resources */ static struct resource __devinitdata ab8500_gpio_resources[] = { { @@ -744,6 +901,39 @@ static struct resource __devinitdata ab8500_usb_resources[] = { }, }; +static struct resource __devinitdata ab8505_iddet_resources[] = { + { + .name = "KeyDeglitch", + .start = AB8505_INT_KEYDEGLITCH, + .end = AB8505_INT_KEYDEGLITCH, + .flags = IORESOURCE_IRQ, + }, + { + .name = "KP", + .start = AB8505_INT_KP, + .end = AB8505_INT_KP, + .flags = IORESOURCE_IRQ, + }, + { + .name = "IKP", + .start = AB8505_INT_IKP, + .end = AB8505_INT_IKP, + .flags = IORESOURCE_IRQ, + }, + { + .name = "IKR", + .start = AB8505_INT_IKR, + .end = AB8505_INT_IKR, + .flags = IORESOURCE_IRQ, + }, + { + .name = "KeyStuck", + .start = AB8505_INT_KEYSTUCK, + .end = AB8505_INT_KEYSTUCK, + .flags = IORESOURCE_IRQ, + }, +}; + static struct resource __devinitdata ab8500_temp_resources[] = { { .name = "AB8500_TEMP_WARM", @@ -778,35 +968,11 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { .resources = ab8500_rtc_resources, }, { - .name = "ab8500-charger", - .num_resources = ARRAY_SIZE(ab8500_charger_resources), - .resources = ab8500_charger_resources, - }, - { - .name = "ab8500-btemp", - .num_resources = ARRAY_SIZE(ab8500_btemp_resources), - .resources = ab8500_btemp_resources, - }, - { - .name = "ab8500-fg", - .num_resources = ARRAY_SIZE(ab8500_fg_resources), - .resources = ab8500_fg_resources, - }, - { - .name = "ab8500-chargalg", - .num_resources = ARRAY_SIZE(ab8500_chargalg_resources), - .resources = ab8500_chargalg_resources, - }, - { .name = "ab8500-acc-det", .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources), .resources = ab8500_av_acc_detect_resources, }, { - .name = "ab8500-codec", - }, - - { .name = "ab8500-poweron-key", .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), .resources = ab8500_poweronkey_db_resources, @@ -834,6 +1000,29 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { }, }; +static struct mfd_cell __devinitdata ab8500_bm_devs[] = { + { + .name = "ab8500-charger", + .num_resources = ARRAY_SIZE(ab8500_charger_resources), + .resources = ab8500_charger_resources, + }, + { + .name = "ab8500-btemp", + .num_resources = ARRAY_SIZE(ab8500_btemp_resources), + .resources = ab8500_btemp_resources, + }, + { + .name = "ab8500-fg", + .num_resources = ARRAY_SIZE(ab8500_fg_resources), + .resources = ab8500_fg_resources, + }, + { + .name = "ab8500-chargalg", + .num_resources = ARRAY_SIZE(ab8500_chargalg_resources), + .resources = ab8500_chargalg_resources, + }, +}; + static struct mfd_cell __devinitdata ab8500_devs[] = { { .name = "ab8500-gpio", @@ -845,6 +1034,9 @@ static struct mfd_cell __devinitdata ab8500_devs[] = { .num_resources = ARRAY_SIZE(ab8500_usb_resources), .resources = ab8500_usb_resources, }, + { + .name = "ab8500-codec", + }, }; static struct mfd_cell __devinitdata ab9540_devs[] = { @@ -858,6 +1050,18 @@ static struct mfd_cell __devinitdata ab9540_devs[] = { .num_resources = ARRAY_SIZE(ab8500_usb_resources), .resources = ab8500_usb_resources, }, + { + .name = "ab9540-codec", + }, +}; + +/* Device list common to ab9540 and ab8505 */ +static struct mfd_cell __devinitdata ab9540_ab8505_devs[] = { + { + .name = "ab-iddet", + .num_resources = ARRAY_SIZE(ab8505_iddet_resources), + .resources = ab8505_iddet_resources, + }, }; static ssize_t show_chip_id(struct device *dev, @@ -1003,18 +1207,66 @@ static struct attribute_group ab9540_attr_group = { .attrs = ab9540_sysfs_entries, }; -int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) +static const struct of_device_id ab8500_match[] = { + { + .compatible = "stericsson,ab8500", + .data = (void *)AB8500_VERSION_AB8500, + }, + {}, +}; + +static int __devinit ab8500_probe(struct platform_device *pdev) { - struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); + struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev); + const struct platform_device_id *platid = platform_get_device_id(pdev); + enum ab8500_version version = AB8500_VERSION_UNDEFINED; + struct device_node *np = pdev->dev.of_node; + struct ab8500 *ab8500; + struct resource *resource; int ret; int i; u8 value; + ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); + if (!ab8500) + return -ENOMEM; + if (plat) ab8500->irq_base = plat->irq_base; + else if (np) + ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base); + + if (!ab8500->irq_base) { + dev_info(&pdev->dev, "couldn't find irq-base\n"); + ret = -EINVAL; + goto out_free_ab8500; + } + + ab8500->dev = &pdev->dev; + + resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!resource) { + ret = -ENODEV; + goto out_free_ab8500; + } + + ab8500->irq = resource->start; + + ab8500->read = ab8500_i2c_read; + ab8500->write = ab8500_i2c_write; + ab8500->write_masked = ab8500_i2c_write_masked; mutex_init(&ab8500->lock); mutex_init(&ab8500->irq_lock); + atomic_set(&ab8500->transfer_ongoing, 0); + + platform_set_drvdata(pdev, ab8500); + + if (platid) + version = platid->driver_data; + else if (np) + version = (unsigned int) + of_match_device(ab8500_match, &pdev->dev)->data; if (version != AB8500_VERSION_UNDEFINED) ab8500->version = version; @@ -1022,7 +1274,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_IC_NAME_REG, &value); if (ret < 0) - return ret; + goto out_free_ab8500; ab8500->version = value; } @@ -1030,7 +1282,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) ret = get_register_interruptible(ab8500, AB8500_MISC, AB8500_REV_REG, &value); if (ret < 0) - return ret; + goto out_free_ab8500; ab8500->chip_id = value; @@ -1105,30 +1357,57 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version) if (ret) goto out_freeoldmask; - ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, - IRQF_ONESHOT | IRQF_NO_SUSPEND, - "ab8500", ab8500); + /* 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); + else + ret = request_threaded_irq(ab8500->irq, NULL, + ab8500_irq, + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); if (ret) goto out_removeirq; } - ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, - ARRAY_SIZE(abx500_common_devs), NULL, - ab8500->irq_base); + if (!np) { + ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, + ARRAY_SIZE(abx500_common_devs), NULL, + ab8500->irq_base); - if (ret) - goto out_freeirq; + if (ret) + goto out_freeirq; + + if (is_ab9540(ab8500)) + ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, + ARRAY_SIZE(ab9540_devs), NULL, + ab8500->irq_base); + else + ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, + ARRAY_SIZE(ab8500_devs), NULL, + ab8500->irq_base); + if (ret) + goto out_freeirq; - if (is_ab9540(ab8500)) - ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, - ARRAY_SIZE(ab9540_devs), NULL, - ab8500->irq_base); - else - ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, - ARRAY_SIZE(ab9540_devs), NULL, - ab8500->irq_base); - if (ret) - goto out_freeirq; + 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); + if (ret) + goto out_freeirq; + } + + if (!no_bm) { + /* Add battery management devices */ + ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, + ARRAY_SIZE(ab8500_bm_devs), NULL, + ab8500->irq_base); + if (ret) + dev_err(ab8500->dev, "error adding bm devices\n"); + } if (is_ab9540(ab8500)) ret = sysfs_create_group(&ab8500->dev->kobj, @@ -1151,12 +1430,16 @@ out_freeoldmask: kfree(ab8500->oldmask); out_freemask: kfree(ab8500->mask); +out_free_ab8500: + kfree(ab8500); return ret; } -int __devexit ab8500_exit(struct ab8500 *ab8500) +static int __devexit ab8500_remove(struct platform_device *pdev) { + struct ab8500 *ab8500 = platform_get_drvdata(pdev); + if (is_ab9540(ab8500)) sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); else @@ -1168,10 +1451,42 @@ int __devexit ab8500_exit(struct ab8500 *ab8500) } kfree(ab8500->oldmask); kfree(ab8500->mask); + kfree(ab8500); return 0; } +static const struct platform_device_id ab8500_id[] = { + { "ab8500-core", AB8500_VERSION_AB8500 }, + { "ab8505-i2c", AB8500_VERSION_AB8505 }, + { "ab9540-i2c", AB8500_VERSION_AB9540 }, + { "ab8540-i2c", AB8500_VERSION_AB8540 }, + { } +}; + +static struct platform_driver ab8500_core_driver = { + .driver = { + .name = "ab8500-core", + .owner = THIS_MODULE, + .of_match_table = ab8500_match, + }, + .probe = ab8500_probe, + .remove = __devexit_p(ab8500_remove), + .id_table = ab8500_id, +}; + +static int __init ab8500_core_init(void) +{ + return platform_driver_register(&ab8500_core_driver); +} + +static void __exit ab8500_core_exit(void) +{ + platform_driver_unregister(&ab8500_core_driver); +} +arch_initcall(ab8500_core_init); +module_exit(ab8500_core_exit); + MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent"); MODULE_DESCRIPTION("AB8500 MFD core"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 9a0211aa889..50c4c89ab22 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -608,10 +608,16 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf) return 0; } +static const struct of_device_id ab8500_debug_match[] = { + { .compatible = "stericsson,ab8500-debug", }, + {} +}; + static struct platform_driver ab8500_debug_driver = { .driver = { .name = "ab8500-debug", .owner = THIS_MODULE, + .of_match_table = ab8500_debug_match, }, .probe = ab8500_debug_probe, .remove = __devexit_p(ab8500_debug_remove) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index c39fc716e1d..b86fd8e1ec3 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -584,7 +584,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); if (gpadc->irq < 0) { - dev_err(gpadc->dev, "failed to get platform irq-%d\n", + dev_err(&pdev->dev, "failed to get platform irq-%d\n", gpadc->irq); ret = gpadc->irq; goto fail; @@ -648,12 +648,18 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ab8500_gpadc_match[] = { + { .compatible = "stericsson,ab8500-gpadc", }, + {} +}; + static struct platform_driver ab8500_gpadc_driver = { .probe = ab8500_gpadc_probe, .remove = __devexit_p(ab8500_gpadc_remove), .driver = { .name = "ab8500-gpadc", .owner = THIS_MODULE, + .of_match_table = ab8500_gpadc_match, }, }; diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c deleted file mode 100644 index b83045f102b..00000000000 --- a/drivers/mfd/ab8500-i2c.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson. - * License Terms: GNU General Public License v2 - * This file was based on drivers/mfd/ab8500-spi.c - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/dbx500-prcmu.h> - -static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data) -{ - int ret; - - ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1); - if (ret < 0) - dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); - return ret; -} - -static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, - u8 data) -{ - int ret; - - ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data, - &mask, 1); - if (ret < 0) - dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); - return ret; -} - -static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr) -{ - int ret; - u8 data; - - ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1); - if (ret < 0) { - dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); - return ret; - } - return (int)data; -} - -static int __devinit ab8500_i2c_probe(struct platform_device *plf) -{ - const struct platform_device_id *platid = platform_get_device_id(plf); - struct ab8500 *ab8500; - struct resource *resource; - int ret; - - ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); - if (!ab8500) - return -ENOMEM; - - ab8500->dev = &plf->dev; - - resource = platform_get_resource(plf, IORESOURCE_IRQ, 0); - if (!resource) { - kfree(ab8500); - return -ENODEV; - } - - ab8500->irq = resource->start; - - ab8500->read = ab8500_i2c_read; - ab8500->write = ab8500_i2c_write; - ab8500->write_masked = ab8500_i2c_write_masked; - - platform_set_drvdata(plf, ab8500); - - ret = ab8500_init(ab8500, platid->driver_data); - if (ret) - kfree(ab8500); - - - return ret; -} - -static int __devexit ab8500_i2c_remove(struct platform_device *plf) -{ - struct ab8500 *ab8500 = platform_get_drvdata(plf); - - ab8500_exit(ab8500); - kfree(ab8500); - - return 0; -} - -static const struct platform_device_id ab8500_id[] = { - { "ab8500-i2c", AB8500_VERSION_AB8500 }, - { "ab8505-i2c", AB8500_VERSION_AB8505 }, - { "ab9540-i2c", AB8500_VERSION_AB9540 }, - { "ab8540-i2c", AB8500_VERSION_AB8540 }, - { } -}; - -static struct platform_driver ab8500_i2c_driver = { - .driver = { - .name = "ab8500-i2c", - .owner = THIS_MODULE, - }, - .probe = ab8500_i2c_probe, - .remove = __devexit_p(ab8500_i2c_remove), - .id_table = ab8500_id, -}; - -static int __init ab8500_i2c_init(void) -{ - return platform_driver_register(&ab8500_i2c_driver); -} - -static void __exit ab8500_i2c_exit(void) -{ - platform_driver_unregister(&ab8500_i2c_driver); -} -arch_initcall(ab8500_i2c_init); -module_exit(ab8500_i2c_exit); - -MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com"); -MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index c28d4eb1eff..5a3e51ccf25 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -61,10 +61,16 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ab8500_sysctrl_match[] = { + { .compatible = "stericsson,ab8500-sysctrl", }, + {} +}; + static struct platform_driver ab8500_sysctrl_driver = { .driver = { .name = "ab8500-sysctrl", .owner = THIS_MODULE, + .of_match_table = ab8500_sysctrl_match, }, .probe = ab8500_sysctrl_probe, .remove = __devexit_p(ab8500_sysctrl_remove), diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c index 2af42480635..6da06341f6c 100644 --- a/drivers/mfd/anatop-mfd.c +++ b/drivers/mfd/anatop-mfd.c @@ -41,39 +41,26 @@ #include <linux/of_address.h> #include <linux/mfd/anatop.h> -u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift, - int bit_width) +u32 anatop_read_reg(struct anatop *adata, u32 addr) { - u32 val, mask; - - if (bit_width == 32) - mask = ~0; - else - mask = (1 << bit_width) - 1; - - val = readl(adata->ioreg + addr); - val = (val >> bit_shift) & mask; - - return val; + return readl(adata->ioreg + addr); } -EXPORT_SYMBOL_GPL(anatop_get_bits); +EXPORT_SYMBOL_GPL(anatop_read_reg); -void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift, - int bit_width, u32 data) +void anatop_write_reg(struct anatop *adata, u32 addr, u32 data, u32 mask) { - u32 val, mask; + u32 val; - if (bit_width == 32) - mask = ~0; - else - mask = (1 << bit_width) - 1; + data &= mask; spin_lock(&adata->reglock); - val = readl(adata->ioreg + addr) & ~(mask << bit_shift); - writel((data << bit_shift) | val, adata->ioreg + addr); + val = readl(adata->ioreg + addr); + val &= ~mask; + val |= data; + writel(val, adata->ioreg + addr); spin_unlock(&adata->reglock); } -EXPORT_SYMBOL_GPL(anatop_set_bits); +EXPORT_SYMBOL_GPL(anatop_write_reg); static const struct of_device_id of_anatop_match[] = { { .compatible = "fsl,imx6q-anatop", }, diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 1582c3d9525..383421bf576 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -353,12 +353,28 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) return 0; } +static int asic3_gpio_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct asic3 *asic = irq_data_get_irq_chip_data(data); + u32 bank, index; + u16 bit; + + bank = asic3_irq_to_bank(asic, data->irq); + index = asic3_irq_to_index(asic, data->irq); + bit = 1<<index; + + asic3_set_register(asic, bank + ASIC3_GPIO_SLEEP_MASK, bit, !on); + + return 0; +} + static struct irq_chip asic3_gpio_irq_chip = { .name = "ASIC3-GPIO", .irq_ack = asic3_mask_gpio_irq, .irq_mask = asic3_mask_gpio_irq, .irq_unmask = asic3_unmask_gpio_irq, .irq_set_type = asic3_gpio_irq_type, + .irq_set_wake = asic3_gpio_irq_set_wake, }; static struct irq_chip asic3_irq_chip = { @@ -529,7 +545,7 @@ static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct asic3 *asic = container_of(chip, struct asic3, gpio); - return (offset < ASIC3_NUM_GPIOS) ? asic->irq_base + offset : -ENXIO; + return asic->irq_base + offset; } static __init int asic3_gpio_probe(struct platform_device *pdev, @@ -894,10 +910,13 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, asic3_mmc_resources[0].start >>= asic->bus_shift; asic3_mmc_resources[0].end >>= asic->bus_shift; - ret = mfd_add_devices(&pdev->dev, pdev->id, + if (pdata->clock_rate) { + ds1wm_pdata.clock_rate = pdata->clock_rate; + ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_ds1wm, 1, mem, asic->irq_base); - if (ret < 0) - goto out; + if (ret < 0) + goto out; + } if (mem_sdio && (irq >= 0)) { ret = mfd_add_devices(&pdev->dev, pdev->id, @@ -1000,6 +1019,9 @@ static int __init asic3_probe(struct platform_device *pdev) asic3_mfd_probe(pdev, pdata, mem); + asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), + (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 1); + dev_info(asic->dev, "ASIC3 Core driver\n"); return 0; @@ -1021,6 +1043,9 @@ static int __devexit asic3_remove(struct platform_device *pdev) int ret; struct asic3 *asic = platform_get_drvdata(pdev); + asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), + (ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 0); + asic3_mfd_remove(pdev); ret = asic3_gpio_remove(pdev); diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 315fef5d466..3419e726de4 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -186,18 +186,7 @@ static struct pci_driver cs5535_mfd_driver = { .remove = __devexit_p(cs5535_mfd_remove), }; -static int __init cs5535_mfd_init(void) -{ - return pci_register_driver(&cs5535_mfd_driver); -} - -static void __exit cs5535_mfd_exit(void) -{ - pci_unregister_driver(&cs5535_mfd_driver); -} - -module_init(cs5535_mfd_init); -module_exit(cs5535_mfd_exit); +module_pci_driver(cs5535_mfd_driver); MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device"); diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 7776aff4626..1f1313c9057 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -318,6 +318,135 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg) } } +/* + * TBAT look-up table is computed from the R90 reg (8 bit register) + * reading as below. The battery temperature is in milliCentigrade + * TBAT = (1/(t1+1/298) - 273) * 1000 mC + * where t1 = (1/B)* ln(( ADCval * 2.5)/(R25*ITBAT*255)) + * Default values are R25 = 10e3, B = 3380, ITBAT = 50e-6 + * Example: + * R25=10E3, B=3380, ITBAT=50e-6, ADCVAL=62d calculates + * TBAT = 20015 mili degrees Centrigrade + * +*/ +static const int32_t tbat_lookup[255] = { + 183258, 144221, 124334, 111336, 101826, 94397, 88343, 83257, + 78889, 75071, 71688, 68656, 65914, 63414, 61120, 59001, + 570366, 55204, 53490, 51881, 50364, 48931, 47574, 46285, + 45059, 43889, 42772, 41703, 40678, 39694, 38748, 37838, + 36961, 36115, 35297, 34507, 33743, 33002, 32284, 31588, + 30911, 30254, 29615, 28994, 28389, 27799, 27225, 26664, + 26117, 25584, 25062, 24553, 24054, 23567, 23091, 22624, + 22167, 21719, 21281, 20851, 20429, 20015, 19610, 19211, + 18820, 18436, 18058, 17688, 17323, 16965, 16612, 16266, + 15925, 15589, 15259, 14933, 14613, 14298, 13987, 13681, + 13379, 13082, 12788, 12499, 12214, 11933, 11655, 11382, + 11112, 10845, 10582, 10322, 10066, 9812, 9562, 9315, + 9071, 8830, 8591, 8356, 8123, 7893, 7665, 7440, + 7218, 6998, 6780, 6565, 6352, 6141, 5933, 5726, + 5522, 5320, 5120, 4922, 4726, 4532, 4340, 4149, + 3961, 3774, 3589, 3406, 3225, 3045, 2867, 2690, + 2516, 2342, 2170, 2000, 1831, 1664, 1498, 1334, + 1171, 1009, 849, 690, 532, 376, 221, 67, + -84, -236, -386, -535, -683, -830, -975, -1119, + -1263, -1405, -1546, -1686, -1825, -1964, -2101, -2237, + -2372, -2506, -2639, -2771, -2902, -3033, -3162, -3291, + -3418, -3545, -3671, -3796, -3920, -4044, -4166, -4288, + -4409, -4529, -4649, -4767, -4885, -5002, -5119, -5235, + -5349, -5464, -5577, -5690, -5802, -5913, -6024, -6134, + -6244, -6352, -6461, -6568, -6675, -6781, -6887, -6992, + -7096, -7200, -7303, -7406, -7508, -7609, -7710, -7810, + -7910, -8009, -8108, -8206, -8304, -8401, -8497, -8593, + -8689, -8784, -8878, -8972, -9066, -9159, -9251, -9343, + -9435, -9526, -9617, -9707, -9796, -9886, -9975, -10063, + -10151, -10238, -10325, -10412, -10839, -10923, -11007, -11090, + -11173, -11256, -11338, -11420, -11501, -11583, -11663, -11744, + -11823, -11903, -11982 +}; + +static const u8 chan_mux[DA9052_ADC_VBBAT + 1] = { + [DA9052_ADC_VDDOUT] = DA9052_ADC_MAN_MUXSEL_VDDOUT, + [DA9052_ADC_ICH] = DA9052_ADC_MAN_MUXSEL_ICH, + [DA9052_ADC_TBAT] = DA9052_ADC_MAN_MUXSEL_TBAT, + [DA9052_ADC_VBAT] = DA9052_ADC_MAN_MUXSEL_VBAT, + [DA9052_ADC_IN4] = DA9052_ADC_MAN_MUXSEL_AD4, + [DA9052_ADC_IN5] = DA9052_ADC_MAN_MUXSEL_AD5, + [DA9052_ADC_IN6] = DA9052_ADC_MAN_MUXSEL_AD6, + [DA9052_ADC_VBBAT] = DA9052_ADC_MAN_MUXSEL_VBBAT +}; + +int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel) +{ + int ret; + unsigned short calc_data; + unsigned short data; + unsigned char mux_sel; + + if (channel > DA9052_ADC_VBBAT) + return -EINVAL; + + mutex_lock(&da9052->auxadc_lock); + + /* Channel gets activated on enabling the Conversion bit */ + mux_sel = chan_mux[channel] | DA9052_ADC_MAN_MAN_CONV; + + ret = da9052_reg_write(da9052, DA9052_ADC_MAN_REG, mux_sel); + if (ret < 0) + goto err; + + /* Wait for an interrupt */ + if (!wait_for_completion_timeout(&da9052->done, + msecs_to_jiffies(500))) { + dev_err(da9052->dev, + "timeout waiting for ADC conversion interrupt\n"); + ret = -ETIMEDOUT; + goto err; + } + + ret = da9052_reg_read(da9052, DA9052_ADC_RES_H_REG); + if (ret < 0) + goto err; + + calc_data = (unsigned short)ret; + data = calc_data << 2; + + ret = da9052_reg_read(da9052, DA9052_ADC_RES_L_REG); + if (ret < 0) + goto err; + + calc_data = (unsigned short)(ret & DA9052_ADC_RES_LSB); + data |= calc_data; + + ret = data; + +err: + mutex_unlock(&da9052->auxadc_lock); + return ret; +} +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; + + tbat = da9052_reg_read(da9052, DA9052_TBAT_RES_REG); + if (tbat <= 0) + return tbat; + + /* ARRAY_SIZE check is not needed since TBAT is a 8-bit register */ + return tbat_lookup[tbat - 1]; +} +EXPORT_SYMBOL_GPL(da9052_adc_read_temp); + static struct resource da9052_rtc_resource = { .name = "ALM", .start = DA9052_IRQ_ALARM, @@ -646,6 +775,9 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) struct irq_desc *desc; int ret; + mutex_init(&da9052->auxadc_lock); + init_completion(&da9052->done); + if (pdata && pdata->init != NULL) pdata->init(da9052); @@ -665,6 +797,12 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) 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 = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0); if (ret) @@ -673,6 +811,7 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id) return 0; err: + free_irq(DA9052_IRQ_ADC_EOM, da9052); mfd_remove_devices(da9052->dev); regmap_err: return ret; @@ -680,6 +819,7 @@ regmap_err: 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); } diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 36b88e39549..82c9d645028 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -22,6 +22,11 @@ #include <linux/mfd/da9052/da9052.h> #include <linux/mfd/da9052/reg.h> +#ifdef CONFIG_OF +#include <linux/of.h> +#include <linux/of_device.h> +#endif + static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) { int reg_val, ret; @@ -41,13 +46,31 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) return 0; } +static struct i2c_device_id da9052_i2c_id[] = { + {"da9052", DA9052}, + {"da9053-aa", DA9053_AA}, + {"da9053-ba", DA9053_BA}, + {"da9053-bb", DA9053_BB}, + {} +}; + +#ifdef CONFIG_OF +static const struct of_device_id dialog_dt_ids[] = { + { .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] }, + { .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] }, + { .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] }, + { .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] }, + { /* sentinel */ } +}; +#endif + static int __devinit da9052_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct da9052 *da9052; int ret; - da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL); + da9052 = devm_kzalloc(&client->dev, sizeof(struct da9052), GFP_KERNEL); if (!da9052) return -ENOMEM; @@ -55,8 +78,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_info(&client->dev, "Error in %s:i2c_check_functionality\n", __func__); - ret = -ENODEV; - goto err; + return -ENODEV; } da9052->dev = &client->dev; @@ -64,29 +86,39 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, da9052); - da9052->regmap = regmap_init_i2c(client, &da9052_regmap_config); + da9052->regmap = devm_regmap_init_i2c(client, &da9052_regmap_config); if (IS_ERR(da9052->regmap)) { ret = PTR_ERR(da9052->regmap); dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } ret = da9052_i2c_enable_multiwrite(da9052); if (ret < 0) - goto err_regmap; + return ret; + +#ifdef CONFIG_OF + if (!id) { + struct device_node *np = client->dev.of_node; + const struct of_device_id *deviceid; + + deviceid = of_match_node(dialog_dt_ids, np); + id = (const struct i2c_device_id *)deviceid->data; + } +#endif + + if (!id) { + ret = -ENODEV; + dev_err(&client->dev, "id is null.\n"); + return ret; + } ret = da9052_device_init(da9052, id->driver_data); if (ret != 0) - goto err_regmap; + return ret; return 0; - -err_regmap: - regmap_exit(da9052->regmap); -err: - kfree(da9052); - return ret; } static int __devexit da9052_i2c_remove(struct i2c_client *client) @@ -94,20 +126,9 @@ static int __devexit da9052_i2c_remove(struct i2c_client *client) struct da9052 *da9052 = i2c_get_clientdata(client); da9052_device_exit(da9052); - regmap_exit(da9052->regmap); - kfree(da9052); - return 0; } -static struct i2c_device_id da9052_i2c_id[] = { - {"da9052", DA9052}, - {"da9053-aa", DA9053_AA}, - {"da9053-ba", DA9053_BA}, - {"da9053-bb", DA9053_BB}, - {} -}; - static struct i2c_driver da9052_i2c_driver = { .probe = da9052_i2c_probe, .remove = __devexit_p(da9052_i2c_remove), @@ -115,6 +136,9 @@ static struct i2c_driver da9052_i2c_driver = { .driver = { .name = "da9052", .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = dialog_dt_ids, +#endif }, }; diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 6faf149e8d9..dbeadc5a643 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -25,8 +25,9 @@ static int __devinit da9052_spi_probe(struct spi_device *spi) { int ret; const struct spi_device_id *id = spi_get_device_id(spi); - struct da9052 *da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL); + struct da9052 *da9052; + da9052 = devm_kzalloc(&spi->dev, sizeof(struct da9052), GFP_KERNEL); if (!da9052) return -ENOMEM; @@ -42,25 +43,19 @@ static int __devinit da9052_spi_probe(struct spi_device *spi) da9052_regmap_config.read_flag_mask = 1; da9052_regmap_config.write_flag_mask = 0; - da9052->regmap = regmap_init_spi(spi, &da9052_regmap_config); + da9052->regmap = devm_regmap_init_spi(spi, &da9052_regmap_config); if (IS_ERR(da9052->regmap)) { ret = PTR_ERR(da9052->regmap); dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } ret = da9052_device_init(da9052, id->driver_data); if (ret != 0) - goto err_regmap; + return ret; return 0; - -err_regmap: - regmap_exit(da9052->regmap); -err: - kfree(da9052); - return ret; } static int __devexit da9052_spi_remove(struct spi_device *spi) @@ -68,9 +63,6 @@ static int __devexit da9052_spi_remove(struct spi_device *spi) struct da9052 *da9052 = dev_get_drvdata(&spi->dev); da9052_device_exit(da9052); - regmap_exit(da9052->regmap); - kfree(da9052); - return 0; } @@ -88,7 +80,6 @@ static struct spi_driver da9052_spi_driver = { .id_table = da9052_spi_id, .driver = { .name = "da9052", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, }; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 5be32489714..671c8bc14bb 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2720,6 +2720,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = { REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"), REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"), REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"), + REGULATOR_SUPPLY("v-i2c", "nmk-i2c.4"), /* "v-mmc" changed to "vcore" in the mainline kernel */ REGULATOR_SUPPLY("vcore", "sdi0"), REGULATOR_SUPPLY("vcore", "sdi1"), @@ -2958,9 +2959,10 @@ static struct mfd_cell db8500_prcmu_devs[] = { * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic * */ -static int __init db8500_prcmu_probe(struct platform_device *pdev) +static int __devinit db8500_prcmu_probe(struct platform_device *pdev) { - int err = 0; + struct device_node *np = pdev->dev.of_node; + int irq = 0, err = 0; if (ux500_is_svp()) return -ENODEV; @@ -2970,8 +2972,14 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) /* Clean up the mailbox interrupts after pre-kernel code. */ writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); - err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler, - prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); + if (np) + irq = platform_get_irq(pdev, 0); + + if (!np || irq <= 0) + irq = IRQ_DB8500_PRCMU1; + + err = request_threaded_irq(irq, prcmu_irq_handler, + prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL); if (err < 0) { pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n"); err = -EBUSY; @@ -2981,14 +2989,16 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev) if (cpu_is_u8500v20_or_later()) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); - err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, - ARRAY_SIZE(db8500_prcmu_devs), NULL, - 0); + if (!np) { + err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, + ARRAY_SIZE(db8500_prcmu_devs), NULL, 0); + if (err) { + pr_err("prcmu: Failed to add subdevices\n"); + return err; + } + } - if (err) - pr_err("prcmu: Failed to add subdevices\n"); - else - pr_info("DB8500 PRCMU initialized\n"); + pr_info("DB8500 PRCMU initialized\n"); no_irq_return: return err; @@ -2999,11 +3009,12 @@ static struct platform_driver db8500_prcmu_driver = { .name = "db8500-prcmu", .owner = THIS_MODULE, }, + .probe = db8500_prcmu_probe, }; static int __init db8500_prcmu_init(void) { - return platform_driver_probe(&db8500_prcmu_driver, db8500_prcmu_probe); + return platform_driver_register(&db8500_prcmu_driver); } arch_initcall(db8500_prcmu_init); diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index b76657eb0c5..59df5584cb5 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -406,7 +406,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) return -ENXIO; } - msic = kzalloc(sizeof(*msic), GFP_KERNEL); + msic = devm_kzalloc(&pdev->dev, sizeof(*msic), GFP_KERNEL); if (!msic) return -ENOMEM; @@ -421,21 +421,13 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get SRAM iomem resource\n"); - ret = -ENODEV; - goto fail_free_msic; + return -ENODEV; } - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - ret = -EBUSY; - goto fail_free_msic; - } - - msic->irq_base = ioremap_nocache(res->start, resource_size(res)); + msic->irq_base = devm_request_and_ioremap(&pdev->dev, res); if (!msic->irq_base) { dev_err(&pdev->dev, "failed to map SRAM memory\n"); - ret = -ENOMEM; - goto fail_release_region; + return -ENOMEM; } platform_set_drvdata(pdev, msic); @@ -443,7 +435,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) ret = intel_msic_init_devices(msic); if (ret) { dev_err(&pdev->dev, "failed to initialize MSIC devices\n"); - goto fail_unmap_mem; + return ret; } dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n", @@ -451,27 +443,14 @@ static int __devinit intel_msic_probe(struct platform_device *pdev) msic->vendor); return 0; - -fail_unmap_mem: - iounmap(msic->irq_base); -fail_release_region: - release_mem_region(res->start, resource_size(res)); -fail_free_msic: - kfree(msic); - - return ret; } static int __devexit intel_msic_remove(struct platform_device *pdev) { struct intel_msic *msic = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); intel_msic_remove_devices(msic); platform_set_drvdata(pdev, NULL); - iounmap(msic->irq_base); - release_mem_region(res->start, resource_size(res)); - kfree(msic); return 0; } diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index a9223ed1b7c..2ea99989551 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -283,23 +283,8 @@ static struct pci_driver cmodio_pci_driver = { .remove = __devexit_p(cmodio_pci_remove), }; -/* - * Module Init / Exit - */ - -static int __init cmodio_init(void) -{ - return pci_register_driver(&cmodio_pci_driver); -} - -static void __exit cmodio_exit(void) -{ - pci_unregister_driver(&cmodio_pci_driver); -} +module_pci_driver(cmodio_pci_driver); MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver"); MODULE_LICENSE("GPL"); - -module_init(cmodio_init); -module_exit(cmodio_exit); diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c new file mode 100644 index 00000000000..0b2879b87fd --- /dev/null +++ b/drivers/mfd/lm3533-core.c @@ -0,0 +1,667 @@ +/* + * lm3533-core.c -- LM3533 Core + * + * Copyright (C) 2011-2012 Texas Instruments + * + * Author: Johan Hovold <jhovold@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/regmap.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <linux/mfd/lm3533.h> + + +#define LM3533_BOOST_OVP_MASK 0x06 +#define LM3533_BOOST_OVP_SHIFT 1 + +#define LM3533_BOOST_FREQ_MASK 0x01 +#define LM3533_BOOST_FREQ_SHIFT 0 + +#define LM3533_BL_ID_MASK 1 +#define LM3533_LED_ID_MASK 3 +#define LM3533_BL_ID_MAX 1 +#define LM3533_LED_ID_MAX 3 + +#define LM3533_HVLED_ID_MAX 2 +#define LM3533_LVLED_ID_MAX 5 + +#define LM3533_REG_OUTPUT_CONF1 0x10 +#define LM3533_REG_OUTPUT_CONF2 0x11 +#define LM3533_REG_BOOST_PWM 0x2c + +#define LM3533_REG_MAX 0xb2 + + +static struct mfd_cell lm3533_als_devs[] = { + { + .name = "lm3533-als", + .id = -1, + }, +}; + +static struct mfd_cell lm3533_bl_devs[] = { + { + .name = "lm3533-backlight", + .id = 0, + }, + { + .name = "lm3533-backlight", + .id = 1, + }, +}; + +static struct mfd_cell lm3533_led_devs[] = { + { + .name = "lm3533-leds", + .id = 0, + }, + { + .name = "lm3533-leds", + .id = 1, + }, + { + .name = "lm3533-leds", + .id = 2, + }, + { + .name = "lm3533-leds", + .id = 3, + }, +}; + +int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val) +{ + int tmp; + int ret; + + ret = regmap_read(lm3533->regmap, reg, &tmp); + if (ret < 0) { + dev_err(lm3533->dev, "failed to read register %02x: %d\n", + reg, ret); + return ret; + } + + *val = tmp; + + dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_read); + +int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val) +{ + int ret; + + dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val); + + ret = regmap_write(lm3533->regmap, reg, val); + if (ret < 0) { + dev_err(lm3533->dev, "failed to write register %02x: %d\n", + reg, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_write); + +int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask) +{ + int ret; + + dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask); + + ret = regmap_update_bits(lm3533->regmap, reg, mask, val); + if (ret < 0) { + dev_err(lm3533->dev, "failed to update register %02x: %d\n", + reg, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_update); + +static int lm3533_set_boost_freq(struct lm3533 *lm3533, + enum lm3533_boost_freq freq) +{ + int ret; + + ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM, + freq << LM3533_BOOST_FREQ_SHIFT, + LM3533_BOOST_FREQ_MASK); + if (ret) + dev_err(lm3533->dev, "failed to set boost frequency\n"); + + return ret; +} + + +static int lm3533_set_boost_ovp(struct lm3533 *lm3533, + enum lm3533_boost_ovp ovp) +{ + int ret; + + ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM, + ovp << LM3533_BOOST_OVP_SHIFT, + LM3533_BOOST_OVP_MASK); + if (ret) + dev_err(lm3533->dev, "failed to set boost ovp\n"); + + return ret; +} + +/* + * HVLED output config -- output hvled controlled by backlight bl + */ +static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl) +{ + u8 val; + u8 mask; + int shift; + int ret; + + if (hvled == 0 || hvled > LM3533_HVLED_ID_MAX) + return -EINVAL; + + if (bl > LM3533_BL_ID_MAX) + return -EINVAL; + + shift = hvled - 1; + mask = LM3533_BL_ID_MASK << shift; + val = bl << shift; + + ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask); + if (ret) + dev_err(lm3533->dev, "failed to set hvled config\n"); + + return ret; +} + +/* + * LVLED output config -- output lvled controlled by LED led + */ +static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led) +{ + u8 reg; + u8 val; + u8 mask; + int shift; + int ret; + + if (lvled == 0 || lvled > LM3533_LVLED_ID_MAX) + return -EINVAL; + + if (led > LM3533_LED_ID_MAX) + return -EINVAL; + + if (lvled < 4) { + reg = LM3533_REG_OUTPUT_CONF1; + shift = 2 * lvled; + } else { + reg = LM3533_REG_OUTPUT_CONF2; + shift = 2 * (lvled - 4); + } + + mask = LM3533_LED_ID_MASK << shift; + val = led << shift; + + ret = lm3533_update(lm3533, reg, val, mask); + if (ret) + dev_err(lm3533->dev, "failed to set lvled config\n"); + + return ret; +} + +static void lm3533_enable(struct lm3533 *lm3533) +{ + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_set_value(lm3533->gpio_hwen, 1); +} + +static void lm3533_disable(struct lm3533 *lm3533) +{ + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_set_value(lm3533->gpio_hwen, 0); +} + +enum lm3533_attribute_type { + LM3533_ATTR_TYPE_BACKLIGHT, + LM3533_ATTR_TYPE_LED, +}; + +struct lm3533_device_attribute { + struct device_attribute dev_attr; + enum lm3533_attribute_type type; + union { + struct { + u8 id; + } output; + } u; +}; + +#define to_lm3533_dev_attr(_attr) \ + container_of(_attr, struct lm3533_device_attribute, dev_attr) + +static ssize_t show_output(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm3533 *lm3533 = dev_get_drvdata(dev); + struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); + int id = lattr->u.output.id; + u8 reg; + u8 val; + u8 mask; + int shift; + int ret; + + if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) { + reg = LM3533_REG_OUTPUT_CONF1; + shift = id - 1; + mask = LM3533_BL_ID_MASK << shift; + } else { + if (id < 4) { + reg = LM3533_REG_OUTPUT_CONF1; + shift = 2 * id; + } else { + reg = LM3533_REG_OUTPUT_CONF2; + shift = 2 * (id - 4); + } + mask = LM3533_LED_ID_MASK << shift; + } + + ret = lm3533_read(lm3533, reg, &val); + if (ret) + return ret; + + val = (val & mask) >> shift; + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t store_output(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lm3533 *lm3533 = dev_get_drvdata(dev); + struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr); + int id = lattr->u.output.id; + u8 val; + int ret; + + if (kstrtou8(buf, 0, &val)) + return -EINVAL; + + if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) + ret = lm3533_set_hvled_config(lm3533, id, val); + else + ret = lm3533_set_lvled_config(lm3533, id, val); + + if (ret) + return ret; + + return len; +} + +#define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \ + struct lm3533_device_attribute lm3533_dev_attr_##_name = \ + { .dev_attr = __ATTR(_name, _mode, _show, _store), \ + .type = _type, \ + .u.output = { .id = _id }, } + +#define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \ + LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \ + show_output, store_output, _type, _id) + +#define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \ + LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr) +#define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \ + LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr) +/* + * Output config: + * + * output_hvled<nr> 0-1 + * output_lvled<nr> 0-3 + */ +static LM3533_OUTPUT_HVLED_ATTR_RW(1); +static LM3533_OUTPUT_HVLED_ATTR_RW(2); +static LM3533_OUTPUT_LVLED_ATTR_RW(1); +static LM3533_OUTPUT_LVLED_ATTR_RW(2); +static LM3533_OUTPUT_LVLED_ATTR_RW(3); +static LM3533_OUTPUT_LVLED_ATTR_RW(4); +static LM3533_OUTPUT_LVLED_ATTR_RW(5); + +static struct attribute *lm3533_attributes[] = { + &lm3533_dev_attr_output_hvled1.dev_attr.attr, + &lm3533_dev_attr_output_hvled2.dev_attr.attr, + &lm3533_dev_attr_output_lvled1.dev_attr.attr, + &lm3533_dev_attr_output_lvled2.dev_attr.attr, + &lm3533_dev_attr_output_lvled3.dev_attr.attr, + &lm3533_dev_attr_output_lvled4.dev_attr.attr, + &lm3533_dev_attr_output_lvled5.dev_attr.attr, + NULL, +}; + +#define to_dev_attr(_attr) \ + container_of(_attr, struct device_attribute, attr) + +static umode_t lm3533_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct lm3533 *lm3533 = dev_get_drvdata(dev); + struct device_attribute *dattr = to_dev_attr(attr); + struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr); + enum lm3533_attribute_type type = lattr->type; + umode_t mode = attr->mode; + + if (!lm3533->have_backlights && type == LM3533_ATTR_TYPE_BACKLIGHT) + mode = 0; + else if (!lm3533->have_leds && type == LM3533_ATTR_TYPE_LED) + mode = 0; + + return mode; +}; + +static struct attribute_group lm3533_attribute_group = { + .is_visible = lm3533_attr_is_visible, + .attrs = lm3533_attributes +}; + +static int __devinit lm3533_device_als_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int ret; + + if (!pdata->als) + return 0; + + lm3533_als_devs[0].platform_data = pdata->als; + lm3533_als_devs[0].pdata_size = sizeof(*pdata->als); + + ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0); + if (ret) { + dev_err(lm3533->dev, "failed to add ALS device\n"); + return ret; + } + + lm3533->have_als = 1; + + return 0; +} + +static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int i; + int ret; + + if (!pdata->backlights || pdata->num_backlights == 0) + return 0; + + if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs)) + pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs); + + for (i = 0; i < pdata->num_backlights; ++i) { + lm3533_bl_devs[i].platform_data = &pdata->backlights[i]; + lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]); + } + + ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs, + pdata->num_backlights, NULL, 0); + if (ret) { + dev_err(lm3533->dev, "failed to add backlight devices\n"); + return ret; + } + + lm3533->have_backlights = 1; + + return 0; +} + +static int __devinit lm3533_device_led_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int i; + int ret; + + if (!pdata->leds || pdata->num_leds == 0) + return 0; + + if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs)) + pdata->num_leds = ARRAY_SIZE(lm3533_led_devs); + + for (i = 0; i < pdata->num_leds; ++i) { + lm3533_led_devs[i].platform_data = &pdata->leds[i]; + lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]); + } + + ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs, + pdata->num_leds, NULL, 0); + if (ret) { + dev_err(lm3533->dev, "failed to add LED devices\n"); + return ret; + } + + lm3533->have_leds = 1; + + return 0; +} + +static int __devinit lm3533_device_setup(struct lm3533 *lm3533, + struct lm3533_platform_data *pdata) +{ + int ret; + + ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq); + if (ret) + return ret; + + ret = lm3533_set_boost_ovp(lm3533, pdata->boost_ovp); + if (ret) + return ret; + + return 0; +} + +static int __devinit lm3533_device_init(struct lm3533 *lm3533) +{ + struct lm3533_platform_data *pdata = lm3533->dev->platform_data; + int ret; + + dev_dbg(lm3533->dev, "%s\n", __func__); + + if (!pdata) { + dev_err(lm3533->dev, "no platform data\n"); + return -EINVAL; + } + + lm3533->gpio_hwen = pdata->gpio_hwen; + + dev_set_drvdata(lm3533->dev, lm3533); + + if (gpio_is_valid(lm3533->gpio_hwen)) { + ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW, + "lm3533-hwen"); + if (ret < 0) { + dev_err(lm3533->dev, + "failed to request HWEN GPIO %d\n", + lm3533->gpio_hwen); + return ret; + } + } + + lm3533_enable(lm3533); + + ret = lm3533_device_setup(lm3533, pdata); + if (ret) + goto err_disable; + + lm3533_device_als_init(lm3533); + lm3533_device_bl_init(lm3533); + lm3533_device_led_init(lm3533); + + ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group); + if (ret < 0) { + dev_err(lm3533->dev, "failed to create sysfs attributes\n"); + goto err_unregister; + } + + return 0; + +err_unregister: + mfd_remove_devices(lm3533->dev); +err_disable: + lm3533_disable(lm3533); + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_free(lm3533->gpio_hwen); + + return ret; +} + +static void __devexit lm3533_device_exit(struct lm3533 *lm3533) +{ + dev_dbg(lm3533->dev, "%s\n", __func__); + + sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group); + + mfd_remove_devices(lm3533->dev); + lm3533_disable(lm3533); + if (gpio_is_valid(lm3533->gpio_hwen)) + gpio_free(lm3533->gpio_hwen); +} + +static bool lm3533_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x10 ... 0x2c: + case 0x30 ... 0x38: + case 0x40 ... 0x45: + case 0x50 ... 0x57: + case 0x60 ... 0x6e: + case 0x70 ... 0x75: + case 0x80 ... 0x85: + case 0x90 ... 0x95: + case 0xa0 ... 0xa5: + case 0xb0 ... 0xb2: + return true; + default: + return false; + } +} + +static bool lm3533_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x34 ... 0x36: /* zone */ + case 0x37 ... 0x38: /* adc */ + case 0xb0 ... 0xb1: /* fault */ + return true; + default: + return false; + } +} + +static bool lm3533_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x34: /* zone */ + return true; + default: + return false; + } +} + +static struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LM3533_REG_MAX, + .readable_reg = lm3533_readable_register, + .volatile_reg = lm3533_volatile_register, + .precious_reg = lm3533_precious_register, +}; + +static int __devinit lm3533_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lm3533 *lm3533; + int ret; + + dev_dbg(&i2c->dev, "%s\n", __func__); + + lm3533 = devm_kzalloc(&i2c->dev, sizeof(*lm3533), GFP_KERNEL); + if (!lm3533) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm3533); + + lm3533->regmap = devm_regmap_init_i2c(i2c, ®map_config); + if (IS_ERR(lm3533->regmap)) + return PTR_ERR(lm3533->regmap); + + lm3533->dev = &i2c->dev; + lm3533->irq = i2c->irq; + + ret = lm3533_device_init(lm3533); + if (ret) + return ret; + + return 0; +} + +static int __devexit lm3533_i2c_remove(struct i2c_client *i2c) +{ + struct lm3533 *lm3533 = i2c_get_clientdata(i2c); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + lm3533_device_exit(lm3533); + + return 0; +} + +static const struct i2c_device_id lm3533_i2c_ids[] = { + { "lm3533", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids); + +static struct i2c_driver lm3533_i2c_driver = { + .driver = { + .name = "lm3533", + .owner = THIS_MODULE, + }, + .id_table = lm3533_i2c_ids, + .probe = lm3533_i2c_probe, + .remove = __devexit_p(lm3533_i2c_remove), +}; + +static int __init lm3533_i2c_init(void) +{ + return i2c_add_driver(&lm3533_i2c_driver); +} +subsys_initcall(lm3533_i2c_init); + +static void __exit lm3533_i2c_exit(void) +{ + i2c_del_driver(&lm3533_i2c_driver); +} +module_exit(lm3533_i2c_exit); + +MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); +MODULE_DESCRIPTION("LM3533 Core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lm3533-ctrlbank.c b/drivers/mfd/lm3533-ctrlbank.c new file mode 100644 index 00000000000..a4cb7a5220a --- /dev/null +++ b/drivers/mfd/lm3533-ctrlbank.c @@ -0,0 +1,148 @@ +/* + * lm3533-ctrlbank.c -- LM3533 Generic Control Bank interface + * + * Copyright (C) 2011-2012 Texas Instruments + * + * Author: Johan Hovold <jhovold@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/device.h> +#include <linux/module.h> + +#include <linux/mfd/lm3533.h> + + +#define LM3533_MAX_CURRENT_MIN 5000 +#define LM3533_MAX_CURRENT_MAX 29800 +#define LM3533_MAX_CURRENT_STEP 800 + +#define LM3533_BRIGHTNESS_MAX 255 +#define LM3533_PWM_MAX 0x3f + +#define LM3533_REG_PWM_BASE 0x14 +#define LM3533_REG_MAX_CURRENT_BASE 0x1f +#define LM3533_REG_CTRLBANK_ENABLE 0x27 +#define LM3533_REG_BRIGHTNESS_BASE 0x40 + + +static inline u8 lm3533_ctrlbank_get_reg(struct lm3533_ctrlbank *cb, u8 base) +{ + return base + cb->id; +} + +int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb) +{ + u8 mask; + int ret; + + dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); + + mask = 1 << cb->id; + ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, + mask, mask); + if (ret) + dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_enable); + +int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb) +{ + u8 mask; + int ret; + + dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id); + + mask = 1 << cb->id; + ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask); + if (ret) + dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable); + +/* + * Full-scale current. + * + * imax 5000 - 29800 uA (800 uA step) + */ +int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax) +{ + u8 reg; + u8 val; + int ret; + + if (imax < LM3533_MAX_CURRENT_MIN || imax > LM3533_MAX_CURRENT_MAX) + return -EINVAL; + + val = (imax - LM3533_MAX_CURRENT_MIN) / LM3533_MAX_CURRENT_STEP; + + reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_MAX_CURRENT_BASE); + ret = lm3533_write(cb->lm3533, reg, val); + if (ret) + dev_err(cb->dev, "failed to set max current\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_max_current); + +#define lm3533_ctrlbank_set(_name, _NAME) \ +int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val) \ +{ \ + u8 reg; \ + int ret; \ + \ + if (val > LM3533_##_NAME##_MAX) \ + return -EINVAL; \ + \ + reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \ + ret = lm3533_write(cb->lm3533, reg, val); \ + if (ret) \ + dev_err(cb->dev, "failed to set " #_name "\n"); \ + \ + return ret; \ +} \ +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name); + +#define lm3533_ctrlbank_get(_name, _NAME) \ +int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val) \ +{ \ + u8 reg; \ + int ret; \ + \ + reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \ + ret = lm3533_read(cb->lm3533, reg, val); \ + if (ret) \ + dev_err(cb->dev, "failed to get " #_name "\n"); \ + \ + return ret; \ +} \ +EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name); + +lm3533_ctrlbank_set(brightness, BRIGHTNESS); +lm3533_ctrlbank_get(brightness, BRIGHTNESS); + +/* + * PWM-input control mask: + * + * bit 5 - PWM-input enabled in Zone 4 + * bit 4 - PWM-input enabled in Zone 3 + * bit 3 - PWM-input enabled in Zone 2 + * bit 2 - PWM-input enabled in Zone 1 + * bit 1 - PWM-input enabled in Zone 0 + * bit 0 - PWM-input enabled + */ +lm3533_ctrlbank_set(pwm, PWM); +lm3533_ctrlbank_get(pwm, PWM); + + +MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); +MODULE_DESCRIPTION("LM3533 Control Bank interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c new file mode 100644 index 00000000000..027cc8f8613 --- /dev/null +++ b/drivers/mfd/lpc_ich.c @@ -0,0 +1,888 @@ +/* + * lpc_ich.c - LPC interface for Intel ICH + * + * LPC bridge function of the Intel ICH contains many other + * functional units, such as Interrupt controllers, Timers, + * Power Management, System Management, GPIO, RTC, and LPC + * Configuration Registers. + * + * This driver is derived from lpc_sch. + + * Copyright (c) 2011 Extreme Engineering Solution, Inc. + * Author: Aaron Sierra <asierra@xes-inc.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver supports the following I/O Controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO) + * document number 290687-002, 298242-027: 82801BA (ICH2) + * document number 290733-003, 290739-013: 82801CA (ICH3-S) + * document number 290716-001, 290718-007: 82801CAM (ICH3-M) + * document number 290744-001, 290745-025: 82801DB (ICH4) + * document number 252337-001, 252663-008: 82801DBM (ICH4-M) + * document number 273599-001, 273645-002: 82801E (C-ICH) + * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R) + * document number 300641-004, 300884-013: 6300ESB + * document number 301473-002, 301474-026: 82801F (ICH6) + * document number 313082-001, 313075-006: 631xESB, 632xESB + * document number 307013-003, 307014-024: 82801G (ICH7) + * document number 322896-001, 322897-001: NM10 + * document number 313056-003, 313057-017: 82801H (ICH8) + * document number 316972-004, 316973-012: 82801I (ICH9) + * document number 319973-002, 319974-002: 82801J (ICH10) + * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) + * document number 320066-003, 320257-008: EP80597 (IICH) + * document number 324645-001, 324646-001: Cougar Point (CPT) + * document number TBD : Patsburg (PBG) + * document number TBD : DH89xxCC + * document number TBD : Panther Point + * document number TBD : Lynx Point + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <linux/mfd/core.h> +#include <linux/mfd/lpc_ich.h> + +#define ACPIBASE 0x40 +#define ACPIBASE_GPE_OFF 0x28 +#define ACPIBASE_GPE_END 0x2f +#define ACPIBASE_SMI_OFF 0x30 +#define ACPIBASE_SMI_END 0x33 +#define ACPIBASE_TCO_OFF 0x60 +#define ACPIBASE_TCO_END 0x7f +#define ACPICTRL 0x44 + +#define ACPIBASE_GCS_OFF 0x3410 +#define ACPIBASE_GCS_END 0x3414 + +#define GPIOBASE 0x48 +#define GPIOCTRL 0x4C + +#define RCBABASE 0xf0 + +#define wdt_io_res(i) wdt_res(0, i) +#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i) +#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)]) + +static int lpc_ich_acpi_save = -1; +static int lpc_ich_gpio_save = -1; + +static struct resource wdt_ich_res[] = { + /* ACPI - TCO */ + { + .flags = IORESOURCE_IO, + }, + /* ACPI - SMI */ + { + .flags = IORESOURCE_IO, + }, + /* GCS */ + { + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource gpio_ich_res[] = { + /* GPIO */ + { + .flags = IORESOURCE_IO, + }, + /* ACPI - GPE0 */ + { + .flags = IORESOURCE_IO, + }, +}; + +enum lpc_cells { + LPC_WDT = 0, + LPC_GPIO, +}; + +static struct mfd_cell lpc_ich_cells[] = { + [LPC_WDT] = { + .name = "iTCO_wdt", + .num_resources = ARRAY_SIZE(wdt_ich_res), + .resources = wdt_ich_res, + .ignore_resource_conflicts = true, + }, + [LPC_GPIO] = { + .name = "gpio_ich", + .num_resources = ARRAY_SIZE(gpio_ich_res), + .resources = gpio_ich_res, + .ignore_resource_conflicts = true, + }, +}; + +/* chipset related info */ +enum lpc_chipsets { + LPC_ICH = 0, /* ICH */ + LPC_ICH0, /* ICH0 */ + LPC_ICH2, /* ICH2 */ + LPC_ICH2M, /* ICH2-M */ + LPC_ICH3, /* ICH3-S */ + LPC_ICH3M, /* ICH3-M */ + LPC_ICH4, /* ICH4 */ + LPC_ICH4M, /* ICH4-M */ + LPC_CICH, /* C-ICH */ + LPC_ICH5, /* ICH5 & ICH5R */ + LPC_6300ESB, /* 6300ESB */ + LPC_ICH6, /* ICH6 & ICH6R */ + LPC_ICH6M, /* ICH6-M */ + LPC_ICH6W, /* ICH6W & ICH6RW */ + LPC_631XESB, /* 631xESB/632xESB */ + LPC_ICH7, /* ICH7 & ICH7R */ + LPC_ICH7DH, /* ICH7DH */ + LPC_ICH7M, /* ICH7-M & ICH7-U */ + LPC_ICH7MDH, /* ICH7-M DH */ + LPC_NM10, /* NM10 */ + LPC_ICH8, /* ICH8 & ICH8R */ + LPC_ICH8DH, /* ICH8DH */ + LPC_ICH8DO, /* ICH8DO */ + LPC_ICH8M, /* ICH8M */ + LPC_ICH8ME, /* ICH8M-E */ + LPC_ICH9, /* ICH9 */ + LPC_ICH9R, /* ICH9R */ + LPC_ICH9DH, /* ICH9DH */ + LPC_ICH9DO, /* ICH9DO */ + LPC_ICH9M, /* ICH9M */ + LPC_ICH9ME, /* ICH9M-E */ + LPC_ICH10, /* ICH10 */ + LPC_ICH10R, /* ICH10R */ + LPC_ICH10D, /* ICH10D */ + LPC_ICH10DO, /* ICH10DO */ + LPC_PCH, /* PCH Desktop Full Featured */ + LPC_PCHM, /* PCH Mobile Full Featured */ + LPC_P55, /* P55 */ + LPC_PM55, /* PM55 */ + LPC_H55, /* H55 */ + LPC_QM57, /* QM57 */ + LPC_H57, /* H57 */ + LPC_HM55, /* HM55 */ + LPC_Q57, /* Q57 */ + LPC_HM57, /* HM57 */ + LPC_PCHMSFF, /* PCH Mobile SFF Full Featured */ + LPC_QS57, /* QS57 */ + LPC_3400, /* 3400 */ + LPC_3420, /* 3420 */ + LPC_3450, /* 3450 */ + LPC_EP80579, /* EP80579 */ + LPC_CPT, /* Cougar Point */ + LPC_CPTD, /* Cougar Point Desktop */ + LPC_CPTM, /* Cougar Point Mobile */ + LPC_PBG, /* Patsburg */ + LPC_DH89XXCC, /* DH89xxCC */ + LPC_PPT, /* Panther Point */ + LPC_LPT, /* Lynx Point */ +}; + +struct lpc_ich_info lpc_chipset_info[] __devinitdata = { + [LPC_ICH] = { + .name = "ICH", + .iTCO_version = 1, + }, + [LPC_ICH0] = { + .name = "ICH0", + .iTCO_version = 1, + }, + [LPC_ICH2] = { + .name = "ICH2", + .iTCO_version = 1, + }, + [LPC_ICH2M] = { + .name = "ICH2-M", + .iTCO_version = 1, + }, + [LPC_ICH3] = { + .name = "ICH3-S", + .iTCO_version = 1, + }, + [LPC_ICH3M] = { + .name = "ICH3-M", + .iTCO_version = 1, + }, + [LPC_ICH4] = { + .name = "ICH4", + .iTCO_version = 1, + }, + [LPC_ICH4M] = { + .name = "ICH4-M", + .iTCO_version = 1, + }, + [LPC_CICH] = { + .name = "C-ICH", + .iTCO_version = 1, + }, + [LPC_ICH5] = { + .name = "ICH5 or ICH5R", + .iTCO_version = 1, + }, + [LPC_6300ESB] = { + .name = "6300ESB", + .iTCO_version = 1, + }, + [LPC_ICH6] = { + .name = "ICH6 or ICH6R", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_ICH6M] = { + .name = "ICH6-M", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_ICH6W] = { + .name = "ICH6W or ICH6RW", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_631XESB] = { + .name = "631xESB/632xESB", + .iTCO_version = 2, + .gpio_version = ICH_V6_GPIO, + }, + [LPC_ICH7] = { + .name = "ICH7 or ICH7R", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH7DH] = { + .name = "ICH7DH", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH7M] = { + .name = "ICH7-M or ICH7-U", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH7MDH] = { + .name = "ICH7-M DH", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_NM10] = { + .name = "NM10", + .iTCO_version = 2, + }, + [LPC_ICH8] = { + .name = "ICH8 or ICH8R", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8DH] = { + .name = "ICH8DH", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8DO] = { + .name = "ICH8DO", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8M] = { + .name = "ICH8M", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH8ME] = { + .name = "ICH8M-E", + .iTCO_version = 2, + .gpio_version = ICH_V7_GPIO, + }, + [LPC_ICH9] = { + .name = "ICH9", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9R] = { + .name = "ICH9R", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9DH] = { + .name = "ICH9DH", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9DO] = { + .name = "ICH9DO", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9M] = { + .name = "ICH9M", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH9ME] = { + .name = "ICH9M-E", + .iTCO_version = 2, + .gpio_version = ICH_V9_GPIO, + }, + [LPC_ICH10] = { + .name = "ICH10", + .iTCO_version = 2, + .gpio_version = ICH_V10CONS_GPIO, + }, + [LPC_ICH10R] = { + .name = "ICH10R", + .iTCO_version = 2, + .gpio_version = ICH_V10CONS_GPIO, + }, + [LPC_ICH10D] = { + .name = "ICH10D", + .iTCO_version = 2, + .gpio_version = ICH_V10CORP_GPIO, + }, + [LPC_ICH10DO] = { + .name = "ICH10DO", + .iTCO_version = 2, + .gpio_version = ICH_V10CORP_GPIO, + }, + [LPC_PCH] = { + .name = "PCH Desktop Full Featured", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PCHM] = { + .name = "PCH Mobile Full Featured", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_P55] = { + .name = "P55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PM55] = { + .name = "PM55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_H55] = { + .name = "H55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_QM57] = { + .name = "QM57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_H57] = { + .name = "H57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_HM55] = { + .name = "HM55", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_Q57] = { + .name = "Q57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_HM57] = { + .name = "HM57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PCHMSFF] = { + .name = "PCH Mobile SFF Full Featured", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_QS57] = { + .name = "QS57", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_3400] = { + .name = "3400", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_3420] = { + .name = "3420", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_3450] = { + .name = "3450", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_EP80579] = { + .name = "EP80579", + .iTCO_version = 2, + }, + [LPC_CPT] = { + .name = "Cougar Point", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_CPTD] = { + .name = "Cougar Point Desktop", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_CPTM] = { + .name = "Cougar Point Mobile", + .iTCO_version = 2, + .gpio_version = ICH_V5_GPIO, + }, + [LPC_PBG] = { + .name = "Patsburg", + .iTCO_version = 2, + }, + [LPC_DH89XXCC] = { + .name = "DH89xxCC", + .iTCO_version = 2, + }, + [LPC_PPT] = { + .name = "Panther Point", + .iTCO_version = 2, + }, + [LPC_LPT] = { + .name = "Lynx Point", + .iTCO_version = 2, + }, +}; + +/* + * This data only exists for exporting the supported PCI ids + * via MODULE_DEVICE_TABLE. We do not actually register a + * pci_driver, because the I/O Controller Hub has also other + * functions that probably will be registered by other drivers. + */ +static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = { + { PCI_VDEVICE(INTEL, 0x2410), LPC_ICH}, + { PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0}, + { PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2}, + { PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M}, + { PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3}, + { PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M}, + { PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4}, + { PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M}, + { PCI_VDEVICE(INTEL, 0x2450), LPC_CICH}, + { PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5}, + { PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB}, + { PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6}, + { PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M}, + { PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W}, + { PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7}, + { PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH}, + { PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M}, + { PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH}, + { PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10}, + { PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8}, + { PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH}, + { PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO}, + { PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M}, + { PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME}, + { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, + { PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R}, + { PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH}, + { PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO}, + { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, + { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, + { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10}, + { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, + { PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D}, + { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, + { PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH}, + { PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM}, + { PCI_VDEVICE(INTEL, 0x3b02), LPC_P55}, + { PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55}, + { PCI_VDEVICE(INTEL, 0x3b06), LPC_H55}, + { PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57}, + { PCI_VDEVICE(INTEL, 0x3b08), LPC_H57}, + { PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55}, + { PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57}, + { PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57}, + { PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF}, + { PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57}, + { PCI_VDEVICE(INTEL, 0x3b12), LPC_3400}, + { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420}, + { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450}, + { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579}, + { PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD}, + { PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM}, + { PCI_VDEVICE(INTEL, 0x1c44), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c45), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c46), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c47), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c48), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c49), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4a), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4b), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4c), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4d), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4e), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c4f), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c50), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c51), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c52), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c53), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c54), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c55), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c56), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c57), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c58), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c59), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5a), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5b), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5c), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5d), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5e), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT}, + { PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG}, + { PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG}, + { PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC}, + { PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e43), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e44), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e45), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e46), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e47), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e48), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e49), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4a), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4b), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4c), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4d), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4e), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e4f), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e50), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e51), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e52), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e53), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e54), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e55), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e56), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e57), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e58), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e59), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5a), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5b), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5c), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c43), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c44), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c45), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c46), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c47), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c48), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c49), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4a), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4b), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4c), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4d), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4e), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c4f), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c50), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c51), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c52), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c53), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c54), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c55), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c56), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c57), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c58), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c59), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5a), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5b), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5c), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT}, + { PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT}, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE(pci, lpc_ich_ids); + +static void lpc_ich_restore_config_space(struct pci_dev *dev) +{ + if (lpc_ich_acpi_save >= 0) { + pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save); + lpc_ich_acpi_save = -1; + } + + if (lpc_ich_gpio_save >= 0) { + pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save); + lpc_ich_gpio_save = -1; + } +} + +static void __devinit lpc_ich_enable_acpi_space(struct pci_dev *dev) +{ + u8 reg_save; + + pci_read_config_byte(dev, ACPICTRL, ®_save); + pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10); + lpc_ich_acpi_save = reg_save; +} + +static void __devinit lpc_ich_enable_gpio_space(struct pci_dev *dev) +{ + u8 reg_save; + + pci_read_config_byte(dev, GPIOCTRL, ®_save); + pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10); + lpc_ich_gpio_save = reg_save; +} + +static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell, + const struct pci_device_id *id) +{ + cell->platform_data = &lpc_chipset_info[id->driver_data]; + cell->pdata_size = sizeof(struct lpc_ich_info); +} + +static int __devinit lpc_ich_init_gpio(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u32 base_addr_cfg; + u32 base_addr; + int ret; + bool acpi_conflict = false; + struct resource *res; + + /* Setup power management base register */ + 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"); + lpc_ich_cells[LPC_GPIO].num_resources--; + goto gpe0_done; + } + + res = &gpio_ich_res[ICH_RES_GPE0]; + res->start = base_addr + ACPIBASE_GPE_OFF; + res->end = base_addr + ACPIBASE_GPE_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + /* + * This isn't fatal for the GPIO, but we have to make sure that + * the platform_device subsystem doesn't see this resource + * or it will register an invalid region. + */ + lpc_ich_cells[LPC_GPIO].num_resources--; + acpi_conflict = true; + } else { + lpc_ich_enable_acpi_space(dev); + } + +gpe0_done: + /* Setup GPIO base register */ + 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"); + ret = -ENODEV; + goto gpio_done; + } + + /* Older devices provide fewer GPIO and have a smaller resource size. */ + res = &gpio_ich_res[ICH_RES_GPIO]; + res->start = base_addr; + switch (lpc_chipset_info[id->driver_data].gpio_version) { + case ICH_V5_GPIO: + case ICH_V10CORP_GPIO: + res->end = res->start + 128 - 1; + break; + default: + res->end = res->start + 64 - 1; + break; + } + + ret = acpi_check_resource_conflict(res); + if (ret) { + /* this isn't necessarily fatal for the GPIO */ + acpi_conflict = true; + goto gpio_done; + } + lpc_ich_enable_gpio_space(dev); + + lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id); + ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO], + 1, NULL, 0); + +gpio_done: + if (acpi_conflict) + pr_warn("Resource conflict(s) found affecting %s\n", + lpc_ich_cells[LPC_GPIO].name); + return ret; +} + +static int __devinit lpc_ich_init_wdt(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u32 base_addr_cfg; + u32 base_addr; + int ret; + bool acpi_conflict = false; + struct resource *res; + + /* Setup power management base register */ + 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"); + ret = -ENODEV; + goto wdt_done; + } + + res = wdt_io_res(ICH_RES_IO_TCO); + res->start = base_addr + ACPIBASE_TCO_OFF; + res->end = base_addr + ACPIBASE_TCO_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + acpi_conflict = true; + goto wdt_done; + } + + res = wdt_io_res(ICH_RES_IO_SMI); + res->start = base_addr + ACPIBASE_SMI_OFF; + res->end = base_addr + ACPIBASE_SMI_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + acpi_conflict = true; + goto wdt_done; + } + lpc_ich_enable_acpi_space(dev); + + /* + * Get the Memory-Mapped GCS register. To get access to it + * 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) { + 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"); + ret = -ENODEV; + goto wdt_done; + } + res = wdt_mem_res(ICH_RES_MEM_GCS); + res->start = base_addr + ACPIBASE_GCS_OFF; + res->end = base_addr + ACPIBASE_GCS_END; + ret = acpi_check_resource_conflict(res); + if (ret) { + acpi_conflict = true; + goto wdt_done; + } + } + + lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id); + ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT], + 1, NULL, 0); + +wdt_done: + if (acpi_conflict) + pr_warn("Resource conflict(s) found affecting %s\n", + lpc_ich_cells[LPC_WDT].name); + return ret; +} + +static int __devinit lpc_ich_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + int ret; + bool cell_added = false; + + ret = lpc_ich_init_wdt(dev, id); + if (!ret) + cell_added = true; + + ret = lpc_ich_init_gpio(dev, id); + if (!ret) + cell_added = true; + + /* + * We only care if at least one or none of the cells registered + * successfully. + */ + if (!cell_added) { + lpc_ich_restore_config_space(dev); + return -ENODEV; + } + + return 0; +} + +static void __devexit lpc_ich_remove(struct pci_dev *dev) +{ + mfd_remove_devices(&dev->dev); + lpc_ich_restore_config_space(dev); +} + +static struct pci_driver lpc_ich_driver = { + .name = "lpc_ich", + .id_table = lpc_ich_ids, + .probe = lpc_ich_probe, + .remove = __devexit_p(lpc_ich_remove), +}; + +static int __init lpc_ich_init(void) +{ + return pci_register_driver(&lpc_ich_driver); +} + +static void __exit lpc_ich_exit(void) +{ + pci_unregister_driver(&lpc_ich_driver); +} + +module_init(lpc_ich_init); +module_exit(lpc_ich_exit); + +MODULE_AUTHOR("Aaron Sierra <asierra@xes-inc.com>"); +MODULE_DESCRIPTION("LPC interface for Intel ICH"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index abc421364a4..9f20abc5e39 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -36,6 +36,7 @@ #define GPIOBASE 0x44 #define GPIO_IO_SIZE 64 +#define GPIO_IO_SIZE_CENTERTON 128 #define WDTBASE 0x84 #define WDT_IO_SIZE 64 @@ -68,7 +69,7 @@ static struct resource wdt_sch_resource = { static struct mfd_cell tunnelcreek_cells[] = { { - .name = "tunnelcreek_wdt", + .name = "ie6xx_wdt", .num_resources = 1, .resources = &wdt_sch_resource, }, @@ -77,6 +78,7 @@ static struct mfd_cell tunnelcreek_cells[] = { static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) }, { 0, } }; MODULE_DEVICE_TABLE(pci, lpc_sch_ids); @@ -115,7 +117,11 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, } gpio_sch_resource.start = base_addr; - gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; + + if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1; + else + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++) lpc_sch_cells[i].id = id->device; @@ -125,7 +131,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, if (ret) goto out_dev; - if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) { + if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC + || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); if (!(base_addr_cfg & (1 << 31))) { dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n"); @@ -167,18 +174,7 @@ static struct pci_driver lpc_sch_driver = { .remove = __devexit_p(lpc_sch_remove), }; -static int __init lpc_sch_init(void) -{ - return pci_register_driver(&lpc_sch_driver); -} - -static void __exit lpc_sch_exit(void) -{ - pci_unregister_driver(&lpc_sch_driver); -} - -module_init(lpc_sch_init); -module_exit(lpc_sch_exit); +module_pci_driver(lpc_sch_driver); MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c new file mode 100644 index 00000000000..2b403569e0a --- /dev/null +++ b/drivers/mfd/max77693-irq.c @@ -0,0 +1,309 @@ +/* + * max77693-irq.c - Interrupt controller support for MAX77693 + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * SangYoung Son <hello.son@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997-irq.c + */ + +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/irqdomain.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> + +static const u8 max77693_mask_reg[] = { + [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK, + [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK, + [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK, + [MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1, + [MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2, + [MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3, +}; + +static struct regmap *max77693_get_regmap(struct max77693_dev *max77693, + enum max77693_irq_source src) +{ + switch (src) { + case LED_INT ... CHG_INT: + return max77693->regmap; + case MUIC_INT1 ... MUIC_INT3: + return max77693->regmap_muic; + default: + return ERR_PTR(-EINVAL); + } +} + +struct max77693_irq_data { + int mask; + enum max77693_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct max77693_irq_data max77693_irqs[] = { + DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN, LED_INT, 1 << 0), + DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT, LED_INT, 1 << 1), + DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN, LED_INT, 1 << 2), + DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT, LED_INT, 1 << 3), + DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH, LED_INT, 1 << 4), + + DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT, TOPSYS_INT, 1 << 0), + DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT, TOPSYS_INT, 1 << 1), + DECLARE_IRQ(MAX77693_TOPSYS_IRQ_LOWSYS_INT, TOPSYS_INT, 1 << 3), + + DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I, CHG_INT, 1 << 0), + DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I, CHG_INT, 1 << 2), + DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I, CHG_INT, 1 << 3), + DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I, CHG_INT, 1 << 4), + DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I, CHG_INT, 1 << 6), + + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC, MUIC_INT1, 1 << 0), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_LOW, MUIC_INT1, 1 << 1), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_ERR, MUIC_INT1, 1 << 2), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K, MUIC_INT1, 1 << 3), + + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP, MUIC_INT2, 1 << 0), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN, MUIC_INT2, 1 << 1), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR, MUIC_INT2, 1 << 2), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP, MUIC_INT2, 1 << 3), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT, MUIC_INT2, 1 << 4), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM, MUIC_INT2, 1 << 5), + + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC, MUIC_INT3, 1 << 0), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC, MUIC_INT3, 1 << 1), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP, MUIC_INT3, 1 << 2), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR, MUIC_INT3, 1 << 3), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MUIC_INT3, 1 << 4), + DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BAT_DET, MUIC_INT3, 1 << 5), +}; + +static void max77693_irq_lock(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + + mutex_lock(&max77693->irqlock); +} + +static void max77693_irq_sync_unlock(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { + u8 mask_reg = max77693_mask_reg[i]; + struct regmap *map = max77693_get_regmap(max77693, i); + + if (mask_reg == MAX77693_REG_INVALID || + IS_ERR_OR_NULL(map)) + continue; + max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i]; + + max77693_write_reg(map, max77693_mask_reg[i], + max77693->irq_masks_cur[i]); + } + + mutex_unlock(&max77693->irqlock); +} + +static const inline struct max77693_irq_data * +irq_to_max77693_irq(struct max77693_dev *max77693, int irq) +{ + return &max77693_irqs[irq]; +} + +static void max77693_irq_mask(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + const struct max77693_irq_data *irq_data = + irq_to_max77693_irq(max77693, data->irq); + + if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) + max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; + else + max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; +} + +static void max77693_irq_unmask(struct irq_data *data) +{ + struct max77693_dev *max77693 = irq_get_chip_data(data->irq); + const struct max77693_irq_data *irq_data = + irq_to_max77693_irq(max77693, data->irq); + + if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) + max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; + else + max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; +} + +static struct irq_chip max77693_irq_chip = { + .name = "max77693", + .irq_bus_lock = max77693_irq_lock, + .irq_bus_sync_unlock = max77693_irq_sync_unlock, + .irq_mask = max77693_irq_mask, + .irq_unmask = max77693_irq_unmask, +}; + +#define MAX77693_IRQSRC_CHG (1 << 0) +#define MAX77693_IRQSRC_TOP (1 << 1) +#define MAX77693_IRQSRC_FLASH (1 << 2) +#define MAX77693_IRQSRC_MUIC (1 << 3) +static irqreturn_t max77693_irq_thread(int irq, void *data) +{ + struct max77693_dev *max77693 = data; + u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {}; + u8 irq_src; + int ret; + int i, cur_irq; + + ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_INTSRC, + &irq_src); + if (ret < 0) { + dev_err(max77693->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (irq_src & MAX77693_IRQSRC_CHG) + /* CHG_INT */ + ret = max77693_read_reg(max77693->regmap, MAX77693_CHG_REG_CHG_INT, + &irq_reg[CHG_INT]); + + if (irq_src & MAX77693_IRQSRC_TOP) + /* TOPSYS_INT */ + ret = max77693_read_reg(max77693->regmap, + MAX77693_PMIC_REG_TOPSYS_INT, &irq_reg[TOPSYS_INT]); + + if (irq_src & MAX77693_IRQSRC_FLASH) + /* LED_INT */ + ret = max77693_read_reg(max77693->regmap, + MAX77693_LED_REG_FLASH_INT, &irq_reg[LED_INT]); + + if (irq_src & MAX77693_IRQSRC_MUIC) + /* MUIC INT1 ~ INT3 */ + max77693_bulk_read(max77693->regmap, MAX77693_MUIC_REG_INT1, + MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]); + + /* Apply masking */ + for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { + if (i >= MUIC_INT1 && i <= MUIC_INT3) + irq_reg[i] &= max77693->irq_masks_cur[i]; + else + irq_reg[i] &= ~max77693->irq_masks_cur[i]; + } + + /* Report */ + for (i = 0; i < MAX77693_IRQ_NR; i++) { + if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask) { + cur_irq = irq_find_mapping(max77693->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } + } + + return IRQ_HANDLED; +} + +int max77693_irq_resume(struct max77693_dev *max77693) +{ + if (max77693->irq) + max77693_irq_thread(0, max77693); + + return 0; +} + +static int max77693_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max77693_dev *max77693 = d->host_data; + + irq_set_chip_data(irq, max77693); + irq_set_chip_and_handler(irq, &max77693_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + return 0; +} + +static struct irq_domain_ops max77693_irq_domain_ops = { + .map = max77693_irq_domain_map, +}; + +int max77693_irq_init(struct max77693_dev *max77693) +{ + struct irq_domain *domain; + int i; + int ret; + + mutex_init(&max77693->irqlock); + + /* Mask individual interrupt sources */ + for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { + struct regmap *map; + /* MUIC IRQ 0:MASK 1:NOT MASK */ + /* Other IRQ 1:MASK 0:NOT MASK */ + if (i >= MUIC_INT1 && i <= MUIC_INT3) { + max77693->irq_masks_cur[i] = 0x00; + max77693->irq_masks_cache[i] = 0x00; + } else { + max77693->irq_masks_cur[i] = 0xff; + max77693->irq_masks_cache[i] = 0xff; + } + map = max77693_get_regmap(max77693, i); + + if (IS_ERR_OR_NULL(map)) + continue; + if (max77693_mask_reg[i] == MAX77693_REG_INVALID) + continue; + if (i >= MUIC_INT1 && i <= MUIC_INT3) + max77693_write_reg(map, max77693_mask_reg[i], 0x00); + else + max77693_write_reg(map, max77693_mask_reg[i], 0xff); + } + + domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR, + &max77693_irq_domain_ops, max77693); + if (!domain) { + dev_err(max77693->dev, "could not create irq domain\n"); + return -ENODEV; + } + max77693->irq_domain = domain; + + ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max77693-irq", max77693); + + if (ret) + dev_err(max77693->dev, "Failed to request IRQ %d: %d\n", + max77693->irq, ret); + + return 0; +} + +void max77693_irq_exit(struct max77693_dev *max77693) +{ + if (max77693->irq) + free_irq(max77693->irq, max77693); +} diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c new file mode 100644 index 00000000000..e9e4278722f --- /dev/null +++ b/drivers/mfd/max77693.c @@ -0,0 +1,249 @@ +/* + * max77693.c - mfd core driver for the MAX 77693 + * + * Copyright (C) 2012 Samsung Electronics + * SangYoung Son <hello.son@smasung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> +#include <linux/regulator/machine.h> +#include <linux/regmap.h> + +#define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */ +#define I2C_ADDR_MUIC (0x4A >> 1) +#define I2C_ADDR_HAPTIC (0x90 >> 1) + +static struct mfd_cell max77693_devs[] = { + { .name = "max77693-pmic", }, + { .name = "max77693-charger", }, + { .name = "max77693-flash", }, + { .name = "max77693-muic", }, + { .name = "max77693-haptic", }, +}; + +int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest) +{ + unsigned int val; + int ret; + + ret = regmap_read(map, reg, &val); + *dest = val; + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_read_reg); + +int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf) +{ + int ret; + + ret = regmap_bulk_read(map, reg, buf, count); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_bulk_read); + +int max77693_write_reg(struct regmap *map, u8 reg, u8 value) +{ + int ret; + + ret = regmap_write(map, reg, value); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_write_reg); + +int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf) +{ + int ret; + + ret = regmap_bulk_write(map, reg, buf, count); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_bulk_write); + +int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask) +{ + int ret; + + ret = regmap_update_bits(map, reg, mask, val); + + return ret; +} +EXPORT_SYMBOL_GPL(max77693_update_reg); + +static const struct regmap_config max77693_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77693_PMIC_REG_END, +}; + +static int max77693_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77693_dev *max77693; + struct max77693_platform_data *pdata = i2c->dev.platform_data; + u8 reg_data; + int ret = 0; + + max77693 = devm_kzalloc(&i2c->dev, + sizeof(struct max77693_dev), GFP_KERNEL); + if (max77693 == NULL) + return -ENOMEM; + + max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config); + if (IS_ERR(max77693->regmap)) { + ret = PTR_ERR(max77693->regmap); + dev_err(max77693->dev,"failed to allocate register map: %d\n", + ret); + goto err_regmap; + } + + i2c_set_clientdata(i2c, max77693); + max77693->dev = &i2c->dev; + max77693->i2c = i2c; + max77693->irq = i2c->irq; + max77693->type = id->driver_data; + + if (!pdata) + goto err_regmap; + + max77693->wakeup = pdata->wakeup; + + mutex_init(&max77693->iolock); + + if (max77693_read_reg(max77693->regmap, + MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) { + dev_err(max77693->dev, "device not found on this channel\n"); + ret = -ENODEV; + goto err_regmap; + } else + dev_info(max77693->dev, "device ID: 0x%x\n", reg_data); + + max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); + i2c_set_clientdata(max77693->muic, max77693); + + max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); + i2c_set_clientdata(max77693->haptic, max77693); + + ret = max77693_irq_init(max77693); + if (ret < 0) + goto err_mfd; + + pm_runtime_set_active(max77693->dev); + + ret = mfd_add_devices(max77693->dev, -1, max77693_devs, + ARRAY_SIZE(max77693_devs), NULL, 0); + if (ret < 0) + goto err_mfd; + + device_init_wakeup(max77693->dev, pdata->wakeup); + + return ret; + +err_mfd: + i2c_unregister_device(max77693->muic); + i2c_unregister_device(max77693->haptic); +err_regmap: + kfree(max77693); + + return ret; +} + +static int max77693_i2c_remove(struct i2c_client *i2c) +{ + struct max77693_dev *max77693 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77693->dev); + i2c_unregister_device(max77693->muic); + i2c_unregister_device(max77693->haptic); + + return 0; +} + +static const struct i2c_device_id max77693_i2c_id[] = { + { "max77693", TYPE_MAX77693 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max77693_i2c_id); + +static int max77693_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77693_dev *max77693 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max77693->irq, 1); + return 0; +} + +static int max77693_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77693_dev *max77693 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max77693->irq, 0); + return max77693_irq_resume(max77693); +} + +const struct dev_pm_ops max77693_pm = { + .suspend = max77693_suspend, + .resume = max77693_resume, +}; + +static struct i2c_driver max77693_i2c_driver = { + .driver = { + .name = "max77693", + .owner = THIS_MODULE, + .pm = &max77693_pm, + }, + .probe = max77693_i2c_probe, + .remove = max77693_i2c_remove, + .id_table = max77693_i2c_id, +}; + +static int __init max77693_i2c_init(void) +{ + return i2c_add_driver(&max77693_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max77693_i2c_init); + +static void __exit max77693_i2c_exit(void) +{ + i2c_del_driver(&max77693_i2c_driver); +} +module_exit(max77693_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver"); +MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 738722cdeca..f0ea3b8b3e4 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -15,24 +15,13 @@ #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/interrupt.h> -#include <linux/spi/spi.h> #include <linux/mfd/core.h> #include <linux/mfd/mc13xxx.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> -struct mc13xxx { - struct spi_device *spidev; - struct mutex lock; - int irq; - int flags; - - irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; - void *irqdata[MC13XXX_NUM_IRQ]; - - int adcflags; -}; +#include "mc13xxx.h" #define MC13XXX_IRQSTAT0 0 #define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0) @@ -139,34 +128,29 @@ struct mc13xxx { #define MC13XXX_ADC2 45 -#define MC13XXX_NUMREGS 0x3f - void mc13xxx_lock(struct mc13xxx *mc13xxx) { if (!mutex_trylock(&mc13xxx->lock)) { - dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n", + dev_dbg(mc13xxx->dev, "wait for %s from %pf\n", __func__, __builtin_return_address(0)); mutex_lock(&mc13xxx->lock); } - dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n", + dev_dbg(mc13xxx->dev, "%s from %pf\n", __func__, __builtin_return_address(0)); } EXPORT_SYMBOL(mc13xxx_lock); void mc13xxx_unlock(struct mc13xxx *mc13xxx) { - dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n", + dev_dbg(mc13xxx->dev, "%s from %pf\n", __func__, __builtin_return_address(0)); mutex_unlock(&mc13xxx->lock); } EXPORT_SYMBOL(mc13xxx_unlock); -#define MC13XXX_REGOFFSET_SHIFT 25 int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val) { - struct spi_transfer t; - struct spi_message m; int ret; BUG_ON(!mutex_is_locked(&mc13xxx->lock)); @@ -174,84 +158,35 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val) if (offset > MC13XXX_NUMREGS) return -EINVAL; - *val = offset << MC13XXX_REGOFFSET_SHIFT; - - memset(&t, 0, sizeof(t)); - - t.tx_buf = val; - t.rx_buf = val; - t.len = sizeof(u32); - - spi_message_init(&m); - spi_message_add_tail(&t, &m); - - ret = spi_sync(mc13xxx->spidev, &m); - - /* error in message.status implies error return from spi_sync */ - BUG_ON(!ret && m.status); + ret = regmap_read(mc13xxx->regmap, offset, val); + dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val); - if (ret) - return ret; - - *val &= 0xffffff; - - dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val); - - return 0; + return ret; } EXPORT_SYMBOL(mc13xxx_reg_read); int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val) { - u32 buf; - struct spi_transfer t; - struct spi_message m; - int ret; - BUG_ON(!mutex_is_locked(&mc13xxx->lock)); - dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val); + dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val); if (offset > MC13XXX_NUMREGS || val > 0xffffff) return -EINVAL; - buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val; - - memset(&t, 0, sizeof(t)); - - t.tx_buf = &buf; - t.rx_buf = &buf; - t.len = sizeof(u32); - - spi_message_init(&m); - spi_message_add_tail(&t, &m); - - ret = spi_sync(mc13xxx->spidev, &m); - - BUG_ON(!ret && m.status); - - if (ret) - return ret; - - return 0; + return regmap_write(mc13xxx->regmap, offset, val); } EXPORT_SYMBOL(mc13xxx_reg_write); int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset, u32 mask, u32 val) { - int ret; - u32 valread; - + BUG_ON(!mutex_is_locked(&mc13xxx->lock)); BUG_ON(val & ~mask); + dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n", + offset, val, mask); - ret = mc13xxx_reg_read(mc13xxx, offset, &valread); - if (ret) - return ret; - - valread = (valread & ~mask) | val; - - return mc13xxx_reg_write(mc13xxx, offset, valread); + return regmap_update_bits(mc13xxx->regmap, offset, mask, val); } EXPORT_SYMBOL(mc13xxx_reg_rmw); @@ -439,7 +374,7 @@ static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx, if (handled == IRQ_HANDLED) num_handled++; } else { - dev_err(&mc13xxx->spidev->dev, + dev_err(mc13xxx->dev, "BUG: irq %u but no handler\n", baseirq + irq); @@ -475,25 +410,23 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data) return IRQ_RETVAL(handled); } -enum mc13xxx_id { - MC13XXX_ID_MC13783, - MC13XXX_ID_MC13892, - MC13XXX_ID_INVALID, -}; - 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, enum mc13xxx_id *id) +static int mc13xxx_identify(struct mc13xxx *mc13xxx) { u32 icid; u32 revision; - const char *name; 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; @@ -502,26 +435,23 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id) switch (icid) { case 2: - *id = MC13XXX_ID_MC13783; - name = "mc13783"; + mc13xxx->ictype = MC13XXX_ID_MC13783; break; case 7: - *id = MC13XXX_ID_MC13892; - name = "mc13892"; + mc13xxx->ictype = MC13XXX_ID_MC13892; break; default: - *id = MC13XXX_ID_INVALID; + mc13xxx->ictype = MC13XXX_ID_INVALID; break; } - if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) { + if (mc13xxx->ictype == MC13XXX_ID_MC13783 || + mc13xxx->ictype == MC13XXX_ID_MC13892) { ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); - if (ret) - return ret; - dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, " + dev_info(mc13xxx->dev, "%s: rev: %d.%d, " "fin: %d, fab: %d, icid: %d/%d\n", - mc13xxx_chipname[*id], + mc13xxx_chipname[mc13xxx->ictype], maskval(revision, MC13XXX_REVISION_REVFULL), maskval(revision, MC13XXX_REVISION_REVMETAL), maskval(revision, MC13XXX_REVISION_FIN), @@ -530,26 +460,12 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id) maskval(revision, MC13XXX_REVISION_ICIDCODE)); } - if (*id != MC13XXX_ID_INVALID) { - const struct spi_device_id *devid = - spi_get_device_id(mc13xxx->spidev); - if (!devid || devid->driver_data != *id) - dev_warn(&mc13xxx->spidev->dev, "device id doesn't " - "match auto detection!\n"); - } - - return 0; + return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0; } static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx) { - const struct spi_device_id *devid = - spi_get_device_id(mc13xxx->spidev); - - if (!devid) - return NULL; - - return mc13xxx_chipname[devid->driver_data]; + return mc13xxx_chipname[mc13xxx->ictype]; } int mc13xxx_get_flags(struct mc13xxx *mc13xxx) @@ -592,7 +508,7 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, }; init_completion(&adcdone_data.done); - dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__); + dev_dbg(mc13xxx->dev, "%s\n", __func__); mc13xxx_lock(mc13xxx); @@ -637,7 +553,8 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, adc1 |= ato << MC13783_ADC1_ATO_SHIFT; if (atox) adc1 |= MC13783_ADC1_ATOX; - dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__); + + dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__); mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, mc13xxx_handler_adcdone, __func__, &adcdone_data); mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE); @@ -695,7 +612,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, if (!cell.name) return -ENOMEM; - return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0); + return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0); } static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) @@ -706,7 +623,7 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) #ifdef CONFIG_OF static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx) { - struct device_node *np = mc13xxx->spidev->dev.of_node; + struct device_node *np = mc13xxx->dev->of_node; if (!np) return -ENODEV; @@ -732,55 +649,15 @@ static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx) } #endif -static const struct spi_device_id mc13xxx_device_id[] = { - { - .name = "mc13783", - .driver_data = MC13XXX_ID_MC13783, - }, { - .name = "mc13892", - .driver_data = MC13XXX_ID_MC13892, - }, { - /* sentinel */ - } -}; -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, }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); - -static int mc13xxx_probe(struct spi_device *spi) +int mc13xxx_common_init(struct mc13xxx *mc13xxx, + struct mc13xxx_platform_data *pdata, int irq) { - const struct of_device_id *of_id; - struct spi_driver *sdrv = to_spi_driver(spi->dev.driver); - struct mc13xxx *mc13xxx; - struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev); - enum mc13xxx_id id; int ret; - of_id = of_match_device(mc13xxx_dt_ids, &spi->dev); - if (of_id) - sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data]; - - mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); - if (!mc13xxx) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, mc13xxx); - spi->mode = SPI_MODE_0 | SPI_CS_HIGH; - spi->bits_per_word = 32; - spi_setup(spi); - - mc13xxx->spidev = spi; - - mutex_init(&mc13xxx->lock); mc13xxx_lock(mc13xxx); - ret = mc13xxx_identify(mc13xxx, &id); - if (ret || id == MC13XXX_ID_INVALID) + ret = mc13xxx_identify(mc13xxx); + if (ret) goto err_revision; /* mask all irqs */ @@ -792,18 +669,19 @@ static int mc13xxx_probe(struct spi_device *spi) if (ret) goto err_mask; - ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread, + ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); if (ret) { err_mask: err_revision: mc13xxx_unlock(mc13xxx); - dev_set_drvdata(&spi->dev, NULL); kfree(mc13xxx); return ret; } + mc13xxx->irq = irq; + mc13xxx_unlock(mc13xxx); if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata) @@ -838,42 +716,19 @@ err_revision: return 0; } +EXPORT_SYMBOL_GPL(mc13xxx_common_init); -static int __devexit mc13xxx_remove(struct spi_device *spi) +void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx) { - struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); + free_irq(mc13xxx->irq, mc13xxx); - free_irq(mc13xxx->spidev->irq, mc13xxx); + mfd_remove_devices(mc13xxx->dev); - mfd_remove_devices(&spi->dev); + regmap_exit(mc13xxx->regmap); kfree(mc13xxx); - - return 0; -} - -static struct spi_driver mc13xxx_driver = { - .id_table = mc13xxx_device_id, - .driver = { - .name = "mc13xxx", - .owner = THIS_MODULE, - .of_match_table = mc13xxx_dt_ids, - }, - .probe = mc13xxx_probe, - .remove = __devexit_p(mc13xxx_remove), -}; - -static int __init mc13xxx_init(void) -{ - return spi_register_driver(&mc13xxx_driver); -} -subsys_initcall(mc13xxx_init); - -static void __exit mc13xxx_exit(void) -{ - spi_unregister_driver(&mc13xxx_driver); } -module_exit(mc13xxx_exit); +EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup); MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC"); MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c new file mode 100644 index 00000000000..d22501dad6a --- /dev/null +++ b/drivers/mfd/mc13xxx-i2c.c @@ -0,0 +1,128 @@ +/* + * Copyright 2009-2010 Creative Product Design + * Marc Reilly marc@cpdesign.com.au + * + * 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/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/mc13xxx.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/i2c.h> +#include <linux/err.h> + +#include "mc13xxx.h" + +static const struct i2c_device_id mc13xxx_i2c_device_id[] = { + { + .name = "mc13892", + .driver_data = MC13XXX_ID_MC13892, + }, { + /* sentinel */ + } +}; +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], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); + +static struct regmap_config mc13xxx_regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 24, + + .max_register = MC13XXX_NUMREGS, + + .cache_type = REGCACHE_NONE, +}; + +static int mc13xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct of_device_id *of_id; + struct i2c_driver *idrv = to_i2c_driver(client->dev.driver); + struct mc13xxx *mc13xxx; + struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev); + int ret; + + of_id = of_match_device(mc13xxx_dt_ids, &client->dev); + if (of_id) + idrv->id_table = (const struct i2c_device_id*) of_id->data; + + mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); + if (!mc13xxx) + return -ENOMEM; + + dev_set_drvdata(&client->dev, mc13xxx); + + mc13xxx->dev = &client->dev; + mutex_init(&mc13xxx->lock); + + mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config); + if (IS_ERR(mc13xxx->regmap)) { + ret = PTR_ERR(mc13xxx->regmap); + dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", + ret); + dev_set_drvdata(&client->dev, NULL); + kfree(mc13xxx); + return ret; + } + + ret = mc13xxx_common_init(mc13xxx, pdata, client->irq); + + if (ret == 0 && (id->driver_data != mc13xxx->ictype)) + dev_warn(mc13xxx->dev, + "device id doesn't match auto detection!\n"); + + return ret; +} + +static int __devexit mc13xxx_i2c_remove(struct i2c_client *client) +{ + struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev); + + mc13xxx_common_cleanup(mc13xxx); + + return 0; +} + +static struct i2c_driver mc13xxx_i2c_driver = { + .id_table = mc13xxx_i2c_device_id, + .driver = { + .owner = THIS_MODULE, + .name = "mc13xxx", + .of_match_table = mc13xxx_dt_ids, + }, + .probe = mc13xxx_i2c_probe, + .remove = __devexit_p(mc13xxx_i2c_remove), +}; + +static int __init mc13xxx_i2c_init(void) +{ + return i2c_add_driver(&mc13xxx_i2c_driver); +} +subsys_initcall(mc13xxx_i2c_init); + +static void __exit mc13xxx_i2c_exit(void) +{ + i2c_del_driver(&mc13xxx_i2c_driver); +} +module_exit(mc13xxx_i2c_exit); + +MODULE_DESCRIPTION("i2c driver for Freescale MC13XXX PMIC"); +MODULE_AUTHOR("Marc Reilly <marc@cpdesign.com.au"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c new file mode 100644 index 00000000000..3fcdab3eb8e --- /dev/null +++ b/drivers/mfd/mc13xxx-spi.c @@ -0,0 +1,140 @@ +/* + * Copyright 2009-2010 Pengutronix + * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> + * + * loosely based on an earlier driver that has + * Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * + * 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/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/mc13xxx.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/err.h> +#include <linux/spi/spi.h> + +#include "mc13xxx.h" + +static const struct spi_device_id mc13xxx_device_id[] = { + { + .name = "mc13783", + .driver_data = MC13XXX_ID_MC13783, + }, { + .name = "mc13892", + .driver_data = MC13XXX_ID_MC13892, + }, { + /* sentinel */ + } +}; +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, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids); + +static struct regmap_config mc13xxx_regmap_spi_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 24, + + .max_register = MC13XXX_NUMREGS, + + .cache_type = REGCACHE_NONE, +}; + +static int mc13xxx_spi_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id; + struct spi_driver *sdrv = to_spi_driver(spi->dev.driver); + struct mc13xxx *mc13xxx; + struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev); + int ret; + + of_id = of_match_device(mc13xxx_dt_ids, &spi->dev); + if (of_id) + sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data]; + + mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); + if (!mc13xxx) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, mc13xxx); + spi->mode = SPI_MODE_0 | SPI_CS_HIGH; + spi->bits_per_word = 32; + + mc13xxx->dev = &spi->dev; + mutex_init(&mc13xxx->lock); + + mc13xxx->regmap = regmap_init_spi(spi, &mc13xxx_regmap_spi_config); + if (IS_ERR(mc13xxx->regmap)) { + ret = PTR_ERR(mc13xxx->regmap); + dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", + ret); + dev_set_drvdata(&spi->dev, NULL); + kfree(mc13xxx); + return ret; + } + + ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq); + + if (ret) { + dev_set_drvdata(&spi->dev, NULL); + } 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"); + } + + return ret; +} + +static int __devexit mc13xxx_spi_remove(struct spi_device *spi) +{ + struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); + + mc13xxx_common_cleanup(mc13xxx); + + return 0; +} + +static struct spi_driver mc13xxx_spi_driver = { + .id_table = mc13xxx_device_id, + .driver = { + .name = "mc13xxx", + .owner = THIS_MODULE, + .of_match_table = mc13xxx_dt_ids, + }, + .probe = mc13xxx_spi_probe, + .remove = __devexit_p(mc13xxx_spi_remove), +}; + +static int __init mc13xxx_init(void) +{ + return spi_register_driver(&mc13xxx_spi_driver); +} +subsys_initcall(mc13xxx_init); + +static void __exit mc13xxx_exit(void) +{ + spi_unregister_driver(&mc13xxx_spi_driver); +} +module_exit(mc13xxx_exit); + +MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC"); +MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mc13xxx.h b/drivers/mfd/mc13xxx.h new file mode 100644 index 00000000000..bbba06feea0 --- /dev/null +++ b/drivers/mfd/mc13xxx.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012 Creative Product Design + * Marc Reilly <marc@cpdesign.com.au> + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#ifndef __DRIVERS_MFD_MC13XXX_H +#define __DRIVERS_MFD_MC13XXX_H + +#include <linux/mutex.h> +#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 regmap *regmap; + + struct device *dev; + enum mc13xxx_id ictype; + + struct mutex lock; + int irq; + int flags; + + irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; + void *irqdata[MC13XXX_NUM_IRQ]; + + int adcflags; +}; + +int mc13xxx_common_init(struct mc13xxx *mc13xxx, + struct mc13xxx_platform_data *pdata, int irq); + +void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx); + +#endif /* __DRIVERS_MFD_MC13XXX_H */ diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 189c2f07b83..29c122bf28e 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -204,7 +204,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client, return -ENOENT; } - pcf = kzalloc(sizeof(*pcf), GFP_KERNEL); + pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL); if (!pcf) return -ENOMEM; @@ -212,12 +212,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client, mutex_init(&pcf->lock); - pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config); + pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config); if (IS_ERR(pcf->regmap)) { ret = PTR_ERR(pcf->regmap); - dev_err(pcf->dev, "Failed to allocate register map: %d\n", - ret); - goto err_free; + dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret); + return ret; } i2c_set_clientdata(client, pcf); @@ -228,7 +227,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client, if (version < 0 || variant < 0) { dev_err(pcf->dev, "Unable to probe pcf50633\n"); ret = -ENODEV; - goto err_regmap; + return ret; } dev_info(pcf->dev, "Probed device version %d variant %d\n", @@ -237,16 +236,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client, pcf50633_irq_init(pcf, client->irq); /* Create sub devices */ - pcf50633_client_dev_register(pcf, "pcf50633-input", - &pcf->input_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-rtc", - &pcf->rtc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-mbc", - &pcf->mbc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-adc", - &pcf->adc_pdev); - pcf50633_client_dev_register(pcf, "pcf50633-backlight", - &pcf->bl_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev); + pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev); for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { @@ -274,13 +268,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client, pdata->probe_done(pcf); return 0; - -err_regmap: - regmap_exit(pcf->regmap); -err_free: - kfree(pcf); - - return ret; } static int __devexit pcf50633_remove(struct i2c_client *client) @@ -300,9 +287,6 @@ static int __devexit pcf50633_remove(struct i2c_client *client) for (i = 0; i < PCF50633_NUM_REGULATORS; i++) platform_device_unregister(pcf->regulator_pdev[i]); - regmap_exit(pcf->regmap); - kfree(pcf); - return 0; } diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c index 44afae0a69c..cdc1df7fa0e 100644 --- a/drivers/mfd/rc5t583.c +++ b/drivers/mfd/rc5t583.c @@ -75,6 +75,7 @@ static struct deepsleep_control_data deepsleep_data[] = { (RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) static struct mfd_cell rc5t583_subdevs[] = { + {.name = "rc5t583-gpio",}, {.name = "rc5t583-regulator",}, {.name = "rc5t583-rtc", }, {.name = "rc5t583-key", } @@ -267,7 +268,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, rc5t583->dev = &i2c->dev; i2c_set_clientdata(i2c, rc5t583); - rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); + rc5t583->regmap = devm_regmap_init_i2c(i2c, &rc5t583_regmap_config); if (IS_ERR(rc5t583->regmap)) { ret = PTR_ERR(rc5t583->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); @@ -276,7 +277,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, ret = rc5t583_clear_ext_power_req(rc5t583, pdata); if (ret < 0) - goto err_irq_init; + return ret; if (i2c->irq) { ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); @@ -299,8 +300,6 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, err_add_devs: if (irq_init_success) rc5t583_irq_exit(rc5t583); -err_irq_init: - regmap_exit(rc5t583->regmap); return ret; } @@ -310,7 +309,6 @@ static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(rc5t583->dev); rc5t583_irq_exit(rc5t583); - regmap_exit(rc5t583->regmap); return 0; } diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 809bd4a6108..685d61e431a 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -108,18 +108,7 @@ static struct pci_driver rdc321x_sb_driver = { .remove = __devexit_p(rdc321x_sb_remove), }; -static int __init rdc321x_sb_init(void) -{ - return pci_register_driver(&rdc321x_sb_driver); -} - -static void __exit rdc321x_sb_exit(void) -{ - pci_unregister_driver(&rdc321x_sb_driver); -} - -module_init(rdc321x_sb_init); -module_exit(rdc321x_sb_exit); +module_pci_driver(rdc321x_sb_driver); MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index 48949d998d1..dd170307e60 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -114,12 +114,12 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c, s5m87xx->wakeup = pdata->wakeup; } - s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config); + s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config); if (IS_ERR(s5m87xx->regmap)) { ret = PTR_ERR(s5m87xx->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - goto err; + return ret; } s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); @@ -159,7 +159,6 @@ err: mfd_remove_devices(s5m87xx->dev); s5m_irq_exit(s5m87xx); i2c_unregister_device(s5m87xx->rtc); - regmap_exit(s5m87xx->regmap); return ret; } @@ -170,7 +169,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c) mfd_remove_devices(s5m87xx->dev); s5m_irq_exit(s5m87xx); i2c_unregister_device(s5m87xx->rtc); - regmap_exit(s5m87xx->regmap); return 0; } diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c new file mode 100644 index 00000000000..d31fed07aef --- /dev/null +++ b/drivers/mfd/sta2x11-mfd.c @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2009-2011 Wind River Systems, Inc. + * Copyright (c) 2011 ST Microelectronics (Alessandro Rubini) + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/list.h> +#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 <asm/sta2x11.h> + +/* This describes STA2X11 MFD chip for us, we may have several */ +struct sta2x11_mfd { + struct sta2x11_instance *instance; + spinlock_t lock; + struct list_head list; + void __iomem *sctl_regs; + void __iomem *apbreg_regs; +}; + +static LIST_HEAD(sta2x11_mfd_list); + +/* Three functions to act on the list */ +static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance; + struct sta2x11_mfd *mfd; + + if (!pdev && !list_empty(&sta2x11_mfd_list)) { + pr_warning("%s: Unspecified device, " + "using first instance\n", __func__); + return list_entry(sta2x11_mfd_list.next, + struct sta2x11_mfd, list); + } + + instance = sta2x11_get_instance(pdev); + if (!instance) + return NULL; + list_for_each_entry(mfd, &sta2x11_mfd_list, list) { + if (mfd->instance == instance) + return mfd; + } + return NULL; +} + +static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + struct sta2x11_instance *instance; + + if (mfd) + return -EBUSY; + instance = sta2x11_get_instance(pdev); + if (!instance) + return -EINVAL; + mfd = kzalloc(sizeof(*mfd), flags); + if (!mfd) + return -ENOMEM; + INIT_LIST_HEAD(&mfd->list); + spin_lock_init(&mfd->lock); + mfd->instance = instance; + list_add(&mfd->list, &sta2x11_mfd_list); + return 0; +} + +static int __devexit mfd_remove(struct pci_dev *pdev) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + + if (!mfd) + return -ENODEV; + list_del(&mfd->list); + kfree(mfd); + 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) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + u32 r; + unsigned long flags; + + if (!mfd) { + dev_warn(&pdev->dev, ": can't access sctl regs\n"); + return 0; + } + if (!mfd->sctl_regs) { + dev_warn(&pdev->dev, ": system ctl not initialized\n"); + return 0; + } + spin_lock_irqsave(&mfd->lock, flags); + r = readl(mfd->sctl_regs + reg); + r &= ~mask; + r |= val; + if (mask) + writel(r, mfd->sctl_regs + reg); + spin_unlock_irqrestore(&mfd->lock, flags); + return r; +} +EXPORT_SYMBOL(sta2x11_sctl_mask); + +u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val) +{ + struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); + u32 r; + unsigned long flags; + + 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; +} +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 + +static struct debugfs_regset32 sctl_regset = { + .regs = sta2x11_sctl_regs, + .nregs = ARRAY_SIZE(sta2x11_sctl_regs), +}; + +#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), +}; +#undef REG + +static struct debugfs_regset32 apbreg_regset = { + .regs = sta2x11_apbreg_regs, + .nregs = ARRAY_SIZE(sta2x11_apbreg_regs), +}; + +static struct dentry *sta2x11_sctl_debugfs; +static struct dentry *sta2x11_apbreg_debugfs; + +/* Probe for the two platform devices */ +static int sta2x11_sctl_probe(struct platform_device *dev) +{ + struct pci_dev **pdev; + struct sta2x11_mfd *mfd; + struct resource *res; + + pdev = dev->dev.platform_data; + mfd = sta2x11_mfd_find(*pdev); + if (!mfd) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + if (!request_mem_region(res->start, resource_size(res), + "sta2x11-sctl")) + return -EBUSY; + + mfd->sctl_regs = ioremap(res->start, resource_size(res)); + if (!mfd->sctl_regs) { + release_mem_region(res->start, resource_size(res)); + return -ENOMEM; + } + 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) +{ + struct pci_dev **pdev; + struct sta2x11_mfd *mfd; + struct resource *res; + + 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; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + if (!request_mem_region(res->start, resource_size(res), + "sta2x11-apbreg")) + return -EBUSY; + + mfd->apbreg_regs = ioremap(res->start, resource_size(res)); + if (!mfd->apbreg_regs) { + release_mem_region(res->start, resource_size(res)); + return -ENOMEM; + } + dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs); + + 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 struct platform_driver sta2x11_sctl_platform_driver = { + .driver = { + .name = "sta2x11-sctl", + .owner = THIS_MODULE, + }, + .probe = sta2x11_sctl_probe, +}; + +static int __init sta2x11_sctl_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_sctl_platform_driver); +} + +static struct platform_driver sta2x11_platform_driver = { + .driver = { + .name = "sta2x11-apbreg", + .owner = THIS_MODULE, + }, + .probe = sta2x11_apbreg_probe, +}; + +static int __init sta2x11_apbreg_init(void) +{ + pr_info("%s\n", __func__); + return platform_driver_register(&sta2x11_platform_driver); +} + +/* + * What follows is the PCI device that hosts the above two pdevs. + * Each logic block is 4kB and they are all consecutive: we use this info. + */ + +/* Bar 0 */ +enum bar0_cells { + STA2X11_GPIO_0 = 0, + STA2X11_GPIO_1, + STA2X11_GPIO_2, + STA2X11_GPIO_3, + STA2X11_SCTL, + STA2X11_SCR, + STA2X11_TIME, +}; +/* Bar 1 */ +enum bar1_cells { + STA2X11_APBREG = 0, +}; +#define CELL_4K(_name, _cell) { \ + .name = _name, \ + .start = _cell * 4096, .end = _cell * 4096 + 4095, \ + .flags = IORESOURCE_MEM, \ + } + +static const __devinitconst struct resource gpio_resources[] = { + { + .name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */ + .start = 0, + .end = (4 * 4096) - 1, + .flags = IORESOURCE_MEM, + } +}; +static const __devinitconst struct resource sctl_resources[] = { + CELL_4K("sta2x11-sctl", STA2X11_SCTL), +}; +static const __devinitconst struct resource scr_resources[] = { + CELL_4K("sta2x11-scr", STA2X11_SCR), +}; +static const __devinitconst struct resource time_resources[] = { + CELL_4K("sta2x11-time", STA2X11_TIME), +}; + +static const __devinitconst struct resource apbreg_resources[] = { + CELL_4K("sta2x11-apbreg", STA2X11_APBREG), +}; + +#define DEV(_name, _r) \ + { .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, } + +static __devinitdata 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 __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = { + DEV("sta2x11-apbreg", apbreg_resources), +}; + +static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int sta2x11_mfd_resume(struct pci_dev *pdev) +{ + int err; + + pci_set_power_state(pdev, 0); + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + + return 0; +} + +static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + int err, i; + struct sta2x11_gpio_pdata *gpio_data; + + dev_info(&pdev->dev, "%s\n", __func__); + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Can't enable device.\n"); + return err; + } + + err = pci_enable_msi(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); + + /* 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; + + /* 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); + 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); + if (err) { + dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err); + goto err_disable; + } + + return 0; + +err_disable: + mfd_remove_devices(&pdev->dev); + pci_disable_device(pdev); + pci_disable_msi(pdev); + return err; +} + +static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = { + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)}, + {0,}, +}; + +static struct pci_driver sta2x11_mfd_driver = { + .name = "sta2x11-mfd", + .id_table = sta2x11_mfd_tbl, + .probe = sta2x11_mfd_probe, + .suspend = sta2x11_mfd_suspend, + .resume = sta2x11_mfd_resume, +}; + +static int __init sta2x11_mfd_init(void) +{ + pr_info("%s\n", __func__); + return pci_register_driver(&sta2x11_mfd_driver); +} + +/* + * All of this must be ready before "normal" devices like MMCI appear. + * But MFD (the pci device) can't be too early. The following choice + * prepares platform drivers very early and probe the PCI device later, + * but before other PCI devices. + */ +subsys_initcall(sta2x11_apbreg_init); +subsys_initcall(sta2x11_sctl_init); +rootfs_initcall(sta2x11_mfd_init); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wind River"); +MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG"); +MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl); diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c index b58c43c7ea9..afd459013ec 100644 --- a/drivers/mfd/stmpe-spi.c +++ b/drivers/mfd/stmpe-spi.c @@ -122,7 +122,6 @@ MODULE_DEVICE_TABLE(spi, stmpe_id); static struct spi_driver stmpe_spi_driver = { .driver = { .name = "stmpe-spi", - .bus = &spi_bus_type, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &stmpe_dev_pm_ops, diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 47f802bf184..396b9d1b6bd 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -283,27 +283,24 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, } } - tps65090->rmap = regmap_init_i2c(tps65090->client, - &tps65090_regmap_config); + tps65090->rmap = devm_regmap_init_i2c(tps65090->client, + &tps65090_regmap_config); if (IS_ERR(tps65090->rmap)) { - dev_err(&client->dev, "regmap_init failed with err: %ld\n", - PTR_ERR(tps65090->rmap)); + ret = PTR_ERR(tps65090->rmap); + dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); goto err_irq_exit; - }; + } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, ARRAY_SIZE(tps65090s), NULL, 0); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); - goto err_regmap_exit; + goto err_irq_exit; } return 0; -err_regmap_exit: - regmap_exit(tps65090->rmap); - err_irq_exit: if (client->irq) free_irq(client->irq, tps65090); @@ -316,29 +313,34 @@ static int __devexit tps65090_i2c_remove(struct i2c_client *client) struct tps65090 *tps65090 = i2c_get_clientdata(client); mfd_remove_devices(tps65090->dev); - regmap_exit(tps65090->rmap); if (client->irq) free_irq(client->irq, tps65090); return 0; } -#ifdef CONFIG_PM -static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tps65090_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); if (client->irq) disable_irq(client->irq); return 0; } -static int tps65090_i2c_resume(struct i2c_client *client) +static int tps65090_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); if (client->irq) enable_irq(client->irq); return 0; } #endif +static const struct dev_pm_ops tps65090_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume) +}; + static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, { }, @@ -349,13 +351,10 @@ static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", .owner = THIS_MODULE, + .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, .remove = __devexit_p(tps65090_i2c_remove), -#ifdef CONFIG_PM - .suspend = tps65090_i2c_suspend, - .resume = tps65090_i2c_resume, -#endif .id_table = tps65090_id_table, }; diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index f7d854e4cc6..db194e433c0 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -96,7 +96,7 @@ EXPORT_SYMBOL_GPL(tps65217_reg_write); * @val: Value to write. * @level: Password protected level */ -int tps65217_update_bits(struct tps65217 *tps, unsigned int reg, +static int tps65217_update_bits(struct tps65217 *tps, unsigned int reg, unsigned int mask, unsigned int val, unsigned int level) { int ret; @@ -150,7 +150,7 @@ static int __devinit tps65217_probe(struct i2c_client *client, return -ENOMEM; tps->pdata = pdata; - tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config); + tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config); if (IS_ERR(tps->regmap)) { ret = PTR_ERR(tps->regmap); dev_err(tps->dev, "Failed to allocate register map: %d\n", @@ -163,9 +163,9 @@ static int __devinit tps65217_probe(struct i2c_client *client, ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version); if (ret < 0) { - dev_err(tps->dev, "Failed to read revision" - " register: %d\n", ret); - goto err_regmap; + dev_err(tps->dev, "Failed to read revision register: %d\n", + ret); + return ret; } dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", @@ -190,11 +190,6 @@ static int __devinit tps65217_probe(struct i2c_client *client, } return 0; - -err_regmap: - regmap_exit(tps->regmap); - - return ret; } static int __devexit tps65217_remove(struct i2c_client *client) @@ -205,8 +200,6 @@ static int __devexit tps65217_remove(struct i2c_client *client) for (i = 0; i < TPS65217_NUM_REGULATOR; i++) platform_device_unregister(tps->regulator_pdev[i]); - regmap_exit(tps->regmap); - return 0; } diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index c9ed5c00a62..09aab3e4776 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -20,15 +20,10 @@ #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> -static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, - int irq) -{ - return (irq - tps65910->irq_base); -} - /* * 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 @@ -41,28 +36,28 @@ static inline int irq_to_tps65910_irq(struct tps65910 *tps65910, static irqreturn_t tps65910_irq(int irq, void *irq_data) { struct tps65910 *tps65910 = irq_data; + unsigned int reg; u32 irq_sts; u32 irq_mask; - u8 reg; int i; - tps65910->read(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS, ®); irq_sts = reg; - tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®); irq_sts |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®); irq_sts |= reg << 16; } - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); irq_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); irq_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); irq_mask |= reg << 16; } @@ -76,19 +71,19 @@ static irqreturn_t tps65910_irq(int irq, void *irq_data) if (!(irq_sts & (1 << i))) continue; - handle_nested_irq(tps65910->irq_base + i); + 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->write(tps65910, TPS65910_INT_STS, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS, reg); reg = irq_sts & 0xFF; - tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = irq_sts >> 8; - tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg); } return IRQ_HANDLED; @@ -105,27 +100,27 @@ static void tps65910_irq_sync_unlock(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); u32 reg_mask; - u8 reg; + unsigned int reg; - tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®); reg_mask = reg; - tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®); reg_mask |= reg << 8; switch (tps65910_chip_id(tps65910)) { case TPS65911: - tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®); reg_mask |= reg << 16; } if (tps65910->irq_mask != reg_mask) { reg = tps65910->irq_mask & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg); reg = tps65910->irq_mask >> 8 & 0xFF; - tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg); switch (tps65910_chip_id(tps65910)) { case TPS65911: reg = tps65910->irq_mask >> 16; - tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®); + tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg); } } mutex_unlock(&tps65910->irq_lock); @@ -135,14 +130,14 @@ static void tps65910_irq_enable(struct irq_data *data) { struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); - tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq)); + 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 << irq_to_tps65910_irq(tps65910, data->irq)); + tps65910->irq_mask |= (1 << data->hwirq); } #ifdef CONFIG_PM_SLEEP @@ -164,10 +159,35 @@ static struct irq_chip tps65910_irq_chip = { .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, cur_irq; + int ret; int flags = IRQF_ONESHOT; if (!irq) { @@ -175,17 +195,11 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq, return -EINVAL; } - if (!pdata || !pdata->irq_base) { - dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n"); + if (!pdata) { + dev_warn(tps65910->dev, "No interrupt support, no pdata\n"); return -EINVAL; } - tps65910->irq_mask = 0xFFFFFF; - - mutex_init(&tps65910->irq_lock); - tps65910->chip_irq = irq; - tps65910->irq_base = pdata->irq_base; - switch (tps65910_chip_id(tps65910)) { case TPS65910: tps65910->irq_num = TPS65910_NUM_IRQ; @@ -195,22 +209,36 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq, break; } - /* Register with genirq */ - for (cur_irq = tps65910->irq_base; - cur_irq < tps65910->irq_num + tps65910->irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, tps65910); - irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 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(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + 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, diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index bf2b25ebf2c..be9e07b7732 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -19,13 +19,16 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/gpio.h> #include <linux/mfd/core.h> #include <linux/regmap.h> #include <linux/mfd/tps65910.h> +#include <linux/of_device.h> static struct mfd_cell tps65910s[] = { { + .name = "tps65910-gpio", + }, + { .name = "tps65910-pmic", }, { @@ -37,30 +40,6 @@ static struct mfd_cell tps65910s[] = { }; -static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg, - int bytes, void *dest) -{ - return regmap_bulk_read(tps65910->regmap, reg, dest, bytes); -} - -static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, - int bytes, void *src) -{ - return regmap_bulk_write(tps65910->regmap, reg, src, bytes); -} - -int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, mask); -} -EXPORT_SYMBOL_GPL(tps65910_set_bits); - -int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask) -{ - return regmap_update_bits(tps65910->regmap, reg, mask, 0); -} -EXPORT_SYMBOL_GPL(tps65910_clear_bits); - static bool is_volatile_reg(struct device *dev, unsigned int reg) { struct tps65910 *tps65910 = dev_get_drvdata(dev); @@ -85,80 +64,197 @@ static const struct regmap_config tps65910_regmap_config = { .reg_bits = 8, .val_bits = 8, .volatile_reg = is_volatile_reg, - .max_register = TPS65910_MAX_REGISTER, - .num_reg_defaults_raw = TPS65910_MAX_REGISTER, + .max_register = TPS65910_MAX_REGISTER - 1, .cache_type = REGCACHE_RBTREE, }; -static int tps65910_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int __devinit tps65910_sleepinit(struct tps65910 *tps65910, + struct tps65910_board *pmic_pdata) +{ + struct device *dev = NULL; + int ret = 0; + + dev = tps65910->dev; + + if (!pmic_pdata->en_dev_slp) + return 0; + + /* enabling SLEEP device state */ + ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + if (ret < 0) { + dev_err(dev, "set dev_slp failed: %d\n", ret); + goto err_sleep_init; + } + + /* Return if there is no sleep keepon data. */ + if (!pmic_pdata->slp_keepon) + return 0; + + if (pmic_pdata->slp_keepon->therm_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set therm_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->clkout32k_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set clkout32k_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + if (pmic_pdata->slp_keepon->i2chs_keepon) { + ret = tps65910_reg_set_bits(tps65910, + TPS65910_SLEEP_KEEP_RES_ON, + SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK); + if (ret < 0) { + dev_err(dev, "set i2chs_keepon failed: %d\n", ret); + goto disable_dev_slp; + } + } + + return 0; + +disable_dev_slp: + tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, + DEVCTRL_DEV_SLP_MASK); + +err_sleep_init: + return ret; +} + +#ifdef CONFIG_OF +static struct of_device_id tps65910_of_match[] = { + { .compatible = "ti,tps65910", .data = (void *)TPS65910}, + { .compatible = "ti,tps65911", .data = (void *)TPS65911}, + { }, +}; +MODULE_DEVICE_TABLE(of, tps65910_of_match); + +static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + struct device_node *np = client->dev.of_node; + struct tps65910_board *board_info; + unsigned int prop; + const struct of_device_id *match; + int ret = 0; + + match = of_match_device(tps65910_of_match, &client->dev); + if (!match) { + dev_err(&client->dev, "Failed to find matching dt id\n"); + return NULL; + } + + *chip_id = (int)match->data; + + board_info = devm_kzalloc(&client->dev, sizeof(*board_info), + GFP_KERNEL); + if (!board_info) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return NULL; + } + + ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop); + if (!ret) + board_info->vmbch_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH-Threshold not specified"); + + ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop); + if (!ret) + board_info->vmbch2_threshold = prop; + else if (*chip_id == TPS65911) + dev_warn(&client->dev, "VMBCH2-Threshold not specified"); + + board_info->irq = client->irq; + board_info->irq_base = -1; + + return board_info; +} +#else +static inline +struct tps65910_board *tps65910_parse_dt(struct i2c_client *client, + int *chip_id) +{ + return NULL; +} +#endif + +static __devinit int tps65910_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct tps65910 *tps65910; struct tps65910_board *pmic_plat_data; + struct tps65910_board *of_pmic_plat_data = NULL; struct tps65910_platform_data *init_data; int ret = 0; + int chip_id = id->driver_data; pmic_plat_data = dev_get_platdata(&i2c->dev); + + if (!pmic_plat_data && i2c->dev.of_node) { + pmic_plat_data = tps65910_parse_dt(i2c, &chip_id); + of_pmic_plat_data = pmic_plat_data; + } + if (!pmic_plat_data) return -EINVAL; - init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL); + init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL); if (init_data == NULL) return -ENOMEM; - tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL); - if (tps65910 == NULL) { - kfree(init_data); + tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL); + if (tps65910 == NULL) return -ENOMEM; - } + tps65910->of_plat_data = of_pmic_plat_data; i2c_set_clientdata(i2c, tps65910); tps65910->dev = &i2c->dev; tps65910->i2c_client = i2c; - tps65910->id = id->driver_data; - tps65910->read = tps65910_i2c_read; - tps65910->write = tps65910_i2c_write; + tps65910->id = chip_id; mutex_init(&tps65910->io_mutex); - tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config); + tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config); if (IS_ERR(tps65910->regmap)) { ret = PTR_ERR(tps65910->regmap); dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); - goto regmap_err; + return ret; } ret = mfd_add_devices(tps65910->dev, -1, tps65910s, ARRAY_SIZE(tps65910s), NULL, 0); - if (ret < 0) - goto err; + 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; - tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base); - tps65910_irq_init(tps65910, init_data->irq, init_data); - kfree(init_data); - return ret; + tps65910_sleepinit(tps65910, pmic_plat_data); -err: - regmap_exit(tps65910->regmap); -regmap_err: - kfree(tps65910); - kfree(init_data); return ret; } -static int tps65910_i2c_remove(struct i2c_client *i2c) +static __devexit int tps65910_i2c_remove(struct i2c_client *i2c) { struct tps65910 *tps65910 = i2c_get_clientdata(i2c); tps65910_irq_exit(tps65910); mfd_remove_devices(tps65910->dev); - regmap_exit(tps65910->regmap); - kfree(tps65910); return 0; } @@ -175,9 +271,10 @@ static struct i2c_driver tps65910_i2c_driver = { .driver = { .name = "tps65910", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65910_of_match), }, .probe = tps65910_i2c_probe, - .remove = tps65910_i2c_remove, + .remove = __devexit_p(tps65910_i2c_remove), .id_table = tps65910_i2c_id, }; diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 5d656e81435..ad733d76207 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -757,6 +757,7 @@ int twl4030_init_irq(struct device *dev, int irq_num) dev_err(dev, "could not claim irq%d: %d\n", irq_num, status); goto fail_rqirq; } + enable_irq_wake(irq_num); return irq_base; fail_rqirq: diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 2d6bedadca0..4ded9e7aa24 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -27,7 +27,12 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/err.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> #include <linux/gpio.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -35,8 +40,24 @@ #include <linux/err.h> #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h> +#include <linux/regulator/consumer.h> #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) +#define TWL6040_NUM_SUPPLIES (2) + +static bool twl6040_has_vibra(struct twl6040_platform_data *pdata, + struct device_node *node) +{ + if (pdata && pdata->vibra) + return true; + +#ifdef CONFIG_OF + if (of_find_node_by_name(node, "vibra")) + return true; +#endif + + return false; +} int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) { @@ -502,17 +523,18 @@ static int __devinit twl6040_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct twl6040_platform_data *pdata = client->dev.platform_data; + struct device_node *node = client->dev.of_node; struct twl6040 *twl6040; struct mfd_cell *cell = NULL; - int ret, children = 0; + int irq, ret, children = 0; - if (!pdata) { + if (!pdata && !node) { dev_err(&client->dev, "Platform data is missing\n"); return -EINVAL; } /* In order to operate correctly we need valid interrupt config */ - if (!client->irq || !pdata->irq_base) { + if (!client->irq) { dev_err(&client->dev, "Invalid IRQ configuration\n"); return -EINVAL; } @@ -524,7 +546,7 @@ static int __devinit twl6040_probe(struct i2c_client *client, goto err; } - twl6040->regmap = regmap_init_i2c(client, &twl6040_regmap_config); + twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config); if (IS_ERR(twl6040->regmap)) { ret = PTR_ERR(twl6040->regmap); goto err; @@ -532,9 +554,23 @@ static int __devinit twl6040_probe(struct i2c_client *client, i2c_set_clientdata(client, twl6040); + twl6040->supplies[0].supply = "vio"; + twl6040->supplies[1].supply = "v2v1"; + ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, + twl6040->supplies); + if (ret != 0) { + dev_err(&client->dev, "Failed to get supplies: %d\n", ret); + goto regulator_get_err; + } + + ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies); + if (ret != 0) { + dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); + goto power_err; + } + twl6040->dev = &client->dev; twl6040->irq = client->irq; - twl6040->irq_base = pdata->irq_base; mutex_init(&twl6040->mutex); mutex_init(&twl6040->io_mutex); @@ -543,22 +579,26 @@ static int __devinit twl6040_probe(struct i2c_client *client, twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) - twl6040->audpwron = pdata->audpwron_gpio; - else + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) { + if (pdata) + twl6040->audpwron = pdata->audpwron_gpio; + else + twl6040->audpwron = of_get_named_gpio(node, + "ti,audpwron-gpio", 0); + } else twl6040->audpwron = -EINVAL; if (gpio_is_valid(twl6040->audpwron)) { ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW, "audpwron"); if (ret) - goto gpio1_err; + goto gpio_err; } /* codec interrupt */ ret = twl6040_irq_init(twl6040); if (ret) - goto gpio2_err; + goto irq_init_err; ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, NULL, twl6040_naudint_handler, 0, @@ -572,22 +612,27 @@ static int __devinit twl6040_probe(struct i2c_client *client, /* dual-access registers controlled by I2C only */ twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); - if (pdata->codec) { - int irq = twl6040->irq_base + TWL6040_IRQ_PLUG; - - cell = &twl6040->cells[children]; - cell->name = "twl6040-codec"; - twl6040_codec_rsrc[0].start = irq; - twl6040_codec_rsrc[0].end = irq; - cell->resources = twl6040_codec_rsrc; - cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); + /* + * The main functionality of twl6040 to provide audio on OMAP4+ systems. + * We can add the ASoC codec child whenever this driver has been loaded. + * The ASoC codec can work without pdata, pass the platform_data only if + * it has been provided. + */ + irq = twl6040->irq_base + TWL6040_IRQ_PLUG; + cell = &twl6040->cells[children]; + cell->name = "twl6040-codec"; + twl6040_codec_rsrc[0].start = irq; + twl6040_codec_rsrc[0].end = irq; + cell->resources = twl6040_codec_rsrc; + cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); + if (pdata && pdata->codec) { cell->platform_data = pdata->codec; cell->pdata_size = sizeof(*pdata->codec); - children++; } + children++; - if (pdata->vibra) { - int irq = twl6040->irq_base + TWL6040_IRQ_VIB; + if (twl6040_has_vibra(pdata, node)) { + irq = twl6040->irq_base + TWL6040_IRQ_VIB; cell = &twl6040->cells[children]; cell->name = "twl6040-vibra"; @@ -596,21 +641,17 @@ static int __devinit twl6040_probe(struct i2c_client *client, cell->resources = twl6040_vibra_rsrc; cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); - cell->platform_data = pdata->vibra; - cell->pdata_size = sizeof(*pdata->vibra); + if (pdata && pdata->vibra) { + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); + } children++; } - if (children) { - ret = mfd_add_devices(&client->dev, -1, twl6040->cells, - children, NULL, 0); - if (ret) - goto mfd_err; - } else { - dev_err(&client->dev, "No platform data found for children\n"); - ret = -ENODEV; + ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, + NULL, 0); + if (ret) goto mfd_err; - } return 0; @@ -618,12 +659,15 @@ mfd_err: free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); irq_err: twl6040_irq_exit(twl6040); -gpio2_err: +irq_init_err: if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); -gpio1_err: +gpio_err: + regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); +power_err: + regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies); +regulator_get_err: i2c_set_clientdata(client, NULL); - regmap_exit(twl6040->regmap); err: return ret; } @@ -643,7 +687,9 @@ static int __devexit twl6040_remove(struct i2c_client *client) mfd_remove_devices(&client->dev); i2c_set_clientdata(client, NULL); - regmap_exit(twl6040->regmap); + + regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); + regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies); return 0; } diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c index b3f8ddaa28a..4b42543da22 100644 --- a/drivers/mfd/twl6040-irq.c +++ b/drivers/mfd/twl6040-irq.c @@ -23,7 +23,10 @@ #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> @@ -138,7 +141,8 @@ static irqreturn_t twl6040_irq_thread(int irq, void *data) int twl6040_irq_init(struct twl6040 *twl6040) { - int cur_irq, ret; + struct device_node *node = twl6040->dev->of_node; + int i, nr_irqs, irq_base, ret; u8 val; mutex_init(&twl6040->irq_mutex); @@ -148,21 +152,31 @@ int twl6040_irq_init(struct twl6040 *twl6040) 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 (cur_irq = twl6040->irq_base; - cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs); - cur_irq++) { - irq_set_chip_data(cur_irq, twl6040); - irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip, + 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(cur_irq, 1); + 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(cur_irq, IRQF_VALID); + set_irq_flags(i, IRQF_VALID); #else - irq_set_noprobe(cur_irq); + irq_set_noprobe(i); #endif } diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c index b73cc15e008..872aff21e4b 100644 --- a/drivers/mfd/vx855.c +++ b/drivers/mfd/vx855.c @@ -131,17 +131,7 @@ static struct pci_driver vx855_pci_driver = { .remove = __devexit_p(vx855_remove), }; -static int vx855_init(void) -{ - return pci_register_driver(&vx855_pci_driver); -} -module_init(vx855_init); - -static void vx855_exit(void) -{ - pci_unregister_driver(&vx855_pci_driver); -} -module_exit(vx855_exit); +module_pci_driver(vx855_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>"); diff --git a/drivers/mfd/wm831x-auxadc.c b/drivers/mfd/wm831x-auxadc.c index 87210954a06..6ee3018d865 100644 --- a/drivers/mfd/wm831x-auxadc.c +++ b/drivers/mfd/wm831x-auxadc.c @@ -280,11 +280,11 @@ void wm831x_auxadc_init(struct wm831x *wm831x) mutex_init(&wm831x->auxadc_lock); INIT_LIST_HEAD(&wm831x->auxadc_pending); - if (wm831x->irq && wm831x->irq_base) { + if (wm831x->irq) { wm831x->auxadc_read = wm831x_auxadc_read_irq; - ret = request_threaded_irq(wm831x->irq_base + - WM831X_IRQ_AUXADC_DATA, + ret = request_threaded_irq(wm831x_irq(wm831x, + WM831X_IRQ_AUXADC_DATA), NULL, wm831x_auxadc_irq, 0, "auxadc", wm831x); if (ret < 0) { diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 838056c3493..946698fd2dc 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -614,8 +614,15 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg, } EXPORT_SYMBOL_GPL(wm831x_set_bits); +static struct resource wm831x_io_parent = { + .start = 0, + .end = 0xffffffff, + .flags = IORESOURCE_IO, +}; + static struct resource wm831x_dcdc1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC1_CONTROL_1, .end = WM831X_DC1_DVS_CONTROL, .flags = IORESOURCE_IO, @@ -637,6 +644,7 @@ static struct resource wm831x_dcdc1_resources[] = { static struct resource wm831x_dcdc2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC2_CONTROL_1, .end = WM831X_DC2_DVS_CONTROL, .flags = IORESOURCE_IO, @@ -657,6 +665,7 @@ static struct resource wm831x_dcdc2_resources[] = { static struct resource wm831x_dcdc3_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC3_CONTROL_1, .end = WM831X_DC3_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -671,6 +680,7 @@ static struct resource wm831x_dcdc3_resources[] = { static struct resource wm831x_dcdc4_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC4_CONTROL, .end = WM831X_DC4_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -685,6 +695,7 @@ static struct resource wm831x_dcdc4_resources[] = { static struct resource wm8320_dcdc4_buck_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_DC4_CONTROL, .end = WM832X_DC4_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -707,6 +718,7 @@ static struct resource wm831x_gpio_resources[] = { static struct resource wm831x_isink1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_CURRENT_SINK_1, .end = WM831X_CURRENT_SINK_1, .flags = IORESOURCE_IO, @@ -720,6 +732,7 @@ static struct resource wm831x_isink1_resources[] = { static struct resource wm831x_isink2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_CURRENT_SINK_2, .end = WM831X_CURRENT_SINK_2, .flags = IORESOURCE_IO, @@ -733,6 +746,7 @@ static struct resource wm831x_isink2_resources[] = { static struct resource wm831x_ldo1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO1_CONTROL, .end = WM831X_LDO1_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -747,6 +761,7 @@ static struct resource wm831x_ldo1_resources[] = { static struct resource wm831x_ldo2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO2_CONTROL, .end = WM831X_LDO2_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -761,6 +776,7 @@ static struct resource wm831x_ldo2_resources[] = { static struct resource wm831x_ldo3_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO3_CONTROL, .end = WM831X_LDO3_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -775,6 +791,7 @@ static struct resource wm831x_ldo3_resources[] = { static struct resource wm831x_ldo4_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO4_CONTROL, .end = WM831X_LDO4_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -789,6 +806,7 @@ static struct resource wm831x_ldo4_resources[] = { static struct resource wm831x_ldo5_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO5_CONTROL, .end = WM831X_LDO5_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -803,6 +821,7 @@ static struct resource wm831x_ldo5_resources[] = { static struct resource wm831x_ldo6_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO6_CONTROL, .end = WM831X_LDO6_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -817,6 +836,7 @@ static struct resource wm831x_ldo6_resources[] = { static struct resource wm831x_ldo7_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO7_CONTROL, .end = WM831X_LDO7_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -831,6 +851,7 @@ static struct resource wm831x_ldo7_resources[] = { static struct resource wm831x_ldo8_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO8_CONTROL, .end = WM831X_LDO8_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -845,6 +866,7 @@ static struct resource wm831x_ldo8_resources[] = { static struct resource wm831x_ldo9_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO9_CONTROL, .end = WM831X_LDO9_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -859,6 +881,7 @@ static struct resource wm831x_ldo9_resources[] = { static struct resource wm831x_ldo10_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO10_CONTROL, .end = WM831X_LDO10_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -873,6 +896,7 @@ static struct resource wm831x_ldo10_resources[] = { static struct resource wm831x_ldo11_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_LDO11_ON_CONTROL, .end = WM831X_LDO11_SLEEP_CONTROL, .flags = IORESOURCE_IO, @@ -974,6 +998,7 @@ static struct resource wm831x_rtc_resources[] = { static struct resource wm831x_status1_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_STATUS_LED_1, .end = WM831X_STATUS_LED_1, .flags = IORESOURCE_IO, @@ -982,6 +1007,7 @@ static struct resource wm831x_status1_resources[] = { static struct resource wm831x_status2_resources[] = { { + .parent = &wm831x_io_parent, .start = WM831X_STATUS_LED_2, .end = WM831X_STATUS_LED_2, .flags = IORESOURCE_IO, @@ -1787,27 +1813,27 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8310: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8310_devs, ARRAY_SIZE(wm8310_devs), - NULL, wm831x->irq_base); + NULL, 0); break; case WM8311: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8311_devs, ARRAY_SIZE(wm8311_devs), - NULL, wm831x->irq_base); + NULL, 0); if (!pdata || !pdata->disable_touch) mfd_add_devices(wm831x->dev, wm831x_num, touch_devs, ARRAY_SIZE(touch_devs), - NULL, wm831x->irq_base); + NULL, 0); break; case WM8312: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8312_devs, ARRAY_SIZE(wm8312_devs), - NULL, wm831x->irq_base); + NULL, 0); if (!pdata || !pdata->disable_touch) mfd_add_devices(wm831x->dev, wm831x_num, touch_devs, ARRAY_SIZE(touch_devs), - NULL, wm831x->irq_base); + NULL, 0); break; case WM8320: @@ -1816,7 +1842,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) case WM8326: ret = mfd_add_devices(wm831x->dev, wm831x_num, wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, wm831x->irq_base); + NULL, 0); break; default: @@ -1841,7 +1867,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) if (ret & WM831X_XTAL_ENA) { ret = mfd_add_devices(wm831x->dev, wm831x_num, rtc_devs, ARRAY_SIZE(rtc_devs), - NULL, wm831x->irq_base); + NULL, 0); if (ret != 0) { dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret); goto err_irq; @@ -1854,7 +1880,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) /* Treat errors as non-critical */ ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs, ARRAY_SIZE(backlight_devs), NULL, - wm831x->irq_base); + 0); if (ret < 0) dev_err(wm831x->dev, "Failed to add backlight: %d\n", ret); @@ -1883,8 +1909,7 @@ void wm831x_device_exit(struct wm831x *wm831x) { wm831x_otp_exit(wm831x); mfd_remove_devices(wm831x->dev); - if (wm831x->irq_base) - free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x); + free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x); wm831x_irq_exit(wm831x); } diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index bec4d053916..804e56ec99e 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -18,6 +18,7 @@ #include <linux/irq.h> #include <linux/mfd/core.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/pdata.h> @@ -328,7 +329,7 @@ static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data) static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x, int irq) { - return &wm831x_irqs[irq - wm831x->irq_base]; + return &wm831x_irqs[irq]; } static void wm831x_irq_lock(struct irq_data *data) @@ -374,7 +375,7 @@ static void wm831x_irq_enable(struct irq_data *data) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, - data->irq); + data->hwirq); wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } @@ -383,7 +384,7 @@ static void wm831x_irq_disable(struct irq_data *data) { struct wm831x *wm831x = irq_data_get_irq_chip_data(data); struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, - data->irq); + data->hwirq); wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; } @@ -393,7 +394,7 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) struct wm831x *wm831x = irq_data_get_irq_chip_data(data); int irq; - irq = data->irq - wm831x->irq_base; + irq = data->hwirq; if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) { /* Ignore internal-only IRQs */ @@ -412,22 +413,25 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) * do the update here as we can be called with the bus lock * held. */ + wm831x->gpio_level_low[irq] = false; + wm831x->gpio_level_high[irq] = false; switch (type) { case IRQ_TYPE_EDGE_BOTH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; - wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_RISING: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; - wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_FALLING: wm831x->gpio_update[irq] = 0x10000; - wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_LEVEL_HIGH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; - wm831x->gpio_level[irq] = true; + wm831x->gpio_level_high[irq] = true; + break; + case IRQ_TYPE_LEVEL_LOW: + wm831x->gpio_update[irq] = 0x10000; + wm831x->gpio_level_low[irq] = true; break; default: return -EINVAL; @@ -469,9 +473,11 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) * descriptors. */ if (primary & WM831X_TCHPD_INT) - handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + WM831X_IRQ_TCHPD)); if (primary & WM831X_TCHDATA_INT) - handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + WM831X_IRQ_TCHDATA)); primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT); for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) { @@ -507,16 +513,29 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) } if (*status & wm831x_irqs[i].mask) - handle_nested_irq(wm831x->irq_base + i); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + i)); /* Simulate an edge triggered IRQ by polling the input * status. This is sucky but improves interoperability. */ if (primary == WM831X_GP_INT && - wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) { + wm831x->gpio_level_high[i - WM831X_IRQ_GPIO_1]) { ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) { - handle_nested_irq(wm831x->irq_base + i); + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + i)); + ret = wm831x_reg_read(wm831x, + WM831X_GPIO_LEVEL); + } + } + + if (primary == WM831X_GP_INT && + wm831x->gpio_level_low[i - WM831X_IRQ_GPIO_1]) { + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + while (!(ret & 1 << (i - WM831X_IRQ_GPIO_1))) { + handle_nested_irq(irq_find_mapping(wm831x->irq_domain, + i)); ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); } @@ -527,10 +546,34 @@ out: return IRQ_HANDLED; } +static int wm831x_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &wm831x_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 wm831x_irq_domain_ops = { + .map = wm831x_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + int wm831x_irq_init(struct wm831x *wm831x, int irq) { struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int i, cur_irq, ret; + struct irq_domain *domain; + int i, ret, irq_base; mutex_init(&wm831x->irq_lock); @@ -543,18 +586,33 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) } /* Try to dynamically allocate IRQs if no base is specified */ - if (!pdata || !pdata->irq_base) - wm831x->irq_base = -1; + if (pdata && pdata->irq_base) { + irq_base = irq_alloc_descs(pdata->irq_base, 0, + WM831X_NUM_IRQS, 0); + if (irq_base < 0) { + dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n", + irq_base); + irq_base = 0; + } + } else { + irq_base = 0; + } + + if (irq_base) + domain = irq_domain_add_legacy(wm831x->dev->of_node, + ARRAY_SIZE(wm831x_irqs), + irq_base, 0, + &wm831x_irq_domain_ops, + wm831x); else - wm831x->irq_base = pdata->irq_base; + domain = irq_domain_add_linear(wm831x->dev->of_node, + ARRAY_SIZE(wm831x_irqs), + &wm831x_irq_domain_ops, + wm831x); - wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0, - WM831X_NUM_IRQS, 0); - if (wm831x->irq_base < 0) { - dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n", - wm831x->irq_base); - wm831x->irq_base = 0; - return 0; + if (!domain) { + dev_warn(wm831x->dev, "Failed to allocate IRQ domain\n"); + return -EINVAL; } if (pdata && pdata->irq_cmos) @@ -565,38 +623,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, WM831X_IRQ_OD, i); - /* Try to flag /IRQ as a wake source; there are a number of - * unconditional wake sources in the PMIC so this isn't - * conditional but we don't actually care *too* much if it - * fails. - */ - ret = enable_irq_wake(irq); - if (ret != 0) { - dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n", - ret); - } - wm831x->irq = irq; - - /* Register them with genirq */ - for (cur_irq = wm831x->irq_base; - cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, wm831x); - irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 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(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif - } + wm831x->irq_domain = domain; if (irq) { + /* Try to flag /IRQ as a wake source; there are a number of + * unconditional wake sources in the PMIC so this isn't + * conditional but we don't actually care *too* much if it + * fails. + */ + ret = enable_irq_wake(irq); + if (ret != 0) { + dev_warn(wm831x->dev, + "Can't enable IRQ as wake source: %d\n", + ret); + } + ret = request_threaded_irq(irq, NULL, wm831x_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "wm831x", wm831x); diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index dd1caaac55e..8a9b11ca076 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -20,6 +20,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/regmap.h> #include <linux/workqueue.h> #include <linux/mfd/wm8350/core.h> @@ -74,7 +75,7 @@ static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, int bytes = num_regs * 2; dev_dbg(wm8350->dev, "volatile read\n"); - ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest); + ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes); for (i = reg; i < reg + num_regs; i++) { /* Cache is CPU endian */ @@ -96,9 +97,6 @@ static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) int ret = 0; int bytes = num_regs * 2; - if (wm8350->read_dev == NULL) - return -ENODEV; - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { dev_err(wm8350->dev, "invalid reg %x\n", reg + num_regs - 1); @@ -149,9 +147,6 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) int end = reg + num_regs; int bytes = num_regs * 2; - if (wm8350->write_dev == NULL) - return -ENODEV; - if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { dev_err(wm8350->dev, "invalid reg %x\n", reg + num_regs - 1); @@ -182,7 +177,7 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) } /* Actually write it out */ - return wm8350->write_dev(wm8350, reg, bytes, (char *)src); + return regmap_raw_write(wm8350->regmap, reg, src, bytes); } /* @@ -515,9 +510,8 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode) * a PMIC so the device many not be in a virgin state and we * can't rely on the silicon values. */ - ret = wm8350->read_dev(wm8350, 0, - sizeof(u16) * (WM8350_MAX_REGISTER + 1), - wm8350->reg_cache); + ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache, + sizeof(u16) * (WM8350_MAX_REGISTER + 1)); if (ret < 0) { dev_err(wm8350->dev, "failed to read initial cache values\n"); @@ -570,35 +564,30 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq, struct wm8350_platform_data *pdata) { int ret; - u16 id1, id2, mask_rev; - u16 cust_id, mode, chip_rev; + unsigned int id1, id2, mask_rev; + unsigned int cust_id, mode, chip_rev; dev_set_drvdata(wm8350->dev, wm8350); /* get WM8350 revision and config mode */ - ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); + ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1); if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); goto err; } - ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + ret = regmap_read(wm8350->regmap, WM8350_ID, &id2); if (ret != 0) { dev_err(wm8350->dev, "Failed to read ID: %d\n", ret); goto err; } - ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev), - &mask_rev); + ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev); if (ret != 0) { dev_err(wm8350->dev, "Failed to read revision: %d\n", ret); goto err; } - id1 = be16_to_cpu(id1); - id2 = be16_to_cpu(id2); - mask_rev = be16_to_cpu(mask_rev); - if (id1 != 0x6143) { dev_err(wm8350->dev, "Device with ID %x is not a WM8350\n", id1); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c index d955faaf27c..a68aceb4e48 100644 --- a/drivers/mfd/wm8350-i2c.c +++ b/drivers/mfd/wm8350-i2c.c @@ -15,47 +15,18 @@ #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/mfd/wm8350/core.h> +#include <linux/regmap.h> #include <linux/slab.h> -static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg, - int bytes, void *dest) -{ - int ret; - - ret = i2c_master_send(wm8350->i2c_client, ®, 1); - if (ret < 0) - return ret; - ret = i2c_master_recv(wm8350->i2c_client, dest, bytes); - if (ret < 0) - return ret; - if (ret != bytes) - return -EIO; - return 0; -} - -static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg, - int bytes, void *src) -{ - /* we add 1 byte for device register */ - u8 msg[(WM8350_MAX_REGISTER << 1) + 1]; - int ret; - - if (bytes > ((WM8350_MAX_REGISTER << 1) + 1)) - return -EINVAL; - - msg[0] = reg; - memcpy(&msg[1], src, bytes); - ret = i2c_master_send(wm8350->i2c_client, msg, bytes + 1); - if (ret < 0) - return ret; - if (ret != bytes + 1) - return -EIO; - return 0; -} +static const struct regmap_config wm8350_regmap = { + .reg_bits = 8, + .val_bits = 16, +}; static int wm8350_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -67,20 +38,18 @@ static int wm8350_i2c_probe(struct i2c_client *i2c, if (wm8350 == NULL) return -ENOMEM; + wm8350->regmap = devm_regmap_init_i2c(i2c, &wm8350_regmap); + if (IS_ERR(wm8350->regmap)) { + ret = PTR_ERR(wm8350->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(i2c, wm8350); wm8350->dev = &i2c->dev; - wm8350->i2c_client = i2c; - wm8350->read_dev = wm8350_i2c_read_device; - wm8350->write_dev = wm8350_i2c_write_device; - - ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data); - if (ret < 0) - goto err; - - return ret; -err: - return ret; + return wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data); } static int wm8350_i2c_remove(struct i2c_client *i2c) diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 1189a17f0f2..4b7d378551d 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -23,136 +23,16 @@ #include <linux/regmap.h> #include <linux/slab.h> -static struct { - u16 readable; /* Mask of readable bits */ - u16 writable; /* Mask of writable bits */ - u16 vol; /* Mask of volatile bits */ - int is_codec; /* Register controlled by codec reset */ - u16 default_val; /* Value on reset */ -} reg_data[] = { - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */ - { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */ - { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */ - { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */ - { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4 */ - { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5 */ - { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6 */ - { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7 */ - { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8 */ - { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9 */ - { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */ - { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */ - { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */ - { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */ - { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */ - { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */ - { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */ - { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */ - { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */ - { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */ - { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */ - { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */ - { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */ - { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */ - { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */ - { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */ - { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */ - { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */ - { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */ - { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */ - { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */ - { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */ - { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */ - { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */ - { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */ - { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */ - { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */ - { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */ - { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */ - { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */ - { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */ - { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */ - { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */ - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */ - { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */ - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */ - { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */ - { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */ - { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */ - { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */ - { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */ - { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */ - { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */ - { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */ - { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */ - { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */ -}; - -static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest) +static bool wm8400_volatile(struct device *dev, unsigned int reg) { - int i, ret = 0; - - BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache)); - - /* If there are any volatile reads then read back the entire block */ - for (i = reg; i < reg + num_regs; i++) - if (reg_data[i].vol) { - ret = regmap_bulk_read(wm8400->regmap, reg, dest, - num_regs); - return ret; - } - - /* Otherwise use the cache */ - memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16)); - - return 0; -} - -static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, - u16 *src) -{ - int ret, i; - - BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache)); - - for (i = 0; i < num_regs; i++) { - BUG_ON(!reg_data[reg + i].writable); - wm8400->reg_cache[reg + i] = src[i]; - ret = regmap_write(wm8400->regmap, reg, src[i]); - if (ret != 0) - return ret; + switch (reg) { + case WM8400_INTERRUPT_STATUS_1: + case WM8400_INTERRUPT_LEVELS: + case WM8400_SHUTDOWN_REASON: + return true; + default: + return false; } - - return 0; } /** @@ -165,13 +45,12 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, */ u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg) { - u16 val; - - mutex_lock(&wm8400->io_lock); - - wm8400_read(wm8400, reg, 1, &val); + unsigned int val; + int ret; - mutex_unlock(&wm8400->io_lock); + ret = regmap_read(wm8400->regmap, reg, &val); + if (ret < 0) + return ret; return val; } @@ -179,63 +58,10 @@ EXPORT_SYMBOL_GPL(wm8400_reg_read); int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data) { - int ret; - - mutex_lock(&wm8400->io_lock); - - ret = wm8400_read(wm8400, reg, count, data); - - mutex_unlock(&wm8400->io_lock); - - return ret; + return regmap_bulk_read(wm8400->regmap, reg, data, count); } EXPORT_SYMBOL_GPL(wm8400_block_read); -/** - * wm8400_set_bits - Bitmask write - * - * @wm8400: Pointer to wm8400 control structure - * @reg: Register to access - * @mask: Mask of bits to change - * @val: Value to set for masked bits - */ -int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val) -{ - u16 tmp; - int ret; - - mutex_lock(&wm8400->io_lock); - - ret = wm8400_read(wm8400, reg, 1, &tmp); - tmp = (tmp & ~mask) | val; - if (ret == 0) - ret = wm8400_write(wm8400, reg, 1, &tmp); - - mutex_unlock(&wm8400->io_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(wm8400_set_bits); - -/** - * wm8400_reset_codec_reg_cache - Reset cached codec registers to - * their default values. - */ -void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) -{ - int i; - - mutex_lock(&wm8400->io_lock); - - /* Reset all codec registers to their initial value */ - for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) - if (reg_data[i].is_codec) - wm8400->reg_cache[i] = reg_data[i].default_val; - - mutex_unlock(&wm8400->io_lock); -} -EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); - static int wm8400_register_codec(struct wm8400 *wm8400) { struct mfd_cell cell = { @@ -257,44 +83,24 @@ static int wm8400_register_codec(struct wm8400 *wm8400) static int wm8400_init(struct wm8400 *wm8400, struct wm8400_platform_data *pdata) { - u16 reg; - int ret, i; - - mutex_init(&wm8400->io_lock); + unsigned int reg; + int ret; dev_set_drvdata(wm8400->dev, wm8400); /* Check that this is actually a WM8400 */ - ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i); + ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, ®); if (ret != 0) { dev_err(wm8400->dev, "Chip ID register read failed\n"); return -EIO; } - if (i != reg_data[WM8400_RESET_ID].default_val) { - dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i); + if (reg != 0x6172) { + dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", + reg); return -ENODEV; } - /* We don't know what state the hardware is in and since this - * is a PMIC we can't reset it safely so initialise the register - * cache from the hardware. - */ - ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache, - ARRAY_SIZE(wm8400->reg_cache)); - if (ret != 0) { - dev_err(wm8400->dev, "Register cache read failed\n"); - return -EIO; - } - for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) - wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]); - - /* If the codec is in reset use hard coded values */ - if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA)) - for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) - if (reg_data[i].is_codec) - wm8400->reg_cache[i] = reg_data[i].default_val; - - ret = wm8400_read(wm8400, WM8400_ID, 1, ®); + ret = regmap_read(wm8400->regmap, WM8400_ID, ®); if (ret != 0) { dev_err(wm8400->dev, "ID register read failed: %d\n", ret); return ret; @@ -334,8 +140,22 @@ static const struct regmap_config wm8400_regmap_config = { .reg_bits = 8, .val_bits = 16, .max_register = WM8400_REGISTER_COUNT - 1, + + .volatile_reg = wm8400_volatile, + + .cache_type = REGCACHE_RBTREE, }; +/** + * wm8400_reset_codec_reg_cache - Reset cached codec registers to + * their default values. + */ +void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) +{ + regmap_reinit_cache(wm8400->regmap, &wm8400_regmap_config); +} +EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int wm8400_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 9d7ca1e978f..1e321d34977 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -500,7 +500,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) ret); goto err_enable; } - wm8994->revision = ret; + wm8994->revision = ret & WM8994_CHIP_REV_MASK; + wm8994->cust_id = (ret & WM8994_CUST_ID_MASK) >> WM8994_CUST_ID_SHIFT; switch (wm8994->type) { case WM8994: @@ -553,8 +554,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq) break; } - dev_info(wm8994->dev, "%s revision %c\n", devname, - 'A' + wm8994->revision); + dev_info(wm8994->dev, "%s revision %c CUST_ID %02x\n", devname, + 'A' + wm8994->revision, wm8994->cust_id); switch (wm8994->type) { case WM1811: @@ -732,23 +733,7 @@ static struct i2c_driver wm8994_i2c_driver = { .id_table = wm8994_i2c_id, }; -static int __init wm8994_i2c_init(void) -{ - int ret; - - ret = i2c_add_driver(&wm8994_i2c_driver); - if (ret != 0) - pr_err("Failed to register wm8994 I2C driver: %d\n", ret); - - return ret; -} -module_init(wm8994_i2c_init); - -static void __exit wm8994_i2c_exit(void) -{ - i2c_del_driver(&wm8994_i2c_driver); -} -module_exit(wm8994_i2c_exit); +module_i2c_driver(wm8994_i2c_driver); MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index bfd25af6ecb..52e9e294494 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -1122,7 +1122,6 @@ static bool wm8994_volatile_register(struct device *dev, unsigned int reg) case WM8994_RATE_STATUS: case WM8958_MIC_DETECT_3: case WM8994_DC_SERVO_4E: - case WM8994_CHIP_REVISION: case WM8994_INTERRUPT_STATUS_1: case WM8994_INTERRUPT_STATUS_2: return true; |