diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-12 10:01:06 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-12 10:01:06 -0700 |
commit | e83ddb335468cdd9ea6e9767eb30b64d8ff176ce (patch) | |
tree | af7ca0b5be74b713970149efaebe682596523252 /drivers/mfd | |
parent | 14a4fa20a10d76eb98b7feb25be60735217929ba (diff) | |
parent | d0a11693967295772d2a7c22b6b37eb20684e709 (diff) |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (40 commits)
mfd: Fix incorrect kfree(i2c) in wm8994-core i2c_driver probe
mfd: Fix incorrect kfree(i2c) in wm831x-core i2c_driver probe
mfd: Fix incorrect kfree(i2c) in tps6507x i2c_driver probe
mfd: Add TPS6586x driver
mfd: Use macros instead of some constant magic numbers for menelaus
mfd: Fix menelaus mmc slot 2 misconfiguration
mfd: Missing slab.h includes
mfd: Fix wrong wm8350-core kfree in error path
mfd: Fix wm8994_device_init() return value
mfd: Avoid calling platform_device_put() twice in ucb1400 probe error path
mfd: Annotate tc6387xb probe/remove routines with __devinit/__devexit
mfd: Fix tc6387xb resource reclaim
mfd: Fix wrong goto labels for tc6393xb error handling
mfd: Get rid of now unused mc13783 private header
hwmon: Don't access struct mc13783 directly from mc13783-adc
mfd: New mc13783 function exposing flags
mfd: Check jz4740-adc kmalloc() result
mfd: Fix jz4740-adc resource reclaim in probe error path
mfd: Add WM8321 support
mfd: Add stmpe auto sleep feature
...
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/88pm860x-core.c | 84 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 65 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 4 | ||||
-rw-r--r-- | drivers/mfd/ab3100-otp.c | 16 | ||||
-rw-r--r-- | drivers/mfd/ab3550-core.c | 23 | ||||
-rw-r--r-- | drivers/mfd/ab8500-spi.c | 7 | ||||
-rw-r--r-- | drivers/mfd/abx500-core.c | 2 | ||||
-rw-r--r-- | drivers/mfd/davinci_voicecodec.c | 6 | ||||
-rw-r--r-- | drivers/mfd/janz-cmodio.c | 1 | ||||
-rw-r--r-- | drivers/mfd/jz4740-adc.c | 394 | ||||
-rw-r--r-- | drivers/mfd/max8925-core.c | 27 | ||||
-rw-r--r-- | drivers/mfd/mc13783-core.c | 30 | ||||
-rw-r--r-- | drivers/mfd/menelaus.c | 75 | ||||
-rw-r--r-- | drivers/mfd/mfd-core.c | 4 | ||||
-rw-r--r-- | drivers/mfd/stmpe.c | 985 | ||||
-rw-r--r-- | drivers/mfd/stmpe.h | 183 | ||||
-rw-r--r-- | drivers/mfd/t7l66xb.c | 3 | ||||
-rw-r--r-- | drivers/mfd/tc6387xb.c | 16 | ||||
-rw-r--r-- | drivers/mfd/tc6393xb.c | 4 | ||||
-rw-r--r-- | drivers/mfd/tps6507x.c | 4 | ||||
-rw-r--r-- | drivers/mfd/tps6586x.c | 375 | ||||
-rw-r--r-- | drivers/mfd/twl6030-pwm.c | 163 | ||||
-rw-r--r-- | drivers/mfd/ucb1400_core.c | 2 | ||||
-rw-r--r-- | drivers/mfd/wm831x-core.c | 18 | ||||
-rw-r--r-- | drivers/mfd/wm8350-core.c | 6 | ||||
-rw-r--r-- | drivers/mfd/wm8994-core.c | 8 |
26 files changed, 2390 insertions, 115 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 2c65a2c5729..07933f3f7e4 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -74,12 +74,12 @@ static struct mfd_cell backlight_devs[] = { } static struct resource led_resources[] = { - PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB2B), - PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB2C), - PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB2D), - PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB1B), - PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB1C), - PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB1D), + PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B), + PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C), + PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D), + PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B), + PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C), + PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D), }; #define PM8606_LED_DEVS(_i) \ @@ -428,52 +428,44 @@ static int __devinit device_gpadc_init(struct pm860x_chip *chip, { struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ : chip->companion; - int use_gpadc = 0, data, ret; + int data; + int ret; /* initialize GPADC without activating it */ - if (pdata && pdata->touch) { - /* set GPADC MISC1 register */ - data = 0; - data |= (pdata->touch->gpadc_prebias << 1) - & PM8607_GPADC_PREBIAS_MASK; - data |= (pdata->touch->slot_cycle << 3) - & PM8607_GPADC_SLOT_CYCLE_MASK; - data |= (pdata->touch->off_scale << 5) - & PM8607_GPADC_OFF_SCALE_MASK; - data |= (pdata->touch->sw_cal << 7) - & PM8607_GPADC_SW_CAL_MASK; - if (data) { - ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); - if (ret < 0) - goto out; - } - /* set tsi prebias time */ - if (pdata->touch->tsi_prebias) { - data = pdata->touch->tsi_prebias; - ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); - if (ret < 0) - goto out; - } - /* set prebias & prechg time of pen detect */ - data = 0; - data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK; - data |= (pdata->touch->pen_prechg << 5) - & PM8607_PD_PRECHG_MASK; - if (data) { - ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); - if (ret < 0) - goto out; - } + if (!pdata || !pdata->touch) + return -EINVAL; - use_gpadc = 1; + /* set GPADC MISC1 register */ + data = 0; + data |= (pdata->touch->gpadc_prebias << 1) & PM8607_GPADC_PREBIAS_MASK; + data |= (pdata->touch->slot_cycle << 3) & PM8607_GPADC_SLOT_CYCLE_MASK; + data |= (pdata->touch->off_scale << 5) & PM8607_GPADC_OFF_SCALE_MASK; + data |= (pdata->touch->sw_cal << 7) & PM8607_GPADC_SW_CAL_MASK; + if (data) { + ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data); + if (ret < 0) + goto out; } - - /* turn on GPADC */ - if (use_gpadc) { - ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, - PM8607_GPADC_EN, PM8607_GPADC_EN); + /* set tsi prebias time */ + if (pdata->touch->tsi_prebias) { + data = pdata->touch->tsi_prebias; + ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data); + if (ret < 0) + goto out; } + /* set prebias & prechg time of pen detect */ + data = 0; + data |= pdata->touch->pen_prebias & PM8607_PD_PREBIAS_MASK; + data |= (pdata->touch->pen_prechg << 5) & PM8607_PD_PRECHG_MASK; + if (data) { + ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data); + if (ret < 0) + goto out; + } + + ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, + PM8607_GPADC_EN, PM8607_GPADC_EN); out: return ret; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9da0e504bbe..d75909e7cf2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -7,7 +7,16 @@ menuconfig MFD_SUPPORT depends on HAS_IOMEM default y help - Configure MFD device drivers. + Multifunction devices embed several functions (e.g. GPIOs, + touchscreens, keyboards, current regulators, power management chips, + etc...) in one single integrated circuit. They usually talk to the + main CPU through one or more IRQ lines and low speed data busses (SPI, + I2C, etc..). They appear as one single device to the main system + through the data bus and the MFD framework allows for sub devices + (a.k.a. functions) to appear as discrete platform devices. + MFDs are typically found on embedded platforms. + + This option alone does not add any kernel code. if MFD_SUPPORT @@ -177,6 +186,38 @@ config TWL4030_CODEC select MFD_CORE default n +config TWL6030_PWM + tristate "TWL6030 PWM (Pulse Width Modulator) Support" + depends on TWL4030_CORE + select HAVE_PWM + default n + help + Say yes here if you want support for TWL6030 PWM. + This is used to control charging LED brightness. + +config MFD_STMPE + bool "Support STMicroelectronics STMPE" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + help + Support for the STMPE family of I/O Expanders from + STMicroelectronics. + + Currently supported devices are: + + STMPE811: GPIO, Touchscreen + STMPE1601: GPIO, Keypad + STMPE2401: GPIO, Keypad + STMPE2403: GPIO, Keypad + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device. Currently available sub drivers are: + + GPIO: stmpe-gpio + Keypad: stmpe-keypad + Touchscreen: stmpe-ts + config MFD_TC35892 bool "Support Toshiba TC35892" depends on I2C=y && GENERIC_HARDIRQS @@ -482,6 +523,28 @@ config MFD_JANZ_CMODIO host many different types of MODULbus daughterboards, including CAN and GPIO controllers. +config MFD_JZ4740_ADC + tristate "Support for the JZ4740 SoC ADC core" + select MFD_CORE + depends on MACH_JZ4740 + help + Say yes here if you want support for the ADC unit in the JZ4740 SoC. + This driver is necessary for jz4740-battery and jz4740-hwmon driver. + +config MFD_TPS6586X + tristate "TPS6586x Power Management chips" + depends on I2C && GPIOLIB + select MFD_CORE + help + If you say yes here you get support for the TPS6586X series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + + This driver can also be built as a module. If so, the module + will be called tps6586x. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index fb503e77dc6..1e48d7e3e88 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o +obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_TC35892) += tc35892.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o @@ -36,6 +37,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o +obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_MFD_MC13783) += mc13783-core.o @@ -71,3 +73,5 @@ obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o +obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o +obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 63d2b727ddb..8440010eb2b 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -199,7 +199,7 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) err = ab3100_otp_read(otp); if (err) - return err; + goto err_otp_read; dev_info(&pdev->dev, "AB3100 OTP readout registered\n"); @@ -208,21 +208,21 @@ static int __init ab3100_otp_probe(struct platform_device *pdev) err = device_create_file(&pdev->dev, &ab3100_otp_attrs[i]); if (err) - goto out_no_sysfs; + goto err_create_file; } /* debugfs entries */ err = ab3100_otp_init_debugfs(&pdev->dev, otp); if (err) - goto out_no_debugfs; + goto err_init_debugfs; return 0; -out_no_sysfs: - for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++) - device_remove_file(&pdev->dev, - &ab3100_otp_attrs[i]); -out_no_debugfs: +err_init_debugfs: +err_create_file: + while (--i >= 0) + device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]); +err_otp_read: kfree(otp); return err; } diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index f54ab62e7bc..8a98739e6d9 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -589,16 +589,16 @@ static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) } /* - * The exported register access functionality. + * The register access functionality. */ -int ab3550_get_chip_id(struct device *dev) +static int ab3550_get_chip_id(struct device *dev) { struct ab3550 *ab = dev_get_drvdata(dev->parent); return (int)ab->chip_id; } -int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) +static int ab3550_mask_and_set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) { struct ab3550 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -612,15 +612,15 @@ int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank, bitmask, bitvalues); } -int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg, - u8 value) +static int ab3550_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 value) { return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, value); } -int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg, - u8 *value) +static int ab3550_get_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 *value) { struct ab3550 *ab; struct platform_device *pdev = to_platform_device(dev); @@ -633,7 +633,7 @@ int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg, return get_register_interruptible(ab, bank, reg, value); } -int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, +static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, u8 first_reg, u8 *regvals, u8 numregs) { struct ab3550 *ab; @@ -649,7 +649,8 @@ int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, numregs); } -int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event) +static int ab3550_event_registers_startup_state_get(struct device *dev, + u8 *event) { struct ab3550 *ab; @@ -661,7 +662,7 @@ int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event) return 0; } -int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) +static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) { struct ab3550 *ab; struct ab3550_platform_data *plf_data; diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c index b81d4f768ef..e1c8b62b086 100644 --- a/drivers/mfd/ab8500-spi.c +++ b/drivers/mfd/ab8500-spi.c @@ -68,7 +68,12 @@ static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr) ret = spi_sync(spi, &msg); if (!ret) - ret = ab8500->rx_buf[0]; + /* + * Only the 8 lowermost bytes are + * defined with value, the rest may + * vary depending on chip/board noise. + */ + ret = ab8500->rx_buf[0] & 0xFFU; return ret; } diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c index 3b3b97ec32a..f12720dbe12 100644 --- a/drivers/mfd/abx500-core.c +++ b/drivers/mfd/abx500-core.c @@ -36,7 +36,7 @@ int abx500_register_ops(struct device *dev, struct abx500_ops *ops) struct abx500_device_entry *dev_entry; dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL); - if (IS_ERR(dev_entry)) { + if (!dev_entry) { dev_err(dev, "register_ops kzalloc failed"); return -ENOMEM; } diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index 3e75f02e477..33c923d215c 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -94,7 +94,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); - return -ENXIO; + ret = -ENXIO; + goto fail4; } davinci_vc->davinci_vcif.dma_tx_channel = res->start; @@ -104,7 +105,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); - return -ENXIO; + ret = -ENXIO; + goto fail4; } davinci_vc->davinci_vcif.dma_rx_channel = res->start; diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 9ed630799ac..36a166bcdb0 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/mfd/core.h> #include <linux/mfd/janz.h> diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c new file mode 100644 index 00000000000..3ad492cb6c4 --- /dev/null +++ b/drivers/mfd/jz4740-adc.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC ADC driver + * + * 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. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver synchronizes access to the JZ4740 ADC core between the + * JZ4740 battery and hwmon drivers. + */ + +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <linux/clk.h> +#include <linux/mfd/core.h> + +#include <linux/jz4740-adc.h> + + +#define JZ_REG_ADC_ENABLE 0x00 +#define JZ_REG_ADC_CFG 0x04 +#define JZ_REG_ADC_CTRL 0x08 +#define JZ_REG_ADC_STATUS 0x0c + +#define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10 +#define JZ_REG_ADC_BATTERY_BASE 0x1c +#define JZ_REG_ADC_HWMON_BASE 0x20 + +#define JZ_ADC_ENABLE_TOUCH BIT(2) +#define JZ_ADC_ENABLE_BATTERY BIT(1) +#define JZ_ADC_ENABLE_ADCIN BIT(0) + +enum { + JZ_ADC_IRQ_ADCIN = 0, + JZ_ADC_IRQ_BATTERY, + JZ_ADC_IRQ_TOUCH, + JZ_ADC_IRQ_PENUP, + JZ_ADC_IRQ_PENDOWN, +}; + +struct jz4740_adc { + struct resource *mem; + void __iomem *base; + + int irq; + int irq_base; + + struct clk *clk; + atomic_t clk_ref; + + spinlock_t lock; +}; + +static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq, + bool masked) +{ + unsigned long flags; + uint8_t val; + + irq -= adc->irq_base; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_CTRL); + if (masked) + val |= BIT(irq); + else + val &= ~BIT(irq); + writeb(val, adc->base + JZ_REG_ADC_CTRL); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void jz4740_adc_irq_mask(unsigned int irq) +{ + struct jz4740_adc *adc = get_irq_chip_data(irq); + jz4740_adc_irq_set_masked(adc, irq, true); +} + +static void jz4740_adc_irq_unmask(unsigned int irq) +{ + struct jz4740_adc *adc = get_irq_chip_data(irq); + jz4740_adc_irq_set_masked(adc, irq, false); +} + +static void jz4740_adc_irq_ack(unsigned int irq) +{ + struct jz4740_adc *adc = get_irq_chip_data(irq); + + irq -= adc->irq_base; + writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS); +} + +static struct irq_chip jz4740_adc_irq_chip = { + .name = "jz4740-adc", + .mask = jz4740_adc_irq_mask, + .unmask = jz4740_adc_irq_unmask, + .ack = jz4740_adc_irq_ack, +}; + +static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct jz4740_adc *adc = get_irq_desc_data(desc); + uint8_t status; + unsigned int i; + + status = readb(adc->base + JZ_REG_ADC_STATUS); + + for (i = 0; i < 5; ++i) { + if (status & BIT(i)) + generic_handle_irq(adc->irq_base + i); + } +} + + +/* Refcounting for the ADC clock is done in here instead of in the clock + * framework, because it is the only clock which is shared between multiple + * devices and thus is the only clock which needs refcounting */ +static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc) +{ + if (atomic_inc_return(&adc->clk_ref) == 1) + clk_enable(adc->clk); +} + +static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc) +{ + if (atomic_dec_return(&adc->clk_ref) == 0) + clk_disable(adc->clk); +} + +static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine, + bool enabled) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_ENABLE); + if (enabled) + val |= BIT(engine); + else + val &= BIT(engine); + writeb(val, adc->base + JZ_REG_ADC_ENABLE); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static int jz4740_adc_cell_enable(struct platform_device *pdev) +{ + struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent); + + jz4740_adc_clk_enable(adc); + jz4740_adc_set_enabled(adc, pdev->id, true); + + return 0; +} + +static int jz4740_adc_cell_disable(struct platform_device *pdev) +{ + struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent); + + jz4740_adc_set_enabled(adc, pdev->id, false); + jz4740_adc_clk_disable(adc); + + return 0; +} + +int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val) +{ + struct jz4740_adc *adc = dev_get_drvdata(dev); + unsigned long flags; + uint32_t cfg; + + if (!adc) + return -ENODEV; + + spin_lock_irqsave(&adc->lock, flags); + + cfg = readl(adc->base + JZ_REG_ADC_CFG); + + cfg &= ~mask; + cfg |= val; + + writel(cfg, adc->base + JZ_REG_ADC_CFG); + + spin_unlock_irqrestore(&adc->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(jz4740_adc_set_config); + +static struct resource jz4740_hwmon_resources[] = { + { + .start = JZ_ADC_IRQ_ADCIN, + .flags = IORESOURCE_IRQ, + }, + { + .start = JZ_REG_ADC_HWMON_BASE, + .end = JZ_REG_ADC_HWMON_BASE + 3, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource jz4740_battery_resources[] = { + { + .start = JZ_ADC_IRQ_BATTERY, + .flags = IORESOURCE_IRQ, + }, + { + .start = JZ_REG_ADC_BATTERY_BASE, + .end = JZ_REG_ADC_BATTERY_BASE + 3, + .flags = IORESOURCE_MEM, + }, +}; + +const struct mfd_cell jz4740_adc_cells[] = { + { + .id = 0, + .name = "jz4740-hwmon", + .num_resources = ARRAY_SIZE(jz4740_hwmon_resources), + .resources = jz4740_hwmon_resources, + .platform_data = (void *)&jz4740_adc_cells[0], + .data_size = sizeof(struct mfd_cell), + + .enable = jz4740_adc_cell_enable, + .disable = jz4740_adc_cell_disable, + }, + { + .id = 1, + .name = "jz4740-battery", + .num_resources = ARRAY_SIZE(jz4740_battery_resources), + .resources = jz4740_battery_resources, + .platform_data = (void *)&jz4740_adc_cells[1], + .data_size = sizeof(struct mfd_cell), + + .enable = jz4740_adc_cell_enable, + .disable = jz4740_adc_cell_disable, + }, +}; + +static int __devinit jz4740_adc_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_adc *adc; + struct resource *mem_base; + int irq; + + adc = kmalloc(sizeof(*adc), GFP_KERNEL); + if (!adc) { + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); + return -ENOMEM; + } + + adc->irq = platform_get_irq(pdev, 0); + if (adc->irq < 0) { + ret = adc->irq; + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); + goto err_free; + } + + adc->irq_base = platform_get_irq(pdev, 1); + if (adc->irq_base < 0) { + ret = adc->irq_base; + dev_err(&pdev->dev, "Failed to get irq base: %d\n", ret); + goto err_free; + } + + mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_base) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); + goto err_free; + } + + /* Only request the shared registers for the MFD driver */ + adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS, + pdev->name); + if (!adc->mem) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + goto err_free; + } + + adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem)); + if (!adc->base) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); + goto err_release_mem_region; + } + + adc->clk = clk_get(&pdev->dev, "adc"); + if (IS_ERR(adc->clk)) { + ret = PTR_ERR(adc->clk); + dev_err(&pdev->dev, "Failed to get clock: %d\n", ret); + goto err_iounmap; + } + + spin_lock_init(&adc->lock); + atomic_set(&adc->clk_ref, 0); + + platform_set_drvdata(pdev, adc); + + for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) { + set_irq_chip_data(irq, adc); + set_irq_chip_and_handler(irq, &jz4740_adc_irq_chip, + handle_level_irq); + } + + set_irq_data(adc->irq, adc); + set_irq_chained_handler(adc->irq, jz4740_adc_irq_demux); + + writeb(0x00, adc->base + JZ_REG_ADC_ENABLE); + writeb(0xff, adc->base + JZ_REG_ADC_CTRL); + + ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells, + ARRAY_SIZE(jz4740_adc_cells), mem_base, adc->irq_base); + if (ret < 0) + goto err_clk_put; + + return 0; + +err_clk_put: + clk_put(adc->clk); +err_iounmap: + platform_set_drvdata(pdev, NULL); + iounmap(adc->base); +err_release_mem_region: + release_mem_region(adc->mem->start, resource_size(adc->mem)); +err_free: + kfree(adc); + + return ret; +} + +static int __devexit jz4740_adc_remove(struct platform_device *pdev) +{ + struct jz4740_adc *adc = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + + set_irq_data(adc->irq, NULL); + set_irq_chained_handler(adc->irq, NULL); + + iounmap(adc->base); + release_mem_region(adc->mem->start, resource_size(adc->mem)); + + clk_put(adc->clk); + + platform_set_drvdata(pdev, NULL); + + kfree(adc); + + return 0; +} + +struct platform_driver jz4740_adc_driver = { + .probe = jz4740_adc_probe, + .remove = __devexit_p(jz4740_adc_remove), + .driver = { + .name = "jz4740-adc", + .owner = THIS_MODULE, + }, +}; + +static int __init jz4740_adc_init(void) +{ + return platform_driver_register(&jz4740_adc_driver); +} +module_init(jz4740_adc_init); + +static void __exit jz4740_adc_exit(void) +{ + platform_driver_unregister(&jz4740_adc_driver); +} +module_exit(jz4740_adc_exit); + +MODULE_DESCRIPTION("JZ4740 SoC ADC driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:jz4740-adc"); diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index f621bcea3d0..04028a9ee08 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -90,6 +90,24 @@ static struct mfd_cell rtc_devs[] = { }, }; +static struct resource onkey_resources[] = { + { + .name = "max8925-onkey", + .start = MAX8925_IRQ_GPM_SW_3SEC, + .end = MAX8925_IRQ_GPM_SW_3SEC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell onkey_devs[] = { + { + .name = "max8925-onkey", + .num_resources = 1, + .resources = &onkey_resources[0], + .id = -1, + }, +}; + #define MAX8925_REG_RESOURCE(_start, _end) \ { \ .start = MAX8925_##_start, \ @@ -596,6 +614,15 @@ int __devinit max8925_device_init(struct max8925_chip *chip, dev_err(chip->dev, "Failed to add rtc subdev\n"); goto out; } + + ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), + &onkey_resources[0], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add onkey subdev\n"); + goto out_dev; + } + if (pdata && pdata->regulator[0]) { ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], ARRAY_SIZE(regulator_devs), diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c index fecf38a4f02..6df34989c1f 100644 --- a/drivers/mfd/mc13783-core.c +++ b/drivers/mfd/mc13783-core.c @@ -11,9 +11,31 @@ */ #include <linux/slab.h> #include <linux/module.h> +#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/mc13783-private.h> +#include <linux/mfd/mc13783.h> + +struct mc13783 { + struct spi_device *spidev; + struct mutex lock; + int irq; + int flags; + + irq_handler_t irqhandler[MC13783_NUM_IRQ]; + void *irqdata[MC13783_NUM_IRQ]; + + /* XXX these should go as platformdata to the regulator subdevice */ + struct mc13783_regulator_init_data *regulators; + int num_regulators; +}; + |