From f337134ff0cfe60fb1e347bc45b8e7190ef90a82 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 17 Aug 2010 13:13:36 +0100 Subject: mfd: Move PCF50633 IRQ protoypes where the definitions can see them Fixed warnings about unprototyped global functions. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/pcf50633/core.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index ad411a78870c..50d4a047118d 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -227,4 +227,11 @@ static inline struct pcf50633 *dev_to_pcf50633(struct device *dev) return dev_get_drvdata(dev); } +int pcf50633_irq_init(struct pcf50633 *pcf, int irq); +void pcf50633_irq_free(struct pcf50633 *pcf); +#ifdef CONFIG_PM +int pcf50633_irq_suspend(struct pcf50633 *pcf); +int pcf50633_irq_resume(struct pcf50633 *pcf); +#endif + #endif -- cgit v1.2.3 From b8e9cf0b28173fc25dae9f3ac44de6fc4e9fc385 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 16 Aug 2010 17:14:44 +0200 Subject: gpio: Add bitmask to block requests to unavailable stmpe GPIOs GPIOs on these controller are multi-functional. If you decided to use some of them e.g. as input channels for the ADC, you surely don't want those pins to be reassigned as simple GPIOs (which may be triggered even from userspace via 'export'). Same for the touchscreen controller pins. Since knowledge about the hardware is needed to decide which GPIOs to reserve, let this bitmask be inside platform_data and provide some defines to assist potential users. Signed-off-by: Wolfram Sang Acked-by: Rabin Vincent Cc: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/gpio/stmpe-gpio.c | 5 +++++ include/linux/mfd/stmpe.h | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c index 4e1f1b9d5e67..65b996083918 100644 --- a/drivers/gpio/stmpe-gpio.c +++ b/drivers/gpio/stmpe-gpio.c @@ -30,6 +30,7 @@ struct stmpe_gpio { struct mutex irq_lock; int irq_base; + unsigned norequest_mask; /* Caches of interrupt control registers for bus_lock */ u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS]; @@ -103,6 +104,9 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) struct stmpe_gpio *stmpe_gpio = to_stmpe_gpio(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; + if (stmpe_gpio->norequest_mask & (1 << offset)) + return -EINVAL; + return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO); } @@ -302,6 +306,7 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev) stmpe_gpio->dev = &pdev->dev; stmpe_gpio->stmpe = stmpe; + stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0; stmpe_gpio->chip = template_chip; stmpe_gpio->chip.ngpio = stmpe->num_gpios; diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 39ca7588659b..e762c270d8d4 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -112,13 +112,19 @@ struct stmpe_keypad_platform_data { bool no_autorepeat; }; +#define STMPE_GPIO_NOREQ_811_TOUCH (0xf0) + /** * struct stmpe_gpio_platform_data - STMPE GPIO platform data * @gpio_base: first gpio number assigned. A maximum of * %STMPE_NR_GPIOS GPIOs will be allocated. + * @norequest_mask: bitmask specifying which GPIOs should _not_ be + * requestable due to different usage (e.g. touch, keypad) + * STMPE_GPIO_NOREQ_* macros can be used here. */ struct stmpe_gpio_platform_data { int gpio_base; + unsigned norequest_mask; void (*setup)(struct stmpe *stmpe, unsigned gpio_base); void (*remove)(struct stmpe *stmpe, unsigned gpio_base); }; -- cgit v1.2.3 From 89712059c09ff12f1e60e444d05d2ca257dd00ef Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 10 Sep 2010 17:10:21 +0200 Subject: i2c: twl: add register defines for pm master module Some modules already need to talk to at least PROTECT_KEY register, while at that, add defines to the entire register space. Signed-off-by: Felipe Balbi Signed-off-by: Samuel Ortiz --- include/linux/i2c/twl.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'include/linux') diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 4793d8a7f480..53089516c17a 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -357,6 +357,52 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset); /*----------------------------------------------------------------------*/ +/* + * PM Master module register offsets (use TWL4030_MODULE_PM_MASTER) + */ + +#define TWL4030_PM_MASTER_CFG_P1_TRANSITION 0x00 +#define TWL4030_PM_MASTER_CFG_P2_TRANSITION 0x01 +#define TWL4030_PM_MASTER_CFG_P3_TRANSITION 0x02 +#define TWL4030_PM_MASTER_CFG_P123_TRANSITION 0x03 +#define TWL4030_PM_MASTER_STS_BOOT 0x04 +#define TWL4030_PM_MASTER_CFG_BOOT 0x05 +#define TWL4030_PM_MASTER_SHUNDAN 0x06 +#define TWL4030_PM_MASTER_BOOT_BCI 0x07 +#define TWL4030_PM_MASTER_CFG_PWRANA1 0x08 +#define TWL4030_PM_MASTER_CFG_PWRANA2 0x09 +#define TWL4030_PM_MASTER_BACKUP_MISC_STS 0x0b +#define TWL4030_PM_MASTER_BACKUP_MISC_CFG 0x0c +#define TWL4030_PM_MASTER_BACKUP_MISC_TST 0x0d +#define TWL4030_PM_MASTER_PROTECT_KEY 0x0e +#define TWL4030_PM_MASTER_STS_HW_CONDITIONS 0x0f +#define TWL4030_PM_MASTER_P1_SW_EVENTS 0x10 +#define TWL4030_PM_MASTER_P2_SW_EVENTS 0x11 +#define TWL4030_PM_MASTER_P3_SW_EVENTS 0x12 +#define TWL4030_PM_MASTER_STS_P123_STATE 0x13 +#define TWL4030_PM_MASTER_PB_CFG 0x14 +#define TWL4030_PM_MASTER_PB_WORD_MSB 0x15 +#define TWL4030_PM_MASTER_PB_WORD_LSB 0x16 +#define TWL4030_PM_MASTER_SEQ_ADD_W2P 0x1c +#define TWL4030_PM_MASTER_SEQ_ADD_P2A 0x1d +#define TWL4030_PM_MASTER_SEQ_ADD_A2W 0x1e +#define TWL4030_PM_MASTER_SEQ_ADD_A2S 0x1f +#define TWL4030_PM_MASTER_SEQ_ADD_S2A12 0x20 +#define TWL4030_PM_MASTER_SEQ_ADD_S2A3 0x21 +#define TWL4030_PM_MASTER_SEQ_ADD_WARM 0x22 +#define TWL4030_PM_MASTER_MEMORY_ADDRESS 0x23 +#define TWL4030_PM_MASTER_MEMORY_DATA 0x24 + +#define TWL4030_PM_MASTER_KEY_CFG1 0xc0 +#define TWL4030_PM_MASTER_KEY_CFG2 0x0c + +#define TWL4030_PM_MASTER_KEY_TST1 0xe0 +#define TWL4030_PM_MASTER_KEY_TST2 0x0e + +#define TWL4030_PM_MASTER_GLOBAL_TST 0xb6 + +/*----------------------------------------------------------------------*/ + /* Power bus message definitions */ /* The TWL4030/5030 splits its power-management resources (the various -- cgit v1.2.3 From 676e02d7a2ed9bb02994670a07df533a29a99de6 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 6 Aug 2010 11:28:06 +0900 Subject: mfd: Use i2c_client as an argument on MAX8998 i2c routines The MAX8998 chip have regulator and rtc features. The i2c slave address of regulator and rtc is different, so needs each i2c client on i2c operation functions. Also, this patch exports i2c operation functions instead of callback to make easy to read. Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Signed-off-by: Samuel Ortiz --- drivers/mfd/max8998.c | 29 ++++++++++++++--------------- drivers/regulator/max8998.c | 17 +++++++++++------ include/linux/mfd/max8998-private.h | 30 +++++------------------------- 3 files changed, 30 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index f1c928915880..3b8a5076d351 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -36,13 +36,13 @@ static struct mfd_cell max8998_devs[] = { } }; -static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest) +int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { - struct i2c_client *client = max8998->i2c_client; + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); int ret; mutex_lock(&max8998->iolock); - ret = i2c_smbus_read_byte_data(client, reg); + ret = i2c_smbus_read_byte_data(i2c, reg); mutex_unlock(&max8998->iolock); if (ret < 0) return ret; @@ -51,36 +51,38 @@ static int max8998_i2c_device_read(struct max8998_dev *max8998, u8 reg, u8 *dest *dest = ret; return 0; } +EXPORT_SYMBOL(max8998_read_reg); -static int max8998_i2c_device_write(struct max8998_dev *max8998, u8 reg, u8 value) +int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value) { - struct i2c_client *client = max8998->i2c_client; + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); int ret; mutex_lock(&max8998->iolock); - ret = i2c_smbus_write_byte_data(client, reg, value); + ret = i2c_smbus_write_byte_data(i2c, reg, value); mutex_unlock(&max8998->iolock); return ret; } +EXPORT_SYMBOL(max8998_write_reg); -static int max8998_i2c_device_update(struct max8998_dev *max8998, u8 reg, - u8 val, u8 mask) +int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) { - struct i2c_client *client = max8998->i2c_client; + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); int ret; mutex_lock(&max8998->iolock); - ret = i2c_smbus_read_byte_data(client, reg); + ret = i2c_smbus_read_byte_data(i2c, reg); if (ret >= 0) { u8 old_val = ret & 0xff; u8 new_val = (val & mask) | (old_val & (~mask)); - ret = i2c_smbus_write_byte_data(client, reg, new_val); + ret = i2c_smbus_write_byte_data(i2c, reg, new_val); if (ret >= 0) ret = 0; } mutex_unlock(&max8998->iolock); return ret; } +EXPORT_SYMBOL(max8998_update_reg); static int max8998_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -94,10 +96,7 @@ static int max8998_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max8998); max8998->dev = &i2c->dev; - max8998->i2c_client = i2c; - max8998->dev_read = max8998_i2c_device_read; - max8998->dev_write = max8998_i2c_device_write; - max8998->dev_update = max8998_i2c_device_update; + max8998->i2c = i2c; mutex_init(&max8998->iolock); ret = mfd_add_devices(max8998->dev, -1, diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index a1baf1fbe004..7f5fe6f198cf 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -173,6 +173,7 @@ static int max8998_get_enable_register(struct regulator_dev *rdev, static int max8998_ldo_is_enabled(struct regulator_dev *rdev) { struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; int ret, reg, shift = 8; u8 val; @@ -180,7 +181,7 @@ static int max8998_ldo_is_enabled(struct regulator_dev *rdev) if (ret) return ret; - ret = max8998_read_reg(max8998->iodev, reg, &val); + ret = max8998_read_reg(i2c, reg, &val); if (ret) return ret; @@ -190,25 +191,27 @@ static int max8998_ldo_is_enabled(struct regulator_dev *rdev) static int max8998_ldo_enable(struct regulator_dev *rdev) { struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; int reg, shift = 8, ret; ret = max8998_get_enable_register(rdev, ®, &shift); if (ret) return ret; - return max8998_update_reg(max8998->iodev, reg, 1<iodev->i2c; int reg, shift = 8, ret; ret = max8998_get_enable_register(rdev, ®, &shift); if (ret) return ret; - return max8998_update_reg(max8998->iodev, reg, 0, 1<iodev->i2c; int reg, shift = 0, mask, ret; u8 val; @@ -283,7 +287,7 @@ static int max8998_get_voltage(struct regulator_dev *rdev) if (ret) return ret; - ret = max8998_read_reg(max8998->iodev, reg, &val); + ret = max8998_read_reg(i2c, reg, &val); if (ret) return ret; @@ -297,6 +301,7 @@ static int max8998_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { struct max8998_data *max8998 = rdev_get_drvdata(rdev); + struct i2c_client *i2c = max8998->iodev->i2c; int min_vol = min_uV / 1000, max_vol = max_uV / 1000; int previous_vol = 0; const struct voltage_map_desc *desc; @@ -330,14 +335,14 @@ static int max8998_set_voltage(struct regulator_dev *rdev, /* wait for RAMP_UP_DELAY if rdev is BUCK1/2 and * ENRAMP is ON */ if (ldo == MAX8998_BUCK1 || ldo == MAX8998_BUCK2) { - max8998_read_reg(max8998->iodev, MAX8998_REG_ONOFF4, &val); + max8998_read_reg(i2c, MAX8998_REG_ONOFF4, &val); if (val & (1 << 4)) { en_ramp = true; previous_vol = max8998_get_voltage(rdev); } } - ret = max8998_update_reg(max8998->iodev, reg, i<min + desc->step*i - previous_vol/1000; diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index 6dc75b3e2d33..f0a20cdc288c 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -75,38 +75,18 @@ enum { /** * struct max8998_dev - max8998 master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) - * @i2c_client: i2c client private data - * @dev_read(): chip register read function - * @dev_write(): chip register write function - * @dev_update(): chip register update function + * @i2c: i2c client private data * @iolock: mutex for serializing io access */ struct max8998_dev { struct device *dev; - struct i2c_client *i2c_client; - int (*dev_read)(struct max8998_dev *max8998, u8 reg, u8 *dest); - int (*dev_write)(struct max8998_dev *max8998, u8 reg, u8 val); - int (*dev_update)(struct max8998_dev *max8998, u8 reg, u8 val, u8 mask); + struct i2c_client *i2c; struct mutex iolock; }; -static inline int max8998_read_reg(struct max8998_dev *max8998, u8 reg, - u8 *value) -{ - return max8998->dev_read(max8998, reg, value); -} - -static inline int max8998_write_reg(struct max8998_dev *max8998, u8 reg, - u8 value) -{ - return max8998->dev_write(max8998, reg, value); -} - -static inline int max8998_update_reg(struct max8998_dev *max8998, u8 reg, - u8 value, u8 mask) -{ - return max8998->dev_update(max8998, reg, value, mask); -} +extern int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +extern int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +extern int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); #endif /* __LINUX_MFD_MAX8998_PRIV_H */ -- cgit v1.2.3 From 2c7e6f5797140b33ec2b967ff28941e1c7eff4b2 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 10 Sep 2010 18:36:39 +0200 Subject: mfd: Add MAX8998 interrupts support Use genirq and provide seperated file for interrupts support. Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 +- drivers/mfd/Makefile | 2 +- drivers/mfd/max8998-irq.c | 255 ++++++++++++++++++++++++++++++++++++ drivers/mfd/max8998.c | 25 +++- include/linux/mfd/max8998-private.h | 72 +++++++++- include/linux/mfd/max8998.h | 11 +- 6 files changed, 358 insertions(+), 9 deletions(-) create mode 100644 drivers/mfd/max8998-irq.c (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 02cd6c0e59cb..8a463e6c22a0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -295,7 +295,7 @@ config MFD_MAX8925 config MFD_MAX8998 bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE help Say yes here to support for Maxim Semiconductor MAX8998 and diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index feaeeaeeddb7..b1ad74da2351 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -58,7 +58,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o -obj-$(CONFIG_MFD_MAX8998) += max8998.o +obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o pcf50633-objs := pcf50633-core.o pcf50633-irq.o obj-$(CONFIG_MFD_PCF50633) += pcf50633.o diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c new file mode 100644 index 000000000000..346fd296cf7d --- /dev/null +++ b/drivers/mfd/max8998-irq.c @@ -0,0 +1,255 @@ +/* + * Interrupt controller support for MAX8998 + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * 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 +#include +#include +#include + +struct max8998_irq_data { + int reg; + int mask; +}; + +static struct max8998_irq_data max8998_irqs[] = { + [MAX8998_IRQ_DCINF] = { + .reg = 1, + .mask = MAX8998_IRQ_DCINF_MASK, + }, + [MAX8998_IRQ_DCINR] = { + .reg = 1, + .mask = MAX8998_IRQ_DCINR_MASK, + }, + [MAX8998_IRQ_JIGF] = { + .reg = 1, + .mask = MAX8998_IRQ_JIGF_MASK, + }, + [MAX8998_IRQ_JIGR] = { + .reg = 1, + .mask = MAX8998_IRQ_JIGR_MASK, + }, + [MAX8998_IRQ_PWRONF] = { + .reg = 1, + .mask = MAX8998_IRQ_PWRONF_MASK, + }, + [MAX8998_IRQ_PWRONR] = { + .reg = 1, + .mask = MAX8998_IRQ_PWRONR_MASK, + }, + [MAX8998_IRQ_WTSREVNT] = { + .reg = 2, + .mask = MAX8998_IRQ_WTSREVNT_MASK, + }, + [MAX8998_IRQ_SMPLEVNT] = { + .reg = 2, + .mask = MAX8998_IRQ_SMPLEVNT_MASK, + }, + [MAX8998_IRQ_ALARM1] = { + .reg = 2, + .mask = MAX8998_IRQ_ALARM1_MASK, + }, + [MAX8998_IRQ_ALARM0] = { + .reg = 2, + .mask = MAX8998_IRQ_ALARM0_MASK, + }, + [MAX8998_IRQ_ONKEY1S] = { + .reg = 3, + .mask = MAX8998_IRQ_ONKEY1S_MASK, + }, + [MAX8998_IRQ_TOPOFFR] = { + .reg = 3, + .mask = MAX8998_IRQ_TOPOFFR_MASK, + }, + [MAX8998_IRQ_DCINOVPR] = { + .reg = 3, + .mask = MAX8998_IRQ_DCINOVPR_MASK, + }, + [MAX8998_IRQ_CHGRSTF] = { + .reg = 3, + .mask = MAX8998_IRQ_CHGRSTF_MASK, + }, + [MAX8998_IRQ_DONER] = { + .reg = 3, + .mask = MAX8998_IRQ_DONER_MASK, + }, + [MAX8998_IRQ_CHGFAULT] = { + .reg = 3, + .mask = MAX8998_IRQ_CHGFAULT_MASK, + }, + [MAX8998_IRQ_LOBAT1] = { + .reg = 4, + .mask = MAX8998_IRQ_LOBAT1_MASK, + }, + [MAX8998_IRQ_LOBAT2] = { + .reg = 4, + .mask = MAX8998_IRQ_LOBAT2_MASK, + }, +}; + +static inline struct max8998_irq_data * +irq_to_max8998_irq(struct max8998_dev *max8998, int irq) +{ + return &max8998_irqs[irq - max8998->irq_base]; +} + +static void max8998_irq_lock(unsigned int irq) +{ + struct max8998_dev *max8998 = get_irq_chip_data(irq); + + mutex_lock(&max8998->irqlock); +} + +static void max8998_irq_sync_unlock(unsigned int irq) +{ + struct max8998_dev *max8998 = get_irq_chip_data(irq); + int i; + + for (i = 0; i < ARRAY_SIZE(max8998->irq_masks_cur); i++) { + /* + * If there's been a change in the mask write it back + * to the hardware. + */ + if (max8998->irq_masks_cur[i] != max8998->irq_masks_cache[i]) { + max8998->irq_masks_cache[i] = max8998->irq_masks_cur[i]; + max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i, + max8998->irq_masks_cur[i]); + } + } + + mutex_unlock(&max8998->irqlock); +} + +static void max8998_irq_unmask(unsigned int irq) +{ + struct max8998_dev *max8998 = get_irq_chip_data(irq); + struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq); + + max8998->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; +} + +static void max8998_irq_mask(unsigned int irq) +{ + struct max8998_dev *max8998 = get_irq_chip_data(irq); + struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq); + + max8998->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; +} + +static struct irq_chip max8998_irq_chip = { + .name = "max8998", + .bus_lock = max8998_irq_lock, + .bus_sync_unlock = max8998_irq_sync_unlock, + .mask = max8998_irq_mask, + .unmask = max8998_irq_unmask, +}; + +static irqreturn_t max8998_irq_thread(int irq, void *data) +{ + struct max8998_dev *max8998 = data; + u8 irq_reg[MAX8998_NUM_IRQ_REGS]; + int ret; + int i; + + ret = max8998_bulk_read(max8998->i2c, MAX8998_REG_IRQ1, + MAX8998_NUM_IRQ_REGS, irq_reg); + if (ret < 0) { + dev_err(max8998->dev, "Failed to read interrupt register: %d\n", + ret); + return IRQ_NONE; + } + + /* Apply masking */ + for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++) + irq_reg[i] &= ~max8998->irq_masks_cur[i]; + + /* Report */ + for (i = 0; i < MAX8998_IRQ_NR; i++) { + if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask) + handle_nested_irq(max8998->irq_base + i); + } + + return IRQ_HANDLED; +} + +int max8998_irq_init(struct max8998_dev *max8998) +{ + int i; + int cur_irq; + int ret; + + if (!max8998->irq) { + dev_warn(max8998->dev, + "No interrupt specified, no interrupts\n"); + max8998->irq_base = 0; + return 0; + } + + if (!max8998->irq_base) { + dev_err(max8998->dev, + "No interrupt base specified, no interrupts\n"); + return 0; + } + + mutex_init(&max8998->irqlock); + + /* Mask the individual interrupt sources */ + for (i = 0; i < MAX8998_NUM_IRQ_REGS; i++) { + max8998->irq_masks_cur[i] = 0xff; + max8998->irq_masks_cache[i] = 0xff; + max8998_write_reg(max8998->i2c, MAX8998_REG_IRQM1 + i, 0xff); + } + + max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff); + max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff); + + /* register with genirq */ + for (i = 0; i < MAX8998_IRQ_NR; i++) { + cur_irq = i + max8998->irq_base; + set_irq_chip_data(cur_irq, max8998); + set_irq_chip_and_handler(cur_irq, &max8998_irq_chip, + handle_edge_irq); + set_irq_nested_thread(cur_irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + set_irq_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max8998-irq", max8998); + if (ret) { + dev_err(max8998->dev, "Failed to request IRQ %d: %d\n", + max8998->irq, ret); + return ret; + } + + if (!max8998->ono) + return 0; + + ret = request_threaded_irq(max8998->ono, NULL, max8998_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "max8998-ono", max8998); + if (ret) + dev_err(max8998->dev, "Failed to request IRQ %d: %d\n", + max8998->ono, ret); + + return 0; +} + +void max8998_irq_exit(struct max8998_dev *max8998) +{ + if (max8998->irq) + free_irq(max8998->irq, max8998); +} diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 3b8a5076d351..49c140a78ba9 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -1,5 +1,5 @@ /* - * max8698.c - mfd core driver for the Maxim 8998 + * max8998.c - mfd core driver for the Maxim 8998 * * Copyright (C) 2009-2010 Samsung Electronics * Kyungmin Park @@ -53,6 +53,21 @@ int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) } EXPORT_SYMBOL(max8998_read_reg); +int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&max8998->iolock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(max8998_bulk_read); + int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value) { struct max8998_dev *max8998 = i2c_get_clientdata(i2c); @@ -87,6 +102,7 @@ EXPORT_SYMBOL(max8998_update_reg); static int max8998_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct max8998_platform_data *pdata = i2c->dev.platform_data; struct max8998_dev *max8998; int ret = 0; @@ -97,8 +113,15 @@ static int max8998_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max8998); max8998->dev = &i2c->dev; max8998->i2c = i2c; + max8998->irq = i2c->irq; + if (pdata) { + max8998->ono = pdata->ono; + max8998->irq_base = pdata->irq_base; + } mutex_init(&max8998->iolock); + max8998_irq_init(max8998); + ret = mfd_add_devices(max8998->dev, -1, max8998_devs, ARRAY_SIZE(max8998_devs), NULL, 0); diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index f0a20cdc288c..3bd2371a05f7 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -1,5 +1,5 @@ /* - * max8698.h - Voltage regulator driver for the Maxim 8998 + * max8998.h - Voltage regulator driver for the Maxim 8998 * * Copyright (C) 2009-2010 Samsung Electrnoics * Kyungmin Park @@ -23,6 +23,8 @@ #ifndef __LINUX_MFD_MAX8998_PRIV_H #define __LINUX_MFD_MAX8998_PRIV_H +#define MAX8998_NUM_IRQ_REGS 4 + /* MAX 8998 registers */ enum { MAX8998_REG_IRQ1, @@ -72,20 +74,86 @@ enum { MAX8998_REG_LBCNFG2, }; +/* IRQ definitions */ +enum { + MAX8998_IRQ_DCINF, + MAX8998_IRQ_DCINR, + MAX8998_IRQ_JIGF, + MAX8998_IRQ_JIGR, + MAX8998_IRQ_PWRONF, + MAX8998_IRQ_PWRONR, + + MAX8998_IRQ_WTSREVNT, + MAX8998_IRQ_SMPLEVNT, + MAX8998_IRQ_ALARM1, + MAX8998_IRQ_ALARM0, + + MAX8998_IRQ_ONKEY1S, + MAX8998_IRQ_TOPOFFR, + MAX8998_IRQ_DCINOVPR, + MAX8998_IRQ_CHGRSTF, + MAX8998_IRQ_DONER, + MAX8998_IRQ_CHGFAULT, + + MAX8998_IRQ_LOBAT1, + MAX8998_IRQ_LOBAT2, + + MAX8998_IRQ_NR, +}; + +#define MAX8998_IRQ_DCINF_MASK (1 << 2) +#define MAX8998_IRQ_DCINR_MASK (1 << 3) +#define MAX8998_IRQ_JIGF_MASK (1 << 4) +#define MAX8998_IRQ_JIGR_MASK (1 << 5) +#define MAX8998_IRQ_PWRONF_MASK (1 << 6) +#define MAX8998_IRQ_PWRONR_MASK (1 << 7) + +#define MAX8998_IRQ_WTSREVNT_MASK (1 << 0) +#define MAX8998_IRQ_SMPLEVNT_MASK (1 << 1) +#define MAX8998_IRQ_ALARM1_MASK (1 << 2) +#define MAX8998_IRQ_ALARM0_MASK (1 << 3) + +#define MAX8998_IRQ_ONKEY1S_MASK (1 << 0) +#define MAX8998_IRQ_TOPOFFR_MASK (1 << 2) +#define MAX8998_IRQ_DCINOVPR_MASK (1 << 3) +#define MAX8998_IRQ_CHGRSTF_MASK (1 << 4) +#define MAX8998_IRQ_DONER_MASK (1 << 5) +#define MAX8998_IRQ_CHGFAULT_MASK (1 << 7) + +#define MAX8998_IRQ_LOBAT1_MASK (1 << 0) +#define MAX8998_IRQ_LOBAT2_MASK (1 << 1) + /** * struct max8998_dev - max8998 master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) * @i2c: i2c client private data * @iolock: mutex for serializing io access + * @irqlock: mutex for buslock + * @irq_base: base IRQ number for max8998, required for IRQs + * @irq: generic IRQ number for max8998 + * @ono: power onoff IRQ number for max8998 + * @irq_masks_cur: currently active value + * @irq_masks_cache: cached hardware value */ - struct max8998_dev { struct device *dev; struct i2c_client *i2c; struct mutex iolock; + struct mutex irqlock; + + int irq_base; + int irq; + int ono; + u8 irq_masks_cur[MAX8998_NUM_IRQ_REGS]; + u8 irq_masks_cache[MAX8998_NUM_IRQ_REGS]; }; +int max8998_irq_init(struct max8998_dev *max8998); +void max8998_irq_exit(struct max8998_dev *max8998); + extern int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +extern int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); extern int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value); extern int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index 1d3601a2d853..d47ed4c190fe 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -1,5 +1,5 @@ /* - * max8698.h - Voltage regulator driver for the Maxim 8998 + * max8998.h - Voltage regulator driver for the Maxim 8998 * * Copyright (C) 2009-2010 Samsung Electrnoics * Kyungmin Park @@ -66,13 +66,16 @@ struct max8998_regulator_data { /** * struct max8998_board - packages regulator init data - * @num_regulators: number of regultors used * @regulators: array of defined regulators + * @num_regulators: number of regultors used + * @irq_base: base IRQ number for max8998, required for IRQs + * @ono: power onoff IRQ number for max8998 */ - struct max8998_platform_data { - int num_regulators; struct max8998_regulator_data *regulators; + int num_regulators; + int irq_base; + int ono; }; #endif /* __LINUX_MFD_MAX8998_H */ -- cgit v1.2.3 From 9b16c0a43b74393cc18666a7748293812c61af1f Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 6 Aug 2010 11:28:08 +0900 Subject: rtc: Add MAX8998 rtc driver This adds support for the RTC provided by the Maxim 8998 chip. This driver was tested on a GONI board by using the rtc-test application from the Documentation/rtc.txt. Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Acked-by: Alessandro Zummo Signed-off-by: Samuel Ortiz --- drivers/mfd/max8998.c | 24 ++- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-max8998.c | 300 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/max8998-private.h | 6 +- 5 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 drivers/rtc/rtc-max8998.c (limited to 'include/linux') diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 49c140a78ba9..310fd8054f35 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -30,10 +30,14 @@ #include #include +#define RTC_I2C_ADDR (0x0c >> 1) + static struct mfd_cell max8998_devs[] = { { .name = "max8998-pmic", - } + }, { + .name = "max8998-rtc", + }, }; int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) @@ -80,6 +84,21 @@ int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value) } EXPORT_SYMBOL(max8998_write_reg); +int max8998_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8998->iolock); + ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&max8998->iolock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(max8998_bulk_write); + int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) { struct max8998_dev *max8998 = i2c_get_clientdata(i2c); @@ -120,6 +139,9 @@ static int max8998_i2c_probe(struct i2c_client *i2c, } mutex_init(&max8998->iolock); + max8998->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); + i2c_set_clientdata(max8998->rtc, max8998); + max8998_irq_init(max8998); ret = mfd_add_devices(max8998->dev, -1, diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6a77437d4f5a..23579d6e4a6e 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -196,6 +196,16 @@ config RTC_DRV_MAX8925 This driver can also be built as a module. If so, the module will be called rtc-max8925. +config RTC_DRV_MAX8998 + tristate "Maxim MAX8998" + depends on MFD_MAX8998 + help + If you say yes here you will get support for the + RTC of Maxim MAX8998 PMIC. + + This driver can also be built as a module. If so, the module + will be called rtc-max8998. + config RTC_DRV_RS5C372 tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 7a7cb3228a1d..c3f3c2d9e1ed 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_MXC) += rtc-mxc.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o +obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c new file mode 100644 index 000000000000..f22dee35f330 --- /dev/null +++ b/drivers/rtc/rtc-max8998.c @@ -0,0 +1,300 @@ +/* + * RTC driver for Maxim MAX8998 + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Minkyu Kang + * Author: Joonyoung Shim + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define MAX8998_RTC_SEC 0x00 +#define MAX8998_RTC_MIN 0x01 +#define MAX8998_RTC_HOUR 0x02 +#define MAX8998_RTC_WEEKDAY 0x03 +#define MAX8998_RTC_DATE 0x04 +#define MAX8998_RTC_MONTH 0x05 +#define MAX8998_RTC_YEAR1 0x06 +#define MAX8998_RTC_YEAR2 0x07 +#define MAX8998_ALARM0_SEC 0x08 +#define MAX8998_ALARM0_MIN 0x09 +#define MAX8998_ALARM0_HOUR 0x0a +#define MAX8998_ALARM0_WEEKDAY 0x0b +#define MAX8998_ALARM0_DATE 0x0c +#define MAX8998_ALARM0_MONTH 0x0d +#define MAX8998_ALARM0_YEAR1 0x0e +#define MAX8998_ALARM0_YEAR2 0x0f +#define MAX8998_ALARM1_SEC 0x10 +#define MAX8998_ALARM1_MIN 0x11 +#define MAX8998_ALARM1_HOUR 0x12 +#define MAX8998_ALARM1_WEEKDAY 0x13 +#define MAX8998_ALARM1_DATE 0x14 +#define MAX8998_ALARM1_MONTH 0x15 +#define MAX8998_ALARM1_YEAR1 0x16 +#define MAX8998_ALARM1_YEAR2 0x17 +#define MAX8998_ALARM0_CONF 0x18 +#define MAX8998_ALARM1_CONF 0x19 +#define MAX8998_RTC_STATUS 0x1a +#define MAX8998_WTSR_SMPL_CNTL 0x1b +#define MAX8998_TEST 0x1f + +#define HOUR_12 (1 << 7) +#define HOUR_PM (1 << 5) +#define ALARM0_STATUS (1 << 1) +#define ALARM1_STATUS (1 << 2) + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, +}; + +struct max8998_rtc_info { + struct device *dev; + struct max8998_dev *max8998; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + int irq; +}; + +static void max8998_data_to_tm(u8 *data, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(data[RTC_SEC]); + tm->tm_min = bcd2bin(data[RTC_MIN]); + if (data[RTC_HOUR] & HOUR_12) { + tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f); + if (data[RTC_HOUR] & HOUR_PM) + tm->tm_hour += 12; + } else + tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); + + tm->tm_wday = data[RTC_WEEKDAY] & 0x07; + tm->tm_mday = bcd2bin(data[RTC_DATE]); + tm->tm_mon = bcd2bin(data[RTC_MONTH]); + tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100; + tm->tm_year -= 1900; +} + +static void max8998_tm_to_data(struct rtc_time *tm, u8 *data) +{ + data[RTC_SEC] = bin2bcd(tm->tm_sec); + data[RTC_MIN] = bin2bcd(tm->tm_min); + data[RTC_HOUR] = bin2bcd(tm->tm_hour); + data[RTC_WEEKDAY] = tm->tm_wday; + data[RTC_DATE] = bin2bcd(tm->tm_mday); + data[RTC_MONTH] = bin2bcd(tm->tm_mon); + data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100); + data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100); +} + +static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + int ret; + + ret = max8998_bulk_read(info->rtc, MAX8998_RTC_SEC, 8, data); + if (ret < 0) + return ret; + + max8998_data_to_tm(data, tm); + + return rtc_valid_tm(tm); +} + +static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + + max8998_tm_to_data(tm, data); + + return max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data); +} + +static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + u8 val; + int ret; + + ret = max8998_bulk_read(info->rtc, MAX8998_ALARM0_SEC, 8, data); + if (ret < 0) + return ret; + + max8998_data_to_tm(data, &alrm->time); + + ret = max8998_read_reg(info->rtc, MAX8998_ALARM0_CONF, &val); + if (ret < 0) + return ret; + + alrm->enabled = !!val; + + ret = max8998_read_reg(info->rtc, MAX8998_RTC_STATUS, &val); + if (ret < 0) + return ret; + + if (val & ALARM0_STATUS) + alrm->pending = 1; + else + alrm->pending = 0; + + return 0; +} + +static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info) +{ + return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0); +} + +static int max8998_rtc_start_alarm(struct max8998_rtc_info *info) +{ + return max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0x77); +} + +static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + u8 data[8]; + int ret; + + max8998_tm_to_data(&alrm->time, data); + + ret = max8998_rtc_stop_alarm(info); + if (ret < 0) + return ret; + + ret = max8998_bulk_write(info->rtc, MAX8998_ALARM0_SEC, 8, data); + if (ret < 0) + return ret; + + if (alrm->enabled) + return max8998_rtc_start_alarm(info); + + return 0; +} + +static int max8998_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct max8998_rtc_info *info = dev_get_drvdata(dev); + + if (enabled) + return max8998_rtc_start_alarm(info); + else + return max8998_rtc_stop_alarm(info); +} + +static irqreturn_t max8998_rtc_alarm_irq(int irq, void *data) +{ + struct max8998_rtc_info *info = data; + + rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops max8998_rtc_ops = { + .read_time = max8998_rtc_read_time, + .set_time = max8998_rtc_set_time, + .read_alarm = max8998_rtc_read_alarm, + .set_alarm = max8998_rtc_set_alarm, + .alarm_irq_enable = max8998_rtc_alarm_irq_enable, +}; + +static int __devinit max8998_rtc_probe(struct platform_device *pdev) +{ + struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent); + struct max8998_rtc_info *info; + int ret; + + info = kzalloc(sizeof(struct max8998_rtc_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + info->max8998 = max8998; + info->rtc = max8998->rtc; + info->irq = max8998->irq_base + MAX8998_IRQ_ALARM0; + + info->rtc_dev = rtc_device_register("max8998-rtc", &pdev->dev, + &max8998_rtc_ops, THIS_MODULE); + + if (IS_ERR(info->rtc_dev)) { + ret = PTR_ERR(info->rtc_dev); + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + goto out_rtc; + } + + platform_set_drvdata(pdev, info); + + ret = request_threaded_irq(info->irq, NULL, max8998_rtc_alarm_irq, 0, + "rtc-alarm0", info); + if (ret < 0) + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + info->irq, ret); + + return 0; + +out_rtc: + kfree(info); + return ret; +} + +static int __devexit max8998_rtc_remove(struct platform_device *pdev) +{ + struct max8998_rtc_info *info = platform_get_drvdata(pdev); + + if (info) { + free_irq(info->irq, info); + rtc_device_unregister(info->rtc_dev); + kfree(info); + } + + return 0; +} + +static struct platform_driver max8998_rtc_driver = { + .driver = { + .name = "max8998-rtc", + .owner = THIS_MODULE, + }, + .probe = max8998_rtc_probe, + .remove = __devexit_p(max8998_rtc_remove), +}; + +static int __init max8998_rtc_init(void) +{ + return platform_driver_register(&max8998_rtc_driver); +} +module_init(max8998_rtc_init); + +static void __exit max8998_rtc_exit(void) +{ + platform_driver_unregister(&max8998_rtc_driver); +} +module_exit(max8998_rtc_exit); + +MODULE_AUTHOR("Minkyu Kang "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("Maxim MAX8998 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index 3bd2371a05f7..170f665c7cdd 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -126,7 +126,8 @@ enum { /** * struct max8998_dev - max8998 master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) - * @i2c: i2c client private data + * @i2c: i2c client private data for regulator + * @rtc: i2c client private data for rtc * @iolock: mutex for serializing io access * @irqlock: mutex for buslock * @irq_base: base IRQ number for max8998, required for IRQs @@ -138,6 +139,7 @@ enum { struct max8998_dev { struct device *dev; struct i2c_client *i2c; + struct i2c_client *rtc; struct mutex iolock; struct mutex irqlock; @@ -155,6 +157,8 @@ extern int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); extern int max8998_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf); extern int max8998_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +extern int max8998_bulk_write(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); extern int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); #endif /* __LINUX_MFD_MAX8998_PRIV_H */ -- cgit v1.2.3 From 19ca7502c508595edfb963e5dbcf62854a926506 Mon Sep 17 00:00:00 2001 From: Arnd Hannemann Date: Tue, 24 Aug 2010 17:26:59 +0200 Subject: mmc: Allow the tmio_mmc mfd driver to specify get_cd handler Some controllers, supported by the tmio_mmc driver do not have the card detect pin of a slot connected, so that polling needs to be used and card detection is handled by other means. This patch exposes a get_cd hook for that purpose. Signed-off-by: Arnd Hannemann Signed-off-by: Samuel Ortiz --- drivers/mmc/host/tmio_mmc.c | 13 +++++++++++++ include/linux/mfd/tmio.h | 1 + 2 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 69d98e3bf6ab..1a47221d01a4 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -756,10 +756,23 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)) ? 0 : 1; } +static int tmio_mmc_get_cd(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct mfd_cell *cell = host->pdev->dev.platform_data; + struct tmio_mmc_data *pdata = cell->driver_data; + + if (!pdata->get_cd) + return -ENOSYS; + else + return pdata->get_cd(host->pdev); +} + static const struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, .get_ro = tmio_mmc_get_ro, + .get_cd = tmio_mmc_get_cd, }; #ifdef CONFIG_PM diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index f07425bc3dcd..24c43bbad541 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -74,6 +74,7 @@ struct tmio_mmc_data { struct tmio_mmc_dma *dma; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); + int (*get_cd)(struct platform_device *host); }; /* -- cgit v1.2.3 From 998283e2e359249133f2f47db26669a55ff25c98 Mon Sep 17 00:00:00 2001 From: Arnd Hannemann Date: Tue, 24 Aug 2010 17:27:00 +0200 Subject: mfd: Allow the platform to specify the sh_mobile_sdhi get_cd handler On some platforms (e.g. AP4EVB) the card detect pin of a slot is not directly connected to the sdhi hardware, so that polling needs to be used with tmio_mmc and card detection is handled in the platform code. This patch allows to set tmio_mmc capabilities (to pass the MMC_CAP_NEEDS_POLL flag) and exposes a get_cd hook for that purpose. Signed-off-by: Arnd Hannemann Signed-off-by: Samuel Ortiz --- drivers/mfd/sh_mobile_sdhi.c | 13 +++++++++++++ include/linux/mfd/sh_mobile_sdhi.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c index 49b4d069cbf9..01d83a41d570 100644 --- a/drivers/mfd/sh_mobile_sdhi.c +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -65,6 +65,17 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state) p->set_pwr(pdev, state); } +static int sh_mobile_sdhi_get_cd(struct platform_device *tmio) +{ + struct platform_device *pdev = to_platform_device(tmio->dev.parent); + struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; + + if (p && p->get_cd) + return p->get_cd(pdev); + else + return -ENOSYS; +} + static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) { struct sh_mobile_sdhi *priv; @@ -106,10 +117,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->hclk = clk_get_rate(priv->clk); mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; + mmc_data->get_cd = sh_mobile_sdhi_get_cd; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; if (p) { mmc_data->flags = p->tmio_flags; mmc_data->ocr_mask = p->tmio_ocr_mask; + mmc_data->capabilities |= p->tmio_caps; } if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) { diff --git a/include/linux/mfd/sh_mobile_sdhi.h b/include/linux/mfd/sh_mobile_sdhi.h index 49067802a6d7..c981b959760f 100644 --- a/include/linux/mfd/sh_mobile_sdhi.h +++ b/include/linux/mfd/sh_mobile_sdhi.h @@ -7,8 +7,10 @@ struct sh_mobile_sdhi_info { int dma_slave_tx; int dma_slave_rx; unsigned long tmio_flags; + unsigned long tmio_caps; u32 tmio_ocr_mask; /* available MMC voltages */ void (*set_pwr)(struct platform_device *pdev, int state); + int (*get_cd)(struct platform_device *pdev); }; #endif /* __SH_MOBILE_SDHI_H__ */ -- cgit v1.2.3 From 777271d0f33da306575ef776c75f66fc27246bf0 Mon Sep 17 00:00:00 2001 From: Arnd Hannemann Date: Tue, 24 Aug 2010 17:27:01 +0200 Subject: mmc: Allow the platform to specify the sh_mmcif get_cd handler In some platforms (e.g. AP4EVB) the card detect pin of a slot is not directly connected to the sh_mmcif controller, so that polling needs to be used. To overcome the overhead induced by querying the controller on each poll cycle, card detection can be handled in the platform code more efficiently. This patch exposes a get_cd hook for that purpose. Signed-off-by: Arnd Hannemann Tested-by: Yusuke Goda Signed-off-by: Samuel Ortiz --- drivers/mmc/host/sh_mmcif.c | 12 ++++++++++++ include/linux/mmc/sh_mmcif.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include/linux') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 0f06b8002814..ddd09840520b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -710,9 +710,21 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->bus_width = ios->bus_width; } +static int sh_mmcif_get_cd(struct mmc_host *mmc) +{ + struct sh_mmcif_host *host = mmc_priv(mmc); + struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + + if (!p->get_cd) + return -ENOSYS; + else + return p->get_cd(host->pd); +} + static struct mmc_host_ops sh_mmcif_ops = { .request = sh_mmcif_request, .set_ios = sh_mmcif_set_ios, + .get_cd = sh_mmcif_get_cd, }; static void sh_mmcif_detect(struct mmc_host *mmc) diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index d4a2ebbdab4b..d19e2114fd86 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -34,6 +34,7 @@ struct sh_mmcif_plat_data { void (*set_pwr)(struct platform_device *pdev, int state); void (*down_pwr)(struct platform_device *pdev); + int (*get_cd)(struct platform_device *pdef); u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ unsigned long caps; u32 ocr; -- cgit v1.2.3 From f1334fb3c3006ba109886158c0ad79512f928bc1 Mon Sep 17 00:00:00 2001 From: Yusuke Goda Date: Mon, 30 Aug 2010 11:50:19 +0100 Subject: mmc: Allow 2 byte requests in 4-bit mode for tmio_mmc Adjust the tmio_mmc block size check to accept 2-byte requests in 4-bit mode if the hardware supports it. Tested with the SDHI hardware block included in sh7724. Signed-off-by: Yusuke Goda Signed-off-by: Matt Fleming Acked-by: Magnus Damm Tested-by: Arnd Hannemann Signed-off-by: Samuel Ortiz --- drivers/mfd/sh_mobile_sdhi.c | 6 ++++++ drivers/mmc/host/tmio_mmc.c | 17 ++++++++++++----- include/linux/mfd/tmio.h | 5 +++++ 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c index 01d83a41d570..f1714f93af9d 100644 --- a/drivers/mfd/sh_mobile_sdhi.c +++ b/drivers/mfd/sh_mobile_sdhi.c @@ -125,6 +125,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->capabilities |= p->tmio_caps; } + /* + * All SDHI blocks support 2-byte and larger block sizes in 4-bit + * bus width mode. + */ + mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; + if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) { priv->param_tx.slave_id = p->dma_slave_tx; priv->param_rx.slave_id = p->dma_slave_rx; diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 1a47221d01a4..e7765a89593e 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -658,14 +658,21 @@ static void tmio_mmc_release_dma(struct tmio_mmc_host *host) static int tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data) { + struct mfd_cell *cell = host->pdev->dev.platform_data; + struct tmio_mmc_data *pdata = cell->driver_data; + pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n", data->blksz, data->blocks); - /* Hardware cannot perform 1 and 2 byte requests in 4 bit mode */ - if (data->blksz < 4 && host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) { - pr_err("%s: %d byte block unsupported in 4 bit mode\n", - mmc_hostname(host->mmc), data->blksz); - return -EINVAL; + /* Some hardware cannot perform 2 byte requests in 4 bit mode */ + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES; + + if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) { + pr_err("%s: %d byte block unsupported in 4 bit mode\n", + mmc_hostname(host->mmc), data->blksz); + return -EINVAL; + } } tmio_mmc_init_sg(host, data); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 24c43bbad541..085f041197dc 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -52,6 +52,11 @@ /* tmio MMC platform flags */ #define TMIO_MMC_WRPROTECT_DISABLE (1 << 0) +/* + * Some controllers can support a 2-byte block size when the bus width + * is configured in 4-bit mode. + */ +#define TMIO_MMC_BLKSZ_2BYTES (1 << 1) int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); -- cgit v1.2.3 From 47c1697508f2ec9f6b31ce6c825fe1017871dea6 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Fri, 10 Sep 2010 17:47:56 +0200 Subject: mfd: Align ab8500 with the abx500 interface This patch makes the ab8500 mixed signal chip expose the same interface for register access as the ab3100, ab3550 and ab5500 chip. The ab8500_read() and ab8500_write() is removed and replaced with abx500_get_register_interruptible() and abx500_set_register_interruptible(). Signed-off-by: Mattias Wallin Acked-by: Linus Walleij Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 4 +- drivers/mfd/ab8500-core.c | 281 ++++++++++++++++++++++++++------------------- drivers/regulator/ab8500.c | 86 ++++++++------ drivers/rtc/rtc-ab8500.c | 103 +++++++++-------- include/linux/mfd/ab8500.h | 28 ++++- include/linux/mfd/abx500.h | 3 +- 6 files changed, 296 insertions(+), 209 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 8a463e6c22a0..ec0af47a9058 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -434,7 +434,7 @@ config PCF50633_GPIO config ABX500_CORE bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" - default y if ARCH_U300 + default y if ARCH_U300 || ARCH_U8500 help Say yes here if you have the ABX500 Mixed Signal IC family chips. This core driver expose register access functions. @@ -475,7 +475,7 @@ config EZX_PCAP config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" - depends on SPI=y && GENERIC_HARDIRQS + depends on SPI=y && GENERIC_HARDIRQS && ABX500_CORE select MFD_CORE help Select this option to enable access to AB8500 power management diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index b8c4b80e4c43..373783146b5e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -4,6 +4,7 @@ * License Terms: GNU General Public License v2 * Author: Srinidhi Kasagar * Author: Rabin Vincent + * Changes: Mattias Wallin */ #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -22,71 +24,71 @@ * Interrupt register offsets * Bank : 0x0E */ -#define AB8500_IT_SOURCE1_REG 0x0E00 -#define AB8500_IT_SOURCE2_REG 0x0E01 -#define AB8500_IT_SOURCE3_REG 0x0E02 -#define AB8500_IT_SOURCE4_REG 0x0E03 -#define AB8500_IT_SOURCE5_REG 0x0E04 -#define AB8500_IT_SOURCE6_REG 0x0E05 -#define AB8500_IT_SOURCE7_REG 0x0E06 -#define AB8500_IT_SOURCE8_REG 0x0E07 -#define AB8500_IT_SOURCE19_REG 0x0E12 -#define AB8500_IT_SOURCE20_REG 0x0E13 -#define AB8500_IT_SOURCE21_REG 0x0E14 -#define AB8500_IT_SOURCE22_REG 0x0E15 -#define AB8500_IT_SOURCE23_REG 0x0E16 -#define AB8500_IT_SOURCE24_REG 0x0E17 +#define AB8500_IT_SOURCE1_REG 0x00 +#define AB8500_IT_SOURCE2_REG 0x01 +#define AB8500_IT_SOURCE3_REG 0x02 +#define AB8500_IT_SOURCE4_REG 0x03 +#define AB8500_IT_SOURCE5_REG 0x04 +#define AB8500_IT_SOURCE6_REG 0x05 +#define AB8500_IT_SOURCE7_REG 0x06 +#define AB8500_IT_SOURCE8_REG 0x07 +#define AB8500_IT_SOURCE19_REG 0x12 +#define AB8500_IT_SOURCE20_REG 0x13 +#define AB8500_IT_SOURCE21_REG 0x14 +#define AB8500_IT_SOURCE22_REG 0x15 +#define AB8500_IT_SOURCE23_REG 0x16 +#define AB8500_IT_SOURCE24_REG 0x17 /* * latch registers */ -#define AB8500_IT_LATCH1_REG 0x0E20 -#define AB8500_IT_LATCH2_REG 0x0E21 -#define AB8500_IT_LATCH3_REG 0x0E22 -#define AB8500_IT_LATCH4_REG 0x0E23 -#define AB8500_IT_LATCH5_REG 0x0E24 -#define AB8500_IT_LATCH6_REG 0x0E25 -#define AB8500_IT_LATCH7_REG 0x0E26 -#define AB8500_IT_LATCH8_REG 0x0E27 -#define AB8500_IT_LATCH9_REG 0x0E28 -#define AB8500_IT_LATCH10_REG 0x0E29 -#define AB8500_IT_LATCH19_REG 0x0E32 -#define AB8500_IT_LATCH20_REG 0x0E33 -#define AB8500_IT_LATCH21_REG 0x0E34 -#define AB8500_IT_LATCH22_REG 0x0E35 -#define AB8500_IT_LATCH23_REG 0x0E36 -#define AB8500_IT_LATCH24_REG 0x0E37 +#define AB8500_IT_LATCH1_REG 0x20 +#define AB8500_IT_LATCH2_REG 0x21 +#define AB8500_IT_LATCH3_REG 0x22 +#define AB8500_IT_LATCH4_REG 0x23 +#define AB8500_IT_LATCH5_REG 0x24 +#define AB8500_IT_LATCH6_REG 0x25 +#define AB8500_IT_LATCH7_REG 0x26 +#define AB8500_IT_LATCH8_REG 0x27 +#define AB8500_IT_LATCH9_REG 0x28 +#define AB8500_IT_LATCH10_REG 0x29 +#define AB8500_IT_LATCH19_REG 0x32 +#define AB8500_IT_LATCH20_REG 0x33 +#define AB8500_IT_LATCH21_REG 0x34 +#define AB8500_IT_LATCH22_REG 0x35 +#define AB8500_IT_LATCH23_REG 0x36 +#define AB8500_IT_LATCH24_REG 0x37 /* * mask registers */ -#define AB8500_IT_MASK1_REG 0x0E40 -#define AB8500_IT_MASK2_REG 0x0E41 -#define AB8500_IT_MASK3_REG 0x0E42 -#define AB8500_IT_MASK4_REG 0x0E43 -#define AB8500_IT_MASK5_REG 0x0E44 -#define AB8500_IT_MASK6_REG 0x0E45 -#define AB8500_IT_MASK7_REG 0x0E46 -#define AB8500_IT_MASK8_REG 0x0E47 -#define AB8500_IT_MASK9_REG 0x0E48 -#define AB8500_IT_MASK10_REG 0x0E49 -#define AB8500_IT_MASK11_REG 0x0E4A -#define AB8500_IT_MASK12_REG 0x0E4B -#define AB8500_IT_MASK13_REG 0x0E4C -#define AB8500_IT_MASK14_REG 0x0E4D -#define AB8500_IT_MASK15_REG 0x0E4E -#define AB8500_IT_MASK16_REG 0x0E4F -#define AB8500_IT_MASK17_REG 0x0E50 -#define AB8500_IT_MASK18_REG 0x0E51 -#define AB8500_IT_MASK19_REG 0x0E52 -#define AB8500_IT_MASK20_REG 0x0E53 -#define AB8500_IT_MASK21_REG 0x0E54 -#define AB8500_IT_MASK22_REG 0x0E55 -#define AB8500_IT_MASK23_REG 0x0E56 -#define AB8500_IT_MASK24_REG 0x0E57 - -#define AB8500_REV_REG 0x1080 +#define AB8500_IT_MASK1_REG 0x40 +#define AB8500_IT_MASK2_REG 0x41 +#define AB8500_IT_MASK3_REG 0x42 +#define AB8500_IT_MASK4_REG 0x43 +#define AB8500_IT_MASK5_REG 0x44 +#define AB8500_IT_MASK6_REG 0x45 +#define AB8500_IT_MASK7_REG 0x46 +#define AB8500_IT_MASK8_REG 0x47 +#define AB8500_IT_MASK9_REG 0x48 +#define AB8500_IT_MASK10_REG 0x49 +#define AB8500_IT_MASK11_REG 0x4A +#define AB8500_IT_MASK12_REG 0x4B +#define AB8500_IT_MASK13_REG 0x4C +#define AB8500_IT_MASK14_REG 0x4D +#define AB8500_IT_MASK15_REG 0x4E +#define AB8500_IT_MASK16_REG 0x4F +#define AB8500_IT_MASK17_REG 0x50 +#define AB8500_IT_MASK18_REG 0x51 +#define AB8500_IT_MASK19_REG 0x52 +#define AB8500_IT_MASK20_REG 0x53 +#define AB8500_IT_MASK21_REG 0x54 +#define AB8500_IT_MASK22_REG 0x55 +#define AB8500_IT_MASK23_REG 0x56 +#define AB8500_IT_MASK24_REG 0x57 + +#define AB8500_REV_REG 0x80 /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt @@ -99,96 +101,132 @@ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21, }; -static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data) +static int ab8500_get_chip_id(struct device *dev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); + return (int)ab8500->chip_id; +} + +static int set_register_interruptible(struct ab8500 *ab8500, u8 bank, + u8 reg, u8 data) { int ret; + /* + * Put the u8 bank and u8 register together into a an u16. + * The bank on higher 8 bits and register in lower 8 bits. + * */ + u16 addr = ((u16)bank) << 8 | reg; dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data); + ret = mutex_lock_interruptible(&ab8500->lock); + if (ret) + return ret; + ret = ab8500->write(ab8500, addr, data); if (ret < 0) dev_err(ab8500->dev, "failed to write reg %#x: %d\n", addr, ret); + mutex_unlock(&ab8500->lock); return ret; } -/** - * ab8500_write() - write an AB8500 register - * @ab8500: device to write to - * @addr: address of the register - * @data: value to write - */ -int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data) +static int ab8500_set_register(struct device *dev, u8 bank, + u8 reg, u8 value) { - int ret; - - mutex_lock(&ab8500->lock); - ret = __ab8500_write(ab8500, addr, data); - mutex_unlock(&ab8500->lock); + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return ret; + return set_register_interruptible(ab8500, bank, reg, value); } -EXPORT_SYMBOL_GPL(ab8500_write); -static int __ab8500_read(struct ab8500 *ab8500, u16 addr) +static int get_register_interruptible(struct ab8500 *ab8500, u8 bank, + u8 reg, u8 *value) { int ret; + /* put the u8 bank and u8 reg together into a an u16. + * bank on higher 8 bits and reg in lower */ + u16 addr = ((u16)bank) << 8 | reg; + + ret = mutex_lock_interruptible(&ab8500->lock); + if (ret) + return ret; ret = ab8500->read(ab8500, addr); if (ret < 0) dev_err(ab8500->dev, "failed to read reg %#x: %d\n", addr, ret); + else + *value = ret; + mutex_unlock(&ab8500->lock); dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret); return ret; } -/** - * ab8500_read() - read an AB8500 register - * @ab8500: device to read from - * @addr: address of the register - */ -int ab8500_read(struct ab8500 *ab8500, u16 addr) +static int ab8500_get_register(struct device *dev, u8 bank, + u8 reg, u8 *value) { - int ret; - - mutex_lock(&ab8500->lock); - ret = __ab8500_read(ab8500, addr); - mutex_unlock(&ab8500->lock); + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return ret; + return get_register_interruptible(ab8500, bank, reg, value); } -EXPORT_SYMBOL_GPL(ab8500_read); - -/** - * ab8500_set_bits() - set a bitfield in an AB8500 register - * @ab8500: device to read from - * @addr: address of the register - * @mask: mask of the bitfield to modify - * @data: value to set to the bitfield - */ -int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data) + +static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) { int ret; + u8 data; + /* put the u8 bank and u8 reg together into a an u16. + * bank on higher 8 bits and reg in lower */ + u16 addr = ((u16)bank) << 8 | reg; - mutex_lock(&ab8500->lock); + ret = mutex_lock_interruptible(&ab8500->lock); + if (ret) + return ret; - ret = __ab8500_read(ab8500, addr); - if (ret < 0) + ret = ab8500->read(ab8500, addr); + if (ret < 0) { + dev_err(ab8500->dev, "failed to read reg %#x: %d\n", + addr, ret); goto out; + } - ret &= ~mask; - ret |= data; + data = (u8)ret; + data = (~bitmask & data) | (bitmask & bitvalues); - ret = __ab8500_write(ab8500, addr, ret); + ret = ab8500->write(ab8500, addr, data); + if (ret < 0) + dev_err(ab8500->dev, "failed to write reg %#x: %d\n", + addr, ret); + dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data); out: mutex_unlock(&ab8500->lock); return ret; } -EXPORT_SYMBOL_GPL(ab8500_set_bits); + +static int ab8500_mask_and_set_register(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); + + return mask_and_set_register_interruptible(ab8500, bank, reg, + bitmask, bitvalues); + +} + +static struct abx500_ops ab8500_ops = { + .get_chip_id = ab8500_get_chip_id, + .get_register = ab8500_get_register, + .set_register = ab8500_set_register, + .get_register_page = NULL, + .set_register_page = NULL, + .mask_and_set_register = ab8500_mask_and_set_register, + .event_registers_startup_state_get = NULL, + .startup_irq_enabled = NULL, +}; static void ab8500_irq_lock(unsigned int irq) { @@ -213,7 +251,7 @@ static void ab8500_irq_sync_unlock(unsigned int irq) ab8500->oldmask[i] = new; reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; - ab8500_write(ab8500, reg, new); + set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new); } mutex_unlock(&ab8500->irq_lock); @@ -257,9 +295,11 @@ static irqreturn_t ab8500_irq(int irq, void *dev) for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { int regoffset = ab8500_irq_regoffset[i]; int status; + u8 value; - status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset); - if (status <= 0) + status = get_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_LATCH1_REG + regoffset, &value); + if (status < 0 || value == 0) continue; do { @@ -267,8 +307,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev) int line = i * 8 + bit; handle_nested_irq(ab8500->irq_base + line); - status &= ~(1 << bit); - } while (status); + value &= ~(1 << bit); + } while (value); } return IRQ_HANDLED; @@ -381,6 +421,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500) struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); int ret; int i; + u8 value; if (plat) ab8500->irq_base = plat->irq_base; @@ -388,7 +429,8 @@ int __devinit ab8500_init(struct ab8500 *ab8500) mutex_init(&ab8500->lock); mutex_init(&ab8500->irq_lock); - ret = ab8500_read(ab8500, AB8500_REV_REG); + ret = get_register_interruptible(ab8500, AB8500_MISC, + AB8500_REV_REG, &value); if (ret < 0) return ret; @@ -397,28 +439,37 @@ int __devinit ab8500_init(struct ab8500 *ab8500) * 0x10 - Cut 1.0 * 0x11 - Cut 1.1 */ - if (ret == 0x0 || ret == 0x10 || ret == 0x11) { - ab8500->revision = ret; - dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret); + if (value == 0x0 || value == 0x10 || value == 0x11) { + ab8500->revision = value; + dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); } else { - dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret); + dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); return -EINVAL; } + ab8500->chip_id = value; if (plat && plat->init) plat->init(ab8500); /* Clear and mask all interrupts */ for (i = 0; i < 10; i++) { - ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i); - ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff); + get_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_LATCH1_REG + i, &value); + set_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_MASK1_REG + i, 0xff); } for (i = 18; i < 24; i++) { - ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i); - ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff); + get_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_LATCH1_REG + i, &value); + set_register_interruptible(ab8500, AB8500_INTERRUPT, + AB8500_IT_MASK1_REG + i, 0xff); } + ret = abx500_register_ops(ab8500->dev, &ab8500_ops); + if (ret) + return ret; + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) ab8500->mask[i] = ab8500->oldmask[i] = 0xff; diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 28c7ae67cec9..db6b70f20511 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +34,11 @@ * @max_uV: maximum voltage (for variable voltage supplies) * @min_uV: minimum voltage (for variable voltage supplies) * @fixed_uV: typical voltage (for fixed voltage supplies) + * @update_bank: bank to control on/off * @update_reg: register to control on/off * @mask: mask to enable/disable regulator * @enable: bits to enable the regulator in normal(high power) mode + * @voltage_bank: bank to control regulator voltage * @voltage_reg: register to control regulator voltage * @voltage_mask: mask to control regulator voltage * @supported_voltages: supported voltage table @@ -49,11 +52,13 @@ struct ab8500_regulator_info { int max_uV; int min_uV; int fixed_uV; - int update_reg; - int mask; - int enable; - int voltage_reg; - int voltage_mask; + u8 update_bank; + u8 update_reg; + u8 mask; + u8 enable; + u8 voltage_bank; + u8 voltage_reg; + u8 voltage_mask; int const *supported_voltages; int voltages_len; }; @@ -97,8 +102,8 @@ static int ab8500_regulator_enable(struct regulator_dev *rdev) if (regulator_id >= AB8500_NUM_REGULATORS) return -EINVAL; - ret = ab8500_set_bits(info->ab8500, info->update_reg, - info->mask, info->enable); + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, info->mask, info->enable); if (ret < 0) dev_err(rdev_get_dev(rdev), "couldn't set enable bits for regulator\n"); @@ -114,8 +119,8 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev) if (regulator_id >= AB8500_NUM_REGULATORS) return -EINVAL; - ret = ab8500_set_bits(info->ab8500, info->update_reg, - info->mask, 0x0); + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->update_bank, info->update_reg, info->mask, 0x0); if (ret < 0) dev_err(rdev_get_dev(rdev), "couldn't set disable bits for regulator\n"); @@ -126,19 +131,21 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev) { int regulator_id, ret; struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 value; regulator_id = rdev_get_id(rdev); if (regulator_id >= AB8500_NUM_REGULATORS) return -EINVAL; - ret = ab8500_read(info->ab8500, info->update_reg); + ret = abx500_get_register_interruptible(info->dev, + info->update_bank, info->update_reg, &value); if (ret < 0) { dev_err(rdev_get_dev(rdev), "couldn't read 0x%x register\n", info->update_reg); return ret; } - if (ret & info->mask) + if (value & info->mask) return true; else return false; @@ -165,14 +172,16 @@ static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector) static int ab8500_regulator_get_voltage(struct regulator_dev *rdev) { - int regulator_id, ret, val; + int regulator_id, ret; struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + u8 value; regulator_id = rdev_get_id(rdev); if (regulator_id >= AB8500_NUM_REGULATORS) return -EINVAL; - ret = ab8500_read(info->ab8500, info->voltage_reg); + ret = abx500_get_register_interruptible(info->dev, info->voltage_bank, + info->voltage_reg, &value); if (ret < 0) { dev_err(rdev_get_dev(rdev), "couldn't read voltage reg for regulator\n"); @@ -180,11 +189,11 @@ static int ab8500_regulator_get_voltage(struct regulator_dev *rdev) } /* vintcore has a different layout */ - val = ret & info->voltage_mask; + value &= info->voltage_mask; if (regulator_id == AB8500_LDO_INTCORE) - ret = info->supported_voltages[val >> 0x3]; + ret = info->supported_voltages[value >> 0x3]; else - ret = info->supported_voltages[val]; + ret = info->supported_voltages[value]; return ret; } @@ -224,8 +233,9 @@ static int ab8500_regulator_set_voltage(struct regulator_dev *rdev, } /* set the registers for the request */ - ret = ab8500_set_bits(info->ab8500, info->voltage_reg, - info->voltage_mask, ret); + ret = abx500_mask_and_set_register_interruptible(info->dev, + info->voltage_bank, info->voltage_reg, + info->voltage_mask, (u8)ret); if (ret < 0) dev_err(rdev_get_dev(rdev), "couldn't set voltage reg for regulator\n"); @@ -262,9 +272,9 @@ static struct regulator_ops ab8500_ldo_fixed_ops = { .list_voltage = ab8500_list_voltage, }; -#define AB8500_LDO(_id, min, max, reg, reg_mask, reg_enable, \ - volt_reg, volt_mask, voltages, \ - len_volts) \ +#define AB8500_LDO(_id, min, max, bank, reg, reg_mask, \ + reg_enable, volt_bank, volt_reg, volt_mask, \ + voltages, len_volts) \ { \ .desc = { \ .name = "LDO-" #_id, \ @@ -275,9 +285,11 @@ static struct regulator_ops ab8500_ldo_fixed_ops = { }, \ .min_uV = (min) * 1000, \ .max_uV = (max) * 1000, \ + .update_bank = bank, \ .update_reg = reg, \ .mask = reg_mask, \ .enable = reg_enable, \ + .voltage_bank = volt_bank, \ .voltage_reg = volt_reg, \ .voltage_mask = volt_mask, \ .supported_voltages = voltages, \ @@ -285,8 +297,8 @@ static struct regulator_ops ab8500_ldo_fixed_ops = { .fixed_uV = 0, \ } -#define AB8500_FIXED_LDO(_id, fixed, reg, reg_mask, \ - reg_enable) \ +#define AB8500_FIXED_LDO(_id, fixed, bank, reg, \ + reg_mask, reg_enable) \ { \ .desc = { \ .name = "LDO-" #_id, \ @@ -296,6 +308,7 @@ static struct regulator_ops ab8500_ldo_fixed_ops = { .owner = THIS_MODULE, \ }, \ .fixed_uV = fixed * 1000, \ + .update_bank = bank, \ .update_reg = reg, \ .mask = reg_mask, \ .enable = reg_enable, \ @@ -304,28 +317,29 @@ static struct regulator_ops ab8500_ldo_fixed_ops = { static struct ab8500_regulator_info ab8500_regulator_info[] = { /* * Variable Voltage LDOs - * name, min uV, max uV, ctrl reg, reg mask, enable mask, - * volt ctrl reg, volt ctrl mask, volt table, num supported volts + * name, min uV, max uV, ctrl bank, ctrl reg, reg mask, enable mask, + * volt ctrl bank, volt ctrl reg, volt ctrl mask, volt table, + * num supported volts */ - AB8500_LDO(AUX1, 1100, 3300, 0x0409, 0x3, 0x1, 0x041f, 0xf, + AB8500_LDO(AUX1, 1100, 3300, 0x04, 0x09, 0x3, 0x1, 0x04, 0x1f, 0xf, ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), - AB8500_LDO(AUX2, 1100, 3300, 0x0409, 0xc, 0x4, 0x0420, 0xf, + AB8500_LDO(AUX2, 1100, 3300, 0x04, 0x09, 0xc, 0x4, 0x04, 0x20, 0xf, ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), - AB8500_LDO(AUX3, 1100, 3300, 0x040a, 0x3, 0x1, 0x0421, 0xf, + AB8500_LDO(AUX3, 1100, 3300, 0x04, 0x0a, 0x3, 0x1, 0x04, 0x21, 0xf, ldo_vauxn_voltages, ARRAY_SIZE(ldo_vauxn_voltages)), - AB8500_LDO(INTCORE, 1100, 3300, 0x0380, 0x4, 0x4, 0x0380, 0x38, + AB8500_LDO(INTCORE, 1100, 3300, 0x03, 0x80, 0x4, 0x4, 0x03, 0x80, 0x38, ldo_vintcore_voltages, ARRAY_SIZE(ldo_vintcore_voltages)), /* * Fixed Voltage LDOs - * name, o/p uV, ctrl reg, enable, disable + * name, o/p uV, ctrl bank, ctrl reg, enable, disable */ - AB8500_FIXED_LDO(TVOUT, 2000, 0x0380, 0x2, 0x2), - AB8500_FIXED_LDO(AUDIO, 2000, 0x0383, 0x2, 0x2), - AB8500_FIXED_LDO(ANAMIC1, 2050, 0x0383, 0x4, 0x4), - AB8500_FIXED_LDO(ANAMIC2, 2050, 0x0383, 0x8, 0x8), - AB8500_FIXED_LDO(DMIC, 1800, 0x0383, 0x10, 0x10), - AB8500_FIXED_LDO(ANA, 1200, 0x0383, 0xc, 0x4), + AB8500_FIXED_LDO(TVOUT, 2000, 0x03, 0x80, 0x2, 0x2), + AB8500_FIXED_LDO(AUDIO, 2000, 0x03, 0x83, 0x2, 0x2), + AB8500_FIXED_LDO(ANAMIC1, 2050, 0x03, 0x83, 0x4, 0x4), + AB8500_FIXED_LDO(ANAMIC2, 2050, 0x03, 0x83, 0x8, 0x8), + AB8500_FIXED_LDO(DMIC, 1800, 0x03, 0x83, 0x10, 0x10), + AB8500_FIXED_LDO(ANA, 1200, 0x03, 0x83, 0xc, 0x4), }; static inline struct ab8500_regulator_info *find_regulator_info(int id) diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 2fda03125e55..e346705aae92 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -14,26 +14,26 @@ #include #include #include +#include #include #include -#define AB8500_RTC_SOFF_STAT_REG 0x0F00 -#define AB8500_RTC_CC_CONF_REG 0x0F01 -#define AB8500_RTC_READ_REQ_REG 0x0F02 -#define AB8500_RTC_WATCH_TSECMID_REG 0x0F03 -#define AB8500_RTC_WATCH_TSECHI_REG 0x0F04 -#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x0F05 -#define AB8500_RTC_WATCH_TMIN_MID_REG 0x0F06 -#define AB8500_RTC_WATCH_TMIN_HI_REG 0x0F07 -#define AB8500_RTC_ALRM_MIN_LOW_REG 0x0F08 -#define AB8500_RTC_ALRM_MIN_MID_REG 0x0F09 -#define AB8500_RTC_ALRM_MIN_HI_REG 0x0F0A -#define AB8500_RTC_STAT_REG 0x0F0B -#define AB8500_RTC_BKUP_CHG_REG 0x0F0C -#define AB8500_RTC_FORCE_BKUP_REG 0x0F0D -#define AB8500_RTC_CALIB_REG 0x0F0E -#define AB8500_RTC_SWITCH_STAT_REG 0x0F0F -#define AB8500_REV_REG 0x1080 +#define AB8500_RTC_SOFF_STAT_REG 0x00 +#define AB8500_RTC_CC_CONF_REG 0x01 +#define AB8500_RTC_READ_REQ_REG 0x02 +#define AB8500_RTC_WATCH_TSECMID_REG 0x03 +#define AB8500_RTC_WATCH_TSECHI_REG 0x04 +#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x05 +#define AB8500_RTC_WATCH_TMIN_MID_REG 0x06 +#define AB8500_RTC_WATCH_TMIN_HI_REG 0x07 +#define AB8500_RTC_ALRM_MIN_LOW_REG 0x08 +#define AB8500_RTC_ALRM_MIN_MID_REG 0x09 +#define AB8500_RTC_ALRM_MIN_HI_REG 0x0A +#define AB8500_RTC_STAT_REG 0x0B +#define AB8500_RTC_BKUP_CHG_REG 0x0C +#define AB8500_RTC_FORCE_BKUP_REG 0x0D +#define AB8500_RTC_CALIB_REG 0x0E +#define AB8500_RTC_SWITCH_STAT_REG 0x0F /* RtcReadRequest bits */ #define RTC_READ_REQUEST 0x01 @@ -46,13 +46,13 @@ #define COUNTS_PER_SEC (0xF000 / 60) #define AB8500_RTC_EPOCH 2000 -static const unsigned long ab8500_rtc_time_regs[] = { +static const u8 ab8500_rtc_time_regs[] = { AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG, AB8500_RTC_WATCH_TMIN_LOW_REG, AB8500_RTC_WATCH_TSECHI_REG, AB8500_RTC_WATCH_TSECMID_REG }; -static const unsigned long ab8500_rtc_alarm_regs[] = { +static const u8 ab8500_rtc_alarm_regs[] = { AB8500_RTC_ALRM_MIN_HI_REG, AB8500_RTC_ALRM_MIN_MID_REG, AB8500_RTC_ALRM_MIN_LOW_REG }; @@ -76,29 +76,30 @@ static unsigned long get_elapsed_seconds(int year) static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); unsigned long timeout = jiffies + HZ; int retval, i; unsigned long mins, secs; unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)]; + u8 value; /* Request a data read */ - retval = ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG, - RTC_READ_REQUEST); + retval = abx500_set_register_interruptible(dev, + AB8500_RTC, AB8500_RTC_READ_REQ_REG, RTC_READ_REQUEST); if (retval < 0) return retval; /* Early AB8500 chips will not clear the rtc read request bit */ - if (ab8500->revision == 0) { + if (abx500_get_chip_id(dev) == 0) { msleep(1); } else { /* Wait for some cycles after enabling the rtc read in ab8500 */ while (time_before(jiffies, timeout)) { - retval = ab8500_read(ab8500, AB8500_RTC_READ_REQ_REG); + retval = abx500_get_register_interruptible(dev, + AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value); if (retval < 0) return retval; - if (!(retval & RTC_READ_REQUEST)) + if (!(value & RTC_READ_REQUEST)) break; msleep(1); @@ -107,10 +108,11 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm) /* Read the Watchtime registers */ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) { - retval = ab8500_read(ab8500, ab8500_rtc_time_regs[i]); + retval = abx500_get_register_interruptible(dev, + AB8500_RTC, ab8500_rtc_time_regs[i], &value); if (retval < 0) return retval; - buf[i] = retval; + buf[i] = value; } mins = (buf[0] << 16) | (buf[1] << 8) | buf[2]; @@ -128,7 +130,6 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm) static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); int retval, i; unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)]; unsigned long no_secs, no_mins, secs = 0; @@ -162,27 +163,29 @@ static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm) buf[0] = (no_mins >> 16) & 0xFF; for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) { - retval = ab8500_write(ab8500, ab8500_rtc_time_regs[i], buf[i]); + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + ab8500_rtc_time_regs[i], buf[i]); if (retval < 0) return retval; } /* Request a data write */ - return ab8500_write(ab8500, AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST); + return abx500_set_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST); } static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); int retval, i; - int rtc_ctrl; + u8 rtc_ctrl, value; unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)]; unsigned long secs, mins; /* Check if the alarm is enabled or not */ - rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG); - if (rtc_ctrl < 0) - return rtc_ctrl; + retval = abx500_get_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_STAT_REG, &rtc_ctrl); + if (retval < 0) + return retval; if (rtc_ctrl & RTC_ALARM_ENA) alarm->enabled = 1; @@ -192,10 +195,11 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) alarm->pending = 0; for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) { - retval = ab8500_read(ab8500, ab8500_rtc_alarm_regs[i]); + retval = abx500_get_register_interruptible(dev, AB8500_RTC, + ab8500_rtc_alarm_regs[i], &value); if (retval < 0) return retval; - buf[i] = retval; + buf[i] = value; } mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); @@ -211,15 +215,13 @@ static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled) { - struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - - return ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_ALARM_ENA, - enabled ? RTC_ALARM_ENA : 0); + return abx500_mask_and_set_register_interruptible(dev, AB8500_RTC, + AB8500_RTC_STAT_REG, RTC_ALARM_ENA, + enabled ? RTC_ALARM_ENA : 0); } static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) { - struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); int retval, i; unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)]; unsigned long mins, secs = 0; @@ -247,7 +249,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) /* Set the alarm time */ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) { - retval = ab8500_write(ab8500, ab8500_rtc_alarm_regs[i], buf[i]); + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + ab8500_rtc_alarm_regs[i], buf[i]); if (retval < 0) return retval; } @@ -276,10 +279,9 @@ static const struct rtc_class_ops ab8500_rtc_ops = { static int __devinit ab8500_rtc_probe(struct platform_device *pdev) { - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); int err; struct rtc_device *rtc; - int rtc_ctrl; + u8 rtc_ctrl; int irq; irq = platform_get_irq_byname(pdev, "ALARM"); @@ -287,17 +289,18 @@ static int __devinit ab8500_rtc_probe(struct platform_device *pdev) return irq; /* For RTC supply test */ - err = ab8500_set_bits(ab8500, AB8500_RTC_STAT_REG, RTC_STATUS_DATA, - RTC_STATUS_DATA); + err = abx500_mask_and_set_register_interruptible(&pdev->dev, AB8500_RTC, + AB8500_RTC_STAT_REG, RTC_STATUS_DATA, RTC_STATUS_DATA); if (err < 0) return err; /* Wait for reset by the PorRtc */ msleep(1); - rtc_ctrl = ab8500_read(ab8500, AB8500_RTC_STAT_REG); - if (rtc_ctrl < 0) - return rtc_ctrl; + err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC, + AB8500_RTC_STAT_REG, &rtc_ctrl); + if (err < 0) + return err; /* Check if the RTC Supply fails */ if (!(rtc_ctrl & RTC_STATUS_DATA)) { diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h index f5cec4500f38..d63b6050b183 100644 --- a/include/linux/mfd/ab8500.h +++ b/include/linux/mfd/ab8500.h @@ -9,6 +9,29 @@ #include +/* + * AB8500 bank addresses + */ +#define AB8500_SYS_CTRL1_BLOCK 0x1 +#define AB8500_SYS_CTRL2_BLOCK 0x2 +#define AB8500_REGU_CTRL1 0x3 +#define AB8500_REGU_CTRL2 0x4 +#define AB8500_USB 0x5 +#define AB8500_TVOUT 0x6 +#define AB8500_DBI 0x7 +#define AB8500_ECI_AV_ACC 0x8 +#define AB8500_RESERVED 0x9 +#define AB8500_GPADC 0xA +#define AB8500_CHARGER 0xB +#define AB8500_GAS_GAUGE 0xC +#define AB8500_AUDIO 0xD +#define AB8500_INTERRUPT 0xE +#define AB8500_RTC 0xF +#define AB8500_MISC 0x10 +#define AB8500_DEBUG 0x12 +#define AB8500_PROD_TEST 0x13 +#define AB8500_OTP_EMUL 0x15 + /* * Interrupts */ @@ -99,6 +122,7 @@ struct ab8500 { int revision; int irq_base; int irq; + u8 chip_id; int (*write) (struct ab8500 *a8500, u16 addr, u8 data); int (*read) (struct ab8500 *a8500, u16 addr); @@ -124,10 +148,6 @@ struct ab8500_platform_data { struct regulator_init_data *regulator[AB8500_NUM_REGULATORS]; }; -extern int ab8500_write(struct ab8500 *a8500, u16 addr, u8 data); -extern int ab8500_read(struct ab8500 *a8500, u16 addr); -extern int ab8500_set_bits(struct ab8500 *a8500, u16 addr, u8 mask, u8 data); - extern int __devinit ab8500_init(struct ab8500 *ab8500); extern int __devexit ab8500_exit(struct ab8500 *ab8500); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 390726fcbcb1..be7373c79bea 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -6,8 +6,7 @@ * * ABX500 core access functions. * The abx500 interface is used for the Analog Baseband chip - * ab3100, ab3550, ab5500 and possibly comming. It is not used for - * ab4500 and ab8500 since they are another family of chip. + * ab3100, ab3550, ab5500, and ab8500. * * Author: Mattias Wallin * Author: Mattias Nilsson -- cgit v1.2.3 From 38b340527aa44bb8d1b88ef1e5a4e26b27695c2b Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Wed, 8 Sep 2010 09:44:34 -0400 Subject: mfd: Update chip id of 88pm8607 Chipid of 88pm8607 is 0x40 or 0x50. Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm860x-core.c | 7 +++++-- include/linux/mfd/88pm860x.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 4db10a150369..20895e7a99c9 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -645,10 +645,13 @@ static void __devinit device_8607_init(struct pm860x_chip *chip, dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); goto out; } - if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION) + switch (ret & PM8607_VERSION_MASK) { + case 0x40: + case 0x50: dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n", ret); - else { + break; + default: dev_err(chip->dev, "Failed to detect Marvell 88PM8607. " "Chip ID: %02x\n", ret); goto out; diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index bfd23bef7363..4db1fbd8969e 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -138,7 +138,7 @@ enum { PM8607_ID_RG_MAX, }; -#define PM8607_VERSION (0x40) /* 8607 chip ID */ +/* 8607 chip ID is 0x40 or 0x50 */ #define PM8607_VERSION_MASK (0xF0) /* 8607 chip ID mask */ /* Interrupt Registers */ -- cgit v1.2.3 From c26448c48448266480e1b6c371f897167060ceaf Mon Sep 17 00:00:00 2001 From: Gary King Date: Mon, 20 Sep 2010 00:18:27 +0200 Subject: mfd: Add basic tps6586x interrupt support Add support for enabling and disabling tps6586x subdevice interrupts Signed-off-by: Gary King Acked-by: Mike Rapoport Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 4 +- drivers/mfd/tps6586x.c | 200 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps6586x.h | 31 +++++++ 3 files changed, 233 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 01c928bca099..66779bdc9627 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -563,8 +563,8 @@ config MFD_JZ4740_ADC This driver is necessary for jz4740-battery and jz4740-hwmon driver. config MFD_TPS6586X - tristate "TPS6586x Power Management chips" - depends on I2C && GPIOLIB + bool "TPS6586x Power Management chips" + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS select MFD_CORE help If you say yes here you get support for the TPS6586X series of diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 2f9336c3710c..117eb7cafe77 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -15,6 +15,8 @@ * published by the Free Software Foundation. */ +#include +#include #include #include #include @@ -29,16 +31,76 @@ #define TPS6586X_GPIOSET1 0x5d #define TPS6586X_GPIOSET2 0x5e +/* interrupt control registers */ +#define TPS6586X_INT_ACK1 0xb5 +#define TPS6586X_INT_ACK2 0xb6 +#define TPS6586X_INT_ACK3 0xb7 +#define TPS6586X_INT_ACK4 0xb8 + +/* interrupt mask registers */ +#define TPS6586X_INT_MASK1 0xb0 +#define TPS6586X_INT_MASK2 0xb1 +#define TPS6586X_INT_MASK3 0xb2 +#define TPS6586X_INT_MASK4 0xb3 +#define TPS6586X_INT_MASK5 0xb4 + /* device id */ #define TPS6586X_VERSIONCRC 0xcd #define TPS658621A_VERSIONCRC 0x15 +struct tps6586x_irq_data { + u8 mask_reg; + u8 mask_mask; +}; + +#define TPS6586X_IRQ(_reg, _mask) \ + { \ + .mask_reg = (_reg) - TPS6586X_INT_MASK1, \ + .mask_mask = (_mask), \ + } + +static const struct tps6586x_irq_data tps6586x_irqs[] = { + [TPS6586X_INT_PLDO_0] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 0), + [TPS6586X_INT_PLDO_1] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 1), + [TPS6586X_INT_PLDO_2] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 2), + [TPS6586X_INT_PLDO_3] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 3), + [TPS6586X_INT_PLDO_4] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 4), + [TPS6586X_INT_PLDO_5] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 5), + [TPS6586X_INT_PLDO_6] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 6), + [TPS6586X_INT_PLDO_7] = TPS6586X_IRQ(TPS6586X_INT_MASK1, 1 << 7), + [TPS6586X_INT_COMP_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 0), + [TPS6586X_INT_ADC] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 1), + [TPS6586X_INT_PLDO_8] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 2), + [TPS6586X_INT_PLDO_9] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 3), + [TPS6586X_INT_PSM_0] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 4), + [TPS6586X_INT_PSM_1] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 5), + [TPS6586X_INT_PSM_2] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 6), + [TPS6586X_INT_PSM_3] = TPS6586X_IRQ(TPS6586X_INT_MASK2, 1 << 7), + [TPS6586X_INT_RTC_ALM1] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 4), + [TPS6586X_INT_ACUSB_OVP] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 0x03), + [TPS6586X_INT_USB_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 2), + [TPS6586X_INT_AC_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 3), + [TPS6586X_INT_BAT_DET] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 1 << 0), + [TPS6586X_INT_CHG_STAT] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 0xfc), + [TPS6586X_INT_CHG_TEMP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0x06), + [TPS6586X_INT_PP] = TPS6586X_IRQ(TPS6586X_INT_MASK3, 0xf0), + [TPS6586X_INT_RESUME] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 5), + [TPS6586X_INT_LOW_SYS] = TPS6586X_IRQ(TPS6586X_INT_MASK5, 1 << 6), + [TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1), +}; + struct tps6586x { struct mutex lock; struct device *dev; struct i2c_client *client; struct gpio_chip gpio; + struct irq_chip irq_chip; + struct mutex irq_lock; + int irq_base; + u32 irq_en; + u8 mask_cache[5]; + u8 mask_reg[5]; }; static inline int __tps6586x_read(struct i2c_client *client, @@ -262,6 +324,129 @@ static int tps6586x_remove_subdevs(struct tps6586x *tps6586x) return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); } +static void tps6586x_irq_lock(unsigned int irq) +{ + struct tps6586x *tps6586x = get_irq_chip_data(irq); + + mutex_lock(&tps6586x->irq_lock); +} + +static void tps6586x_irq_enable(unsigned int irq) +{ + struct tps6586x *tps6586x = get_irq_chip_data(irq); + unsigned int __irq = irq - tps6586x->irq_base; + const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; + + tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; + tps6586x->irq_en |= (1 << __irq); +} + +static void tps6586x_irq_disable(unsigned int irq) +{ + struct tps6586x *tps6586x = get_irq_chip_data(irq); + + unsigned int __irq = irq - tps6586x->irq_base; + const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; + + tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; + tps6586x->irq_en &= ~(1 << __irq); +} + +static void tps6586x_irq_sync_unlock(unsigned int irq) +{ + struct tps6586x *tps6586x = get_irq_chip_data(irq); + int i; + + for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) { + if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) { + if (!WARN_ON(tps6586x_write(tps6586x->dev, + TPS6586X_INT_MASK1 + i, + tps6586x->mask_reg[i]))) + tps6586x->mask_cache[i] = tps6586x->mask_reg[i]; + } + } + + mutex_unlock(&tps6586x->irq_lock); +} + +static irqreturn_t tps6586x_irq(int irq, void *data) +{ + struct tps6586x *tps6586x = data; + u32 acks; + int ret = 0; + + ret = tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, + sizeof(acks), (uint8_t *)&acks); + + if (ret < 0) { + dev_err(tps6586x->dev, "failed to read interrupt status\n"); + return IRQ_NONE; + } + + acks = le32_to_cpu(acks); + + while (acks) { + int i = __ffs(acks); + + if (tps6586x->irq_en & (1 << i)) + handle_nested_irq(tps6586x->irq_base + i); + + acks &= ~(1 << i); + } + + return IRQ_HANDLED; +} + +static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, + int irq_base) +{ + int i, ret; + u8 tmp[4]; + + if (!irq_base) { + dev_warn(tps6586x->dev, "No interrupt support on IRQ base\n"); + return -EINVAL; + } + + mutex_init(&tps6586x->irq_lock); + for (i = 0; i < 5; i++) { + tps6586x->mask_cache[i] = 0xff; + tps6586x->mask_reg[i] = 0xff; + tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff); + } + + tps6586x_reads(tps6586x->dev, TPS6586X_INT_ACK1, sizeof(tmp), tmp); + + tps6586x->irq_base = irq_base; + + tps6586x->irq_chip.name = "tps6586x"; + tps6586x->irq_chip.enable = tps6586x_irq_enable; + tps6586x->irq_chip.disable = tps6586x_irq_disable; + tps6586x->irq_chip.bus_lock = tps6586x_irq_lock; + tps6586x->irq_chip.bus_sync_unlock = tps6586x_irq_sync_unlock; + + for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) { + int __irq = i + tps6586x->irq_base; + set_irq_chip_data(__irq, tps6586x); + set_irq_chip_and_handler(__irq, &tps6586x->irq_chip, + handle_simple_irq); + set_irq_nested_thread(__irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(__irq, IRQF_VALID); +#endif + } + + ret = request_threaded_irq(irq, NULL, tps6586x_irq, IRQF_ONESHOT, + "tps6586x", tps6586x); + + if (!ret) { + device_init_wakeup(tps6586x->dev, 1); + enable_irq_wake(irq); + } + + return ret; +} + static int __devinit tps6586x_add_subdevs(struct tps6586x *tps6586x, struct tps6586x_platform_data *pdata) { @@ -327,6 +512,15 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, mutex_init(&tps6586x->lock); + if (client->irq) { + ret = tps6586x_irq_init(tps6586x, client->irq, + pdata->irq_base); + if (ret) { + dev_err(&client->dev, "IRQ init failed: %d\n", ret); + goto err_irq_init; + } + } + ret = tps6586x_add_subdevs(tps6586x, pdata); if (ret) { dev_err(&client->dev, "add devices failed: %d\n", ret); @@ -338,6 +532,9 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, return 0; err_add_devs: + if (client->irq) + free_irq(client->irq, tps6586x); +err_irq_init: kfree(tps6586x); return ret; } @@ -348,6 +545,9 @@ static int __devexit tps6586x_i2c_remove(struct i2c_client *client) struct tps6586x_platform_data *pdata = client->dev.platform_data; int ret; + if (client->irq) + free_irq(client->irq, tps6586x); + if (pdata->gpio_base) { ret = gpiochip_remove(&tps6586x->gpio); if (ret) diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h index 772b3ae640af..b6bab1b04e25 100644 --- a/include/linux/mfd/tps6586x.h +++ b/include/linux/mfd/tps6586x.h @@ -18,6 +18,36 @@ enum { TPS6586X_ID_LDO_RTC, }; +enum { + TPS6586X_INT_PLDO_0, + TPS6586X_INT_PLDO_1, + TPS6586X_INT_PLDO_2, + TPS6586X_INT_PLDO_3, + TPS6586X_INT_PLDO_4, + TPS6586X_INT_PLDO_5, + TPS6586X_INT_PLDO_6, + TPS6586X_INT_PLDO_7, + TPS6586X_INT_COMP_DET, + TPS6586X_INT_ADC, + TPS6586X_INT_PLDO_8, + TPS6586X_INT_PLDO_9, + TPS6586X_INT_PSM_0, + TPS6586X_INT_PSM_1, + TPS6586X_INT_PSM_2, + TPS6586X_INT_PSM_3, + TPS6586X_INT_RTC_ALM1, + TPS6586X_INT_ACUSB_OVP, + TPS6586X_INT_USB_DET, + TPS6586X_INT_AC_DET, + TPS6586X_INT_BAT_DET, + TPS6586X_INT_CHG_STAT, + TPS6586X_INT_CHG_TEMP, + TPS6586X_INT_PP, + TPS6586X_INT_RESUME, + TPS6586X_INT_LOW_SYS, + TPS6586X_INT_RTC_ALM2, +}; + struct tps6586x_subdev_info { int id; const char *name; @@ -29,6 +59,7 @@ struct tps6586x_platform_data { struct tps6586x_subdev_info *subdevs; int gpio_base; + int irq_base; }; /* -- cgit v1.2.3 From c6252e9ce7f51a2af66bd69c93afb37191467c96 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 22 Sep 2010 14:58:30 +0100 Subject: mfd: Declare abx500_remove_ops() Otherwise sparse warns about a public symbol with no declaration and the compiler can't spot if the callers and users have different signatures for the function. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/abx500.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index be7373c79bea..67bd6f7ecf32 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -229,4 +229,5 @@ struct abx500_ops { }; int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops); +void abx500_remove_ops(struct device *dev); #endif -- cgit v1.2.3 From 5f2545fa156f3d4d327038d7664608e146809a3c Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 30 Sep 2010 21:55:36 +0100 Subject: mfd: Allow for bypass of cell resource conflict check The upcoming VIA VX855 MFD driver needs to communicate resources to subdevices where the resources may be claimed by ACPI. Add a flag to mfd_cell to request that resources are not policed. Signed-off-by: Daniel Drake Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 8 +++++--- include/linux/mfd/core.h | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 1823a57b7d8f..d1c8605d4ed4 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -65,9 +65,11 @@ static int mfd_add_device(struct device *parent, int id, res[r].end = cell->resources[r].end; } - ret = acpi_check_resource_conflict(res); - if (ret) - goto fail_res; + if (!cell->ignore_resource_conflicts) { + ret = acpi_check_resource_conflict(res); + if (ret) + goto fail_res; + } } ret = platform_device_add_resources(pdev, res, cell->num_resources); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 11d740b8831d..cb93d80aa642 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -44,6 +44,9 @@ struct mfd_cell { */ int num_resources; const struct resource *resources; + + /* don't check for resource conflicts */ + bool ignore_resource_conflicts; }; extern int mfd_add_devices(struct device *parent, int id, -- cgit v1.2.3 From b4e017e332b873133602f47ae8cacfae64ab82c5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 28 Sep 2010 16:38:41 +0200 Subject: mfd: Remove deprecated mc13783 functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last user is gone since v2.6.34-rc1~40 Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- include/linux/mfd/mc13783.h | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index 0fa44fb8dd26..5f6aff55eeb7 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -35,24 +35,6 @@ int mc13783_irq_status(struct mc13783 *mc13783, int irq, int *enabled, int *pending); int mc13783_irq_ack(struct mc13783 *mc13783, int irq); -static inline int mc13783_mask(struct mc13783 *mc13783, int irq) __deprecated; -static inline int mc13783_mask(struct mc13783 *mc13783, int irq) -{ - return mc13783_irq_mask(mc13783, irq); -} - -static inline int mc13783_unmask(struct mc13783 *mc13783, int irq) __deprecated; -static inline int mc13783_unmask(struct mc13783 *mc13783, int irq) -{ - return mc13783_irq_unmask(mc13783, irq); -} - -static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq) __deprecated; -static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq) -{ - return mc13783_irq_ack(mc13783, irq); -} - #define MC13783_ADC0 43 #define MC13783_ADC0_ADREFEN (1 << 10) #define MC13783_ADC0_ADREFMODE (1 << 11) -- cgit v1.2.3 From 8e00593557c3c5a7bc6f636412a1cadcf4624232 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 28 Sep 2010 16:37:20 +0200 Subject: mfd: Add mc13892 support to mc13xxx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mc13892 is the companion PMIC for Freescale's i.MX51. It's similar enough to mc13782 to support it in a single driver. This patch introduces enough compatibility cruft to keep all users of the superseded mc13783 driver unchanged. Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 +- drivers/mfd/Makefile | 2 +- drivers/mfd/mc13783-core.c | 743 --------------------------------------- drivers/mfd/mc13xxx-core.c | 840 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/mc13783.h | 247 ++++++------- include/linux/mfd/mc13xxx.h | 154 ++++++++ 6 files changed, 1113 insertions(+), 882 deletions(-) delete mode 100644 drivers/mfd/mc13783-core.c create mode 100644 drivers/mfd/mc13xxx-core.c create mode 100644 include/linux/mfd/mc13xxx.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9735f581574d..6c6b9f02d177 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -409,11 +409,16 @@ config MFD_PCF50633 so that function-specific drivers can bind to them. config MFD_MC13783 - tristate "Support Freescale MC13783" + tristate + +config MFD_MC13XXX + tristate "Support Freescale MC13783 and MC13892" depends on SPI_MASTER select MFD_CORE + select MFD_MC13783 help - Support for the Freescale (Atlas) MC13783 PMIC and audio CODEC. + Support for the Freescale (Atlas) PMIC and audio CODECs + MC13783 and MC13892. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the functionality of the device. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d18a6ab2ab58..70b26999dc81 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -39,7 +39,7 @@ 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 +obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o obj-$(CONFIG_MFD_CORE) += mfd-core.o diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c deleted file mode 100644 index 2506e6888507..000000000000 --- a/drivers/mfd/mc13783-core.c +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright 2009 Pengutronix - * Uwe Kleine-Koenig - * - * loosely based on an earlier driver that has - * Copyright 2009 Pengutronix, Sascha Hauer - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -struct mc13783 { - struct spi_device *spidev; - struct mutex lock; - int irq; - int adcflags; - - irq_handler_t irqhandler[MC13783_NUM_IRQ]; - void *irqdata[MC13783_NUM_IRQ]; -}; - -#define MC13783_REG_REVISION 7 -#define MC13783_REG_ADC_0 43 -#define MC13783_REG_ADC_1 44 -#define MC13783_REG_ADC_2 45 - -#define MC13783_IRQSTAT0 0 -#define MC13783_IRQSTAT0_ADCDONEI (1 << 0) -#define MC13783_IRQSTAT0_ADCBISDONEI (1 << 1) -#define MC13783_IRQSTAT0_TSI (1 << 2) -#define MC13783_IRQSTAT0_WHIGHI (1 << 3) -#define MC13783_IRQSTAT0_WLOWI (1 << 4) -#define MC13783_IRQSTAT0_CHGDETI (1 << 6) -#define MC13783_IRQSTAT0_CHGOVI (1 << 7) -#define MC13783_IRQSTAT0_CHGREVI (1 << 8) -#define MC13783_IRQSTAT0_CHGSHORTI (1 << 9) -#define MC13783_IRQSTAT0_CCCVI (1 << 10) -#define MC13783_IRQSTAT0_CHGCURRI (1 << 11) -#define MC13783_IRQSTAT0_BPONI (1 << 12) -#define MC13783_IRQSTAT0_LOBATLI (1 << 13) -#define MC13783_IRQSTAT0_LOBATHI (1 << 14) -#define MC13783_IRQSTAT0_UDPI (1 << 15) -#define MC13783_IRQSTAT0_USBI (1 << 16) -#define MC13783_IRQSTAT0_IDI (1 << 19) -#define MC13783_IRQSTAT0_SE1I (1 << 21) -#define MC13783_IRQSTAT0_CKDETI (1 << 22) -#define MC13783_IRQSTAT0_UDMI (1 << 23) - -#define MC13783_IRQMASK0 1 -#define MC13783_IRQMASK0_ADCDONEM MC13783_IRQSTAT0_ADCDONEI -#define MC13783_IRQMASK0_ADCBISDONEM MC13783_IRQSTAT0_ADCBISDONEI -#define MC13783_IRQMASK0_TSM MC13783_IRQSTAT0_TSI -#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI -#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI -#define MC13783_IRQMASK0_CHGDETM MC13783_IRQSTAT0_CHGDETI -#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI -#define MC13783_IRQMASK0_CHGREVM MC13783_IRQSTAT0_CHGREVI -#define MC13783_IRQMASK0_CHGSHORTM MC13783_IRQSTAT0_CHGSHORTI -#define MC13783_IRQMASK0_CCCVM MC13783_IRQSTAT0_CCCVI -#define MC13783_IRQMASK0_CHGCURRM MC13783_IRQSTAT0_CHGCURRI -#define MC13783_IRQMASK0_BPONM MC13783_IRQSTAT0_BPONI -#define MC13783_IRQMASK0_LOBATLM MC13783_IRQSTAT0_LOBATLI -#define MC13783_IRQMASK0_LOBATHM MC13783_IRQSTAT0_LOBATHI -#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI -#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI -#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI -#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I -#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI -#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI - -#define MC13783_IRQSTAT1 3 -#define MC13783_IRQSTAT1_1HZI (1 << 0) -#define MC13783_IRQSTAT1_TODAI (1 << 1) -#define MC13783_IRQSTAT1_ONOFD1I (1 << 3) -#define MC13783_IRQSTAT1_ONOFD2I (1 << 4) -#define MC13783_IRQSTAT1_ONOFD3I (1 << 5) -#define MC13783_IRQSTAT1_SYSRSTI (1 << 6) -#define MC13783_IRQSTAT1_RTCRSTI (1 << 7) -#define MC13783_IRQSTAT1_PCI (1 << 8) -#define MC13783_IRQSTAT1_WARMI (1 << 9) -#define MC13783_IRQSTAT1_MEMHLDI (1 << 10) -#define MC13783_IRQSTAT1_PWRRDYI (1 << 11) -#define MC13783_IRQSTAT1_THWARNLI (1 << 12) -#define MC13783_IRQSTAT1_THWARNHI (1 << 13) -#define MC13783_IRQSTAT1_CLKI (1 << 14) -#define MC13783_IRQSTAT1_SEMAFI (1 << 15) -#define MC13783_IRQSTAT1_MC2BI (1 << 17) -#define MC13783_IRQSTAT1_HSDETI (1 << 18) -#define MC13783_IRQSTAT1_HSLI (1 << 19) -#define MC13783_IRQSTAT1_ALSPTHI (1 << 20) -#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21) - -#define MC13783_IRQMASK1 4 -#define MC13783_IRQMASK1_1HZM MC13783_IRQSTAT1_1HZI -#define MC13783_IRQMASK1_TODAM MC13783_IRQSTAT1_TODAI -#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I -#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I -#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I -#define MC13783_IRQMASK1_SYSRSTM MC13783_IRQSTAT1_SYSRSTI -#define MC13783_IRQMASK1_RTCRSTM MC13783_IRQSTAT1_RTCRSTI -#define MC13783_IRQMASK1_PCM MC13783_IRQSTAT1_PCI -#define MC13783_IRQMASK1_WARMM MC13783_IRQSTAT1_WARMI -#define MC13783_IRQMASK1_MEMHLDM MC13783_IRQSTAT1_MEMHLDI -#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI -#define MC13783_IRQMASK1_THWARNLM MC13783_IRQSTAT1_THWARNLI -#define MC13783_IRQMASK1_THWARNHM MC13783_IRQSTAT1_THWARNHI -#define MC13783_IRQMASK1_CLKM MC13783_IRQSTAT1_CLKI -#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI -#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI -#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI -#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI -#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI -#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI - -#define MC13783_ADC1 44 -#define MC13783_ADC1_ADEN (1 << 0) -#define MC13783_ADC1_RAND (1 << 1) -#define MC13783_ADC1_ADSEL (1 << 3) -#define MC13783_ADC1_ASC (1 << 20) -#define MC13783_ADC1_ADTRIGIGN (1 << 21) - -#define MC13783_NUMREGS 0x3f - -void mc13783_lock(struct mc13783 *mc13783) -{ - if (!mutex_trylock(&mc13783->lock)) { - dev_dbg(&mc13783->spidev->dev, "wait for %s from %pf\n", - __func__, __builtin_return_address(0)); - - mutex_lock(&mc13783->lock); - } - dev_dbg(&mc13783->spidev->dev, "%s from %pf\n", - __func__, __builtin_return_address(0)); -} -EXPORT_SYMBOL(mc13783_lock); - -void mc13783_unlock(struct mc13783 *mc13783) -{ - dev_dbg(&mc13783->spidev->dev, "%s from %pf\n", - __func__, __builtin_return_address(0)); - mutex_unlock(&mc13783->lock); -} -EXPORT_SYMBOL(mc13783_unlock); - -#define MC13783_REGOFFSET_SHIFT 25 -int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val) -{ - struct spi_transfer t; - struct spi_message m; - int ret; - - BUG_ON(!mutex_is_locked(&mc13783->lock)); - - if (offset > MC13783_NUMREGS) - return -EINVAL; - - *val = offset << MC13783_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(mc13783->spidev, &m); - - /* error in message.status implies error return from spi_sync */ - BUG_ON(!ret && m.status); - - if (ret) - return ret; - - *val &= 0xffffff; - - dev_vdbg(&mc13783->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val); - - return 0; -} -EXPORT_SYMBOL(mc13783_reg_read); - -int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val) -{ - u32 buf; - struct spi_transfer t; - struct spi_message m; - int ret; - - BUG_ON(!mutex_is_locked(&mc13783->lock)); - - dev_vdbg(&mc13783->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val); - - if (offset > MC13783_NUMREGS || val > 0xffffff) - return -EINVAL; - - buf = 1 << 31 | offset << MC13783_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(mc13783->spidev, &m); - - BUG_ON(!ret && m.status); - - if (ret) - return ret; - - return 0; -} -EXPORT_SYMBOL(mc13783_reg_write); - -int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset, - u32 mask, u32 val) -{ - int ret; - u32 valread; - - BUG_ON(val & ~mask); - - ret = mc13783_reg_read(mc13783, offset, &valread); - if (ret) - return ret; - - valread = (valread & ~mask) | val; - - return mc13783_reg_write(mc13783, offset, valread); -} -EXPORT_SYMBOL(mc13783_reg_rmw); - -int mc13783_get_flags(struct mc13783 *mc13783) -{ - struct mc13783_platform_data *pdata = - dev_get_platdata(&mc13783->spidev->dev); - - return pdata->flags; -} -EXPORT_SYMBOL(mc13783_get_flags); - -int mc13783_irq_mask(struct mc13783 *mc13783, int irq) -{ - int ret; - unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1; - u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); - u32 mask; - - if (irq < 0 || irq >= MC13783_NUM_IRQ) - return -EINVAL; - - ret = mc13783_reg_read(mc13783, offmask, &mask); - if (ret) - return ret; - - if (mask & irqbit) - /* already masked */ - return 0; - - return mc13783_reg_write(mc13783, offmask, mask | irqbit); -} -EXPORT_SYMBOL(mc13783_irq_mask); - -int mc13783_irq_unmask(struct mc13783 *mc13783, int irq) -{ - int ret; - unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1; - u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); - u32 mask; - - if (irq < 0 || irq >= MC13783_NUM_IRQ) - return -EINVAL; - - ret = mc13783_reg_read(mc13783, offmask, &mask); - if (ret) - return ret; - - if (!(mask & irqbit)) - /* already unmasked */ - return 0; - - return mc13783_reg_write(mc13783, offmask, mask & ~irqbit); -} -EXPORT_SYMBOL(mc13783_irq_unmask); - -int mc13783_irq_status(struct mc13783 *mc13783, int irq, - int *enabled, int *pending) -{ - int ret; - unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1; - unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1; - u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); - - if (irq < 0 || irq >= MC13783_NUM_IRQ) - return -EINVAL; - - if (enabled) { - u32 mask; - - ret = mc13783_reg_read(mc13783, offmask, &mask); - if (ret) - return ret; - - *enabled = mask & irqbit; - } - - if (pending) { - u32 stat; - - ret = mc13783_reg_read(mc13783, offstat, &stat); - if (ret) - return ret; - - *pending = stat & irqbit; - } - - return 0; -} -EXPORT_SYMBOL(mc13783_irq_status); - -int mc13783_irq_ack(struct mc13783 *mc13783, int irq) -{ - unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1; - unsigned int val = 1 << (irq < 24 ? irq : irq - 24); - - BUG_ON(irq < 0 || irq >= MC13783_NUM_IRQ); - - return mc13783_reg_write(mc13783, offstat, val); -} -EXPORT_SYMBOL(mc13783_irq_ack); - -int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - BUG_ON(!mutex_is_locked(&mc13783->lock)); - BUG_ON(!handler); - - if (irq < 0 || irq >= MC13783_NUM_IRQ) - return -EINVAL; - - if (mc13783->irqhandler[irq]) - return -EBUSY; - - mc13783->irqhandler[irq] = handler; - mc13783->irqdata[irq] = dev; - - return 0; -} -EXPORT_SYMBOL(mc13783_irq_request_nounmask); - -int mc13783_irq_request(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - int ret; - - ret = mc13783_irq_request_nounmask(mc13783, irq, handler, name, dev); - if (ret) - return ret; - - ret = mc13783_irq_unmask(mc13783, irq); - if (ret) { - mc13783->irqhandler[irq] = NULL; - mc13783->irqdata[irq] = NULL; - return ret; - } - - return 0; -} -EXPORT_SYMBOL(mc13783_irq_request); - -int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev) -{ - int ret; - BUG_ON(!mutex_is_locked(&mc13783->lock)); - - if (irq < 0 || irq >= MC13783_NUM_IRQ || !mc13783->irqhandler[irq] || - mc13783->irqdata[irq] != dev) - return -EINVAL; - - ret = mc13783_irq_mask(mc13783, irq); - if (ret) - return ret; - - mc13783->irqhandler[irq] = NULL; - mc13783->irqdata[irq] = NULL; - - return 0; -} -EXPORT_SYMBOL(mc13783_irq_free); - -static inline irqreturn_t mc13783_irqhandler(struct mc13783 *mc13783, int irq) -{ - return mc13783->irqhandler[irq](irq, mc13783->irqdata[irq]); -} - -/* - * returns: number of handled irqs or negative error - * locking: holds mc13783->lock - */ -static int mc13783_irq_handle(struct mc13783 *mc13783, - unsigned int offstat, unsigned int offmask, int baseirq) -{ - u32 stat, mask; - int ret = mc13783_reg_read(mc13783, offstat, &stat); - int num_handled = 0; - - if (ret) - return ret; - - ret = mc13783_reg_read(mc13783, offmask, &mask); - if (ret) - return ret; - - while (stat & ~mask) { - int irq = __ffs(stat & ~mask); - - stat &= ~(1 << irq); - - if (likely(mc13783->irqhandler[baseirq + irq])) { - irqreturn_t handled; - - handled = mc13783_irqhandler(mc13783, baseirq + irq); - if (handled == IRQ_HANDLED) - num_handled++; - } else { - dev_err(&mc13783->spidev->dev, - "BUG: irq %u but no handler\n", - baseirq + irq); - - mask |= 1 << irq; - - ret = mc13783_reg_write(mc13783, offmask, mask); - } - } - - return num_handled; -} - -static irqreturn_t mc13783_irq_thread(int irq, void *data) -{ - struct mc13783 *mc13783 = data; - irqreturn_t ret; - int handled = 0; - - mc13783_lock(mc13783); - - ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT0, - MC13783_IRQMASK0, MC13783_IRQ_ADCDONE); - if (ret > 0) - handled = 1; - - ret = mc13783_irq_handle(mc13783, MC13783_IRQSTAT1, - MC13783_IRQMASK1, MC13783_IRQ_1HZ); - if (ret > 0) - handled = 1; - - mc13783_unlock(mc13783); - - return IRQ_RETVAL(handled); -} - -#define MC13783_ADC1_CHAN0_SHIFT 5 -#define MC13783_ADC1_CHAN1_SHIFT 8 - -struct mc13783_adcdone_data { - struct mc13783 *mc13783; - struct completion done; -}; - -static irqreturn_t mc13783_handler_adcdone(int irq, void *data) -{ - struct mc13783_adcdone_data *adcdone_data = data; - - mc13783_irq_ack(adcdone_data->mc13783, irq); - - complete_all(&adcdone_data->done); - - return IRQ_HANDLED; -} - -#define MC13783_ADC_WORKING (1 << 0) - -int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, - unsigned int channel, unsigned int *sample) -{ - u32 adc0, adc1, old_adc0; - int i, ret; - struct mc13783_adcdone_data adcdone_data = { - .mc13783 = mc13783, - }; - init_completion(&adcdone_data.done); - - dev_dbg(&mc13783->spidev->dev, "%s\n", __func__); - - mc13783_lock(mc13783); - - if (mc13783->adcflags & MC13783_ADC_WORKING) { - ret = -EBUSY; - goto out; - } - - mc13783->adcflags |= MC13783_ADC_WORKING; - - mc13783_reg_read(mc13783, MC13783_ADC0, &old_adc0); - - adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2; - adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC; - - if (channel > 7) - adc1 |= MC13783_ADC1_ADSEL; - - switch (mode) { - case MC13783_ADC_MODE_TS: - adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 | - MC13783_ADC0_TSMOD1; - adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; - break; - - case MC13783_ADC_MODE_SINGLE_CHAN: - adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; - adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT; - adc1 |= MC13783_ADC1_RAND; - break; - - case MC13783_ADC_MODE_MULT_CHAN: - adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; - adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; - break; - - default: - mc13783_unlock(mc13783); - return -EINVAL; - } - - dev_dbg(&mc13783->spidev->dev, "%s: request irq\n", __func__); - mc13783_irq_request(mc13783, MC13783_IRQ_ADCDONE, - mc13783_handler_adcdone, __func__, &adcdone_data); - mc13783_irq_ack(mc13783, MC13783_IRQ_ADCDONE); - - mc13783_reg_write(mc13783, MC13783_REG_ADC_0, adc0); - mc13783_reg_write(mc13783, MC13783_REG_ADC_1, adc1); - - mc13783_unlock(mc13783); - - ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ); - - if (!ret) - ret = -ETIMEDOUT; - - mc13783_lock(mc13783); - - mc13783_irq_free(mc13783, MC13783_IRQ_ADCDONE, &adcdone_data); - - if (ret > 0) - for (i = 0; i < 4; ++i) { - ret = mc13783_reg_read(mc13783, - MC13783_REG_ADC_2, &sample[i]); - if (ret) - break; - } - - if (mode == MC13783_ADC_MODE_TS) - /* restore TSMOD */ - mc13783_reg_write(mc13783, MC13783_REG_ADC_0, old_adc0); - - mc13783->adcflags &= ~MC13783_ADC_WORKING; -out: - mc13783_unlock(mc13783); - - return ret; -} -EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); - -static int mc13783_add_subdevice_pdata(struct mc13783 *mc13783, - const char *name, void *pdata, size_t pdata_size) -{ - struct mfd_cell cell = { - .name = name, - .platform_data = pdata, - .data_size = pdata_size, - }; - - return mfd_add_devices(&mc13783->spidev->dev, -1, &cell, 1, NULL, 0); -} - -static int mc13783_add_subdevice(struct mc13783 *mc13783, const char *name) -{ - return mc13783_add_subdevice_pdata(mc13783, name, NULL, 0); -} - -static int mc13783_check_revision(struct mc13783 *mc13783) -{ - u32 rev_id, rev1, rev2, finid, icid; - - mc13783_reg_read(mc13783, MC13783_REG_REVISION, &rev_id); - - rev1 = (rev_id & 0x018) >> 3; - rev2 = (rev_id & 0x007); - icid = (rev_id & 0x01C0) >> 6; - finid = (rev_id & 0x01E00) >> 9; - - /* Ver 0.2 is actually 3.2a. Report as 3.2 */ - if ((rev1 == 0) && (rev2 == 2)) - rev1 = 3; - - if (rev1 == 0 || icid != 2) { - dev_err(&mc13783->spidev->dev, "No MC13783 detected.\n"); - return -ENODEV; - } - - dev_info(&mc13783->spidev->dev, - "MC13783 Rev %d.%d FinVer %x detected\n", - rev1, rev2, finid); - - return 0; -} - -static int mc13783_probe(struct spi_device *spi) -{ - struct mc13783 *mc13783; - struct mc13783_platform_data *pdata = dev_get_platdata(&spi->dev); - int ret; - - mc13783 = kzalloc(sizeof(*mc13783), GFP_KERNEL); - if (!mc13783) - return -ENOMEM; - - dev_set_drvdata(&spi->dev, mc13783); - spi->mode = SPI_MODE_0 | SPI_CS_HIGH; - spi->bits_per_word = 32; - spi_setup(spi); - - mc13783->spidev = spi; - - mutex_init(&mc13783->lock); - mc13783_lock(mc13783); - - ret = mc13783_check_revision(mc13783); - if (ret) - goto err_revision; - - /* mask all irqs */ - ret = mc13783_reg_write(mc13783, MC13783_IRQMASK0, 0x00ffffff); - if (ret) - goto err_mask; - - ret = mc13783_reg_write(mc13783, MC13783_IRQMASK1, 0x00ffffff); - if (ret) - goto err_mask; - - ret = request_threaded_irq(spi->irq, NULL, mc13783_irq_thread, - IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13783", mc13783); - - if (ret) { -err_mask: -err_revision: - mutex_unlock(&mc13783->lock); - dev_set_drvdata(&spi->dev, NULL); - kfree(mc13783); - return ret; - } - - mc13783_unlock(mc13783); - - if (pdata->flags & MC13783_USE_ADC) - mc13783_add_subdevice(mc13783, "mc13783-adc"); - - if (pdata->flags & MC13783_USE_CODEC) - mc13783_add_subdevice(mc13783, "mc13783-codec"); - - if (pdata->flags & MC13783_USE_REGULATOR) { - struct mc13783_regulator_platform_data regulator_pdata = { - .num_regulators = pdata->num_regulators, - .regulators = pdata->regulators, - }; - - mc13783_add_subdevice_pdata(mc13783, "mc13783-regulator", - ®ulator_pdata, sizeof(regulator_pdata)); - } - - if (pdata->flags & MC13783_USE_RTC) - mc13783_add_subdevice(mc13783, "mc13783-rtc"); - - if (pdata->flags & MC13783_USE_TOUCHSCREEN) - mc13783_add_subdevice(mc13783, "mc13783-ts"); - - if (pdata->flags & MC13783_USE_LED) - mc13783_add_subdevice_pdata(mc13783, "mc13783-led", - pdata->leds, sizeof(*pdata->leds)); - - return 0; -} - -static int __devexit mc13783_remove(struct spi_device *spi) -{ - struct mc13783 *mc13783 = dev_get_drvdata(&spi->dev); - - free_irq(mc13783->spidev->irq, mc13783); - - mfd_remove_devices(&spi->dev); - - return 0; -} - -static struct spi_driver mc13783_driver = { - .driver = { - .name = "mc13783", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = mc13783_probe, - .remove = __devexit_p(mc13783_remove), -}; - -static int __init mc13783_init(void) -{ - return spi_register_driver(&mc13783_driver); -} -subsys_initcall(mc13783_init); - -static void __exit mc13783_exit(void) -{ - spi_unregister_driver(&mc13783_driver); -} -module_exit(mc13783_exit); - -MODULE_DESCRIPTION("Core driver for Freescale MC13783 PMIC"); -MODULE_AUTHOR("Uwe Kleine-Koenig "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c new file mode 100644 index 000000000000..93258adf8b63 --- /dev/null +++ b/drivers/mfd/mc13xxx-core.c @@ -0,0 +1,840 @@ +/* + * Copyright 2009-2010 Pengutronix + * Uwe Kleine-Koenig + * + * loosely based on an earlier driver that has + * Copyright 2009 Pengutronix, Sascha Hauer + * + * 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. + */ +#define DEBUG +#define VERBOSE_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +struct mc13xxx { + struct spi_device *spidev; + struct mutex lock; + int irq; + + irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; + void *irqdata[MC13XXX_NUM_IRQ]; +}; + +struct mc13783 { + struct mc13xxx mc13xxx; + + int adcflags; +}; + +struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783) +{ + return &mc13783->mc13xxx; +} +EXPORT_SYMBOL(mc13783_to_mc13xxx); + +#define MC13XXX_IRQSTAT0 0 +#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0) +#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1) +#define MC13XXX_IRQSTAT0_TSI (1 << 2) +#define MC13783_IRQSTAT0_WHIGHI (1 << 3) +#define MC13783_IRQSTAT0_WLOWI (1 << 4) +#define MC13XXX_IRQSTAT0_CHGDETI (1 << 6) +#define MC13783_IRQSTAT0_CHGOVI (1 << 7) +#define MC13XXX_IRQSTAT0_CHGREVI (1 << 8) +#define MC13XXX_IRQSTAT0_CHGSHORTI (1 << 9) +#define MC13XXX_IRQSTAT0_CCCVI (1 << 10) +#define MC13XXX_IRQSTAT0_CHGCURRI (1 << 11) +#define MC13XXX_IRQSTAT0_BPONI (1 << 12) +#define MC13XXX_IRQSTAT0_LOBATLI (1 << 13) +#define MC13XXX_IRQSTAT0_LOBATHI (1 << 14) +#define MC13783_IRQSTAT0_UDPI (1 << 15) +#define MC13783_IRQSTAT0_USBI (1 << 16) +#define MC13783_IRQSTAT0_IDI (1 << 19) +#define MC13783_IRQSTAT0_SE1I (1 << 21) +#define MC13783_IRQSTAT0_CKDETI (1 << 22) +#define MC13783_IRQSTAT0_UDMI (1 << 23) + +#define MC13XXX_IRQMASK0 1 +#define MC13XXX_IRQMASK0_ADCDONEM MC13XXX_IRQSTAT0_ADCDONEI +#define MC13XXX_IRQMASK0_ADCBISDONEM MC13XXX_IRQSTAT0_ADCBISDONEI +#define MC13XXX_IRQMASK0_TSM MC13XXX_IRQSTAT0_TSI +#define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI +#define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI +#define MC13XXX_IRQMASK0_CHGDETM MC13XXX_IRQSTAT0_CHGDETI +#define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI +#define MC13XXX_IRQMASK0_CHGREVM MC13XXX_IRQSTAT0_CHGREVI +#define MC13XXX_IRQMASK0_CHGSHORTM MC13XXX_IRQSTAT0_CHGSHORTI +#define MC13XXX_IRQMASK0_CCCVM MC13XXX_IRQSTAT0_CCCVI +#define MC13XXX_IRQMASK0_CHGCURRM MC13XXX_IRQSTAT0_CHGCURRI +#define MC13XXX_IRQMASK0_BPONM MC13XXX_IRQSTAT0_BPONI +#define MC13XXX_IRQMASK0_LOBATLM MC13XXX_IRQSTAT0_LOBATLI +#define MC13XXX_IRQMASK0_LOBATHM MC13XXX_IRQSTAT0_LOBATHI +#define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI +#define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI +#define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI +#define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I +#define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI +#define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI + +#define MC13XXX_IRQSTAT1 3 +#define MC13XXX_IRQSTAT1_1HZI (1 << 0) +#define MC13XXX_IRQSTAT1_TODAI (1 << 1) +#define MC13783_IRQSTAT1_ONOFD1I (1 << 3) +#define MC13783_IRQSTAT1_ONOFD2I (1 << 4) +#define MC13783_IRQSTAT1_ONOFD3I (1 << 5) +#define MC13XXX_IRQSTAT1_SYSRSTI (1 << 6) +#define MC13XXX_IRQSTAT1_RTCRSTI (1 << 7) +#define MC13XXX_IRQSTAT1_PCI (1 << 8) +#define MC13XXX_IRQSTAT1_WARMI (1 << 9) +#define MC13XXX_IRQSTAT1_MEMHLDI (1 << 10) +#define MC13783_IRQSTAT1_PWRRDYI (1 << 11) +#define MC13XXX_IRQSTAT1_THWARNLI (1 << 12) +#define MC13XXX_IRQSTAT1_THWARNHI (1 << 13) +#define MC13XXX_IRQSTAT1_CLKI (1 << 14) +#define MC13783_IRQSTAT1_SEMAFI (1 << 15) +#define MC13783_IRQSTAT1_MC2BI (1 << 17) +#define MC13783_IRQSTAT1_HSDETI (1 << 18) +#define MC13783_IRQSTAT1_HSLI (1 << 19) +#define MC13783_IRQSTAT1_ALSPTHI (1 << 20) +#define MC13783_IRQSTAT1_AHSSHORTI (1 << 21) + +#define MC13XXX_IRQMASK1 4 +#define MC13XXX_IRQMASK1_1HZM MC13XXX_IRQSTAT1_1HZI +#define MC13XXX_IRQMASK1_TODAM MC13XXX_IRQSTAT1_TODAI +#define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I +#define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I +#define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I +#define MC13XXX_IRQMASK1_SYSRSTM MC13XXX_IRQSTAT1_SYSRSTI +#define MC13XXX_IRQMASK1_RTCRSTM MC13XXX_IRQSTAT1_RTCRSTI +#define MC13XXX_IRQMASK1_PCM MC13XXX_IRQSTAT1_PCI +#define MC13XXX_IRQMASK1_WARMM MC13XXX_IRQSTAT1_WARMI +#define MC13XXX_IRQMASK1_MEMHLDM MC13XXX_IRQSTAT1_MEMHLDI +#define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI +#define MC13XXX_IRQMASK1_THWARNLM MC13XXX_IRQSTAT1_THWARNLI +#define MC13XXX_IRQMASK1_THWARNHM MC13XXX_IRQSTAT1_THWARNHI +#define MC13XXX_IRQMASK1_CLKM MC13XXX_IRQSTAT1_CLKI +#define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI +#define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI +#define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI +#define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI +#define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI +#define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI + +#define MC13XXX_REVISION 7 +#define MC13XXX_REVISION_REVMETAL (0x07 << 0) +#define MC13XXX_REVISION_REVFULL (0x03 << 3) +#define MC13XXX_REVISION_ICID (0x07 << 6) +#define MC13XXX_REVISION_FIN (0x03 << 9) +#define MC13XXX_REVISION_FAB (0x03 << 11) +#define MC13XXX_REVISION_ICIDCODE (0x3f << 13) + +#define MC13783_ADC1 44 +#define MC13783_ADC1_ADEN (1 << 0) +#define MC13783_ADC1_RAND (1 << 1) +#define MC13783_ADC1_ADSEL (1 << 3) +#define MC13783_ADC1_ASC (1 << 20) +#define MC13783_ADC1_ADTRIGIGN (1 << 21) + +#define MC13783_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", + __func__, __builtin_return_address(0)); + + mutex_lock(&mc13xxx->lock); + } + dev_dbg(&mc13xxx->spidev->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", + __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)); + + 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); + + if (ret) + return ret; + + *val &= 0xffffff; + + dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val); + + return 0; +} +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); + + 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; +} +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(val & ~mask); + + ret = mc13xxx_reg_read(mc13xxx, offset, &valread); + if (ret) + return ret; + + valread = (valread & ~mask) | val; + + return mc13xxx_reg_write(mc13xxx, offset, valread); +} +EXPORT_SYMBOL(mc13xxx_reg_rmw); + +int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq) +{ + int ret; + unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1; + u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); + u32 mask; + + if (irq < 0 || irq >= MC13XXX_NUM_IRQ) + return -EINVAL; + + ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); + if (ret) + return ret; + + if (mask & irqbit) + /* already masked */ + return 0; + + return mc13xxx_reg_write(mc13xxx, offmask, mask | irqbit); +} +EXPORT_SYMBOL(mc13xxx_irq_mask); + +int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq) +{ + int ret; + unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1; + u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); + u32 mask; + + if (irq < 0 || irq >= MC13XXX_NUM_IRQ) + return -EINVAL; + + ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); + if (ret) + return ret; + + if (!(mask & irqbit)) + /* already unmasked */ + return 0; + + return mc13xxx_reg_write(mc13xxx, offmask, mask & ~irqbit); +} +EXPORT_SYMBOL(mc13xxx_irq_unmask); + +int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq, + int *enabled, int *pending) +{ + int ret; + unsigned int offmask = irq < 24 ? MC13XXX_IRQMASK0 : MC13XXX_IRQMASK1; + unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1; + u32 irqbit = 1 << (irq < 24 ? irq : irq - 24); + + if (irq < 0 || irq >= MC13XXX_NUM_IRQ) + return -EINVAL; + + if (enabled) { + u32 mask; + + ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); + if (ret) + return ret; + + *enabled = mask & irqbit; + } + + if (pending) { + u32 stat; + + ret = mc13xxx_reg_read(mc13xxx, offstat, &stat); + if (ret) + return ret; + + *pending = stat & irqbit; + } + + return 0; +} +EXPORT_SYMBOL(mc13xxx_irq_status); + +int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq) +{ + unsigned int offstat = irq < 24 ? MC13XXX_IRQSTAT0 : MC13XXX_IRQSTAT1; + unsigned int val = 1 << (irq < 24 ? irq : irq - 24); + + BUG_ON(irq < 0 || irq >= MC13XXX_NUM_IRQ); + + return mc13xxx_reg_write(mc13xxx, offstat, val); +} +EXPORT_SYMBOL(mc13xxx_irq_ack); + +int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq, + irq_handler_t handler, const char *name, void *dev) +{ + BUG_ON(!mutex_is_locked(&mc13xxx->lock)); + BUG_ON(!handler); + + if (irq < 0 || irq >= MC13XXX_NUM_IRQ) + return -EINVAL; + + if (mc13xxx->irqhandler[irq]) + return -EBUSY; + + mc13xxx->irqhandler[irq] = handler; + mc13xxx->irqdata[irq] = dev; + + return 0; +} +EXPORT_SYMBOL(mc13xxx_irq_request_nounmask); + +int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq, + irq_handler_t handler, const char *name, void *dev) +{ + int ret; + + ret = mc13xxx_irq_request_nounmask(mc13xxx, irq, handler, name, dev); + if (ret) + return ret; + + ret = mc13xxx_irq_unmask(mc13xxx, irq); + if (ret) { + mc13xxx->irqhandler[irq] = NULL; + mc13xxx->irqdata[irq] = NULL; + return ret; + } + + return 0; +} +EXPORT_SYMBOL(mc13xxx_irq_request); + +int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev) +{ + int ret; + BUG_ON(!mutex_is_locked(&mc13xxx->lock)); + + if (irq < 0 || irq >= MC13XXX_NUM_IRQ || !mc13xxx->irqhandler[irq] || + mc13xxx->irqdata[irq] != dev) + return -EINVAL; + + ret = mc13xxx_irq_mask(mc13xxx, irq); + if (ret) + return ret; + + mc13xxx->irqhandler[irq] = NULL; + mc13xxx->irqdata[irq] = NULL; + + return 0; +} +EXPORT_SYMBOL(mc13xxx_irq_free); + +static inline irqreturn_t mc13xxx_irqhandler(struct mc13xxx *mc13xxx, int irq) +{ + return mc13xxx->irqhandler[irq](irq, mc13xxx->irqdata[irq]); +} + +/* + * returns: number of handled irqs or negative error + * locking: holds mc13xxx->lock + */ +static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx, + unsigned int offstat, unsigned int offmask, int baseirq) +{ + u32 stat, mask; + int ret = mc13xxx_reg_read(mc13xxx, offstat, &stat); + int num_handled = 0; + + if (ret) + return ret; + + ret = mc13xxx_reg_read(mc13xxx, offmask, &mask); + if (ret) + return ret; + + while (stat & ~mask) { + int irq = __ffs(stat & ~mask); + + stat &= ~(1 << irq); + + if (likely(mc13xxx->irqhandler[baseirq + irq])) { + irqreturn_t handled; + + handled = mc13xxx_irqhandler(mc13xxx, baseirq + irq); + if (handled == IRQ_HANDLED) + num_handled++; + } else { + dev_err(&mc13xxx->spidev->dev, + "BUG: irq %u but no handler\n", + baseirq + irq); + + mask |= 1 << irq; + + ret = mc13xxx_reg_write(mc13xxx, offmask, mask); + } + } + + return num_handled; +} + +static irqreturn_t mc13xxx_irq_thread(int irq, void *data) +{ + struct mc13xxx *mc13xxx = data; + irqreturn_t ret; + int handled = 0; + + mc13xxx_lock(mc13xxx); + + ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT0, + MC13XXX_IRQMASK0, 0); + if (ret > 0) + handled = 1; + + ret = mc13xxx_irq_handle(mc13xxx, MC13XXX_IRQSTAT1, + MC13XXX_IRQMASK1, 24); + if (ret > 0) + handled = 1; + + mc13xxx_unlock(mc13xxx); + + return IRQ_RETVAL(handled); +} + +enum mc13xxx_id { + MC13XXX_ID_MC13783, + MC13XXX_ID_MC13892, + MC13XXX_ID_INVALID, +}; + +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) +{ + u32 icid; + u32 revision; + const char *name; + int ret; + + ret = mc13xxx_reg_read(mc13xxx, 46, &icid); + if (ret) + return ret; + + icid = (icid >> 6) & 0x7; + + switch (icid) { + case 2: + *id = MC13XXX_ID_MC13783; + name = "mc13783"; + break; + case 7: + *id = MC13XXX_ID_MC13892; + name = "mc13892"; + break; + default: + *id = MC13XXX_ID_INVALID; + break; + } + + if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) { + ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision); + if (ret) + return ret; + + dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, " + "fin: %d, fab: %d, icid: %d/%d\n", + mc13xxx_chipname[*id], + maskval(revision, MC13XXX_REVISION_REVFULL), + maskval(revision, MC13XXX_REVISION_REVMETAL), + maskval(revision, MC13XXX_REVISION_FIN), + maskval(revision, MC13XXX_REVISION_FAB), + maskval(revision, MC13XXX_REVISION_ICID), + maskval(revision, MC13XXX_REVISION_ICIDCODE)); + } + + 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; +} + +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]; +} + +#include + +int mc13xxx_get_flags(struct mc13xxx *mc13xxx) +{ + struct mc13xxx_platform_data *pdata = + dev_get_platdata(&mc13xxx->spidev->dev); + + return pdata->flags; +} +EXPORT_SYMBOL(mc13xxx_get_flags); + +#define MC13783_ADC1_CHAN0_SHIFT 5 +#define MC13783_ADC1_CHAN1_SHIFT 8 + +struct mc13xxx_adcdone_data { + struct mc13xxx *mc13xxx; + struct completion done; +}; + +static irqreturn_t mc13783_handler_adcdone(int irq, void *data) +{ + struct mc13xxx_adcdone_data *adcdone_data = data; + + mc13xxx_irq_ack(adcdone_data->mc13xxx, irq); + + complete_all(&adcdone_data->done); + + return IRQ_HANDLED; +} + +#define MC13783_ADC_WORKING (1 << 0) + +int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, + unsigned int channel, unsigned int *sample) +{ + struct mc13xxx *mc13xxx = &mc13783->mc13xxx; + u32 adc0, adc1, old_adc0; + int i, ret; + struct mc13xxx_adcdone_data adcdone_data = { + .mc13xxx = mc13xxx, + }; + init_completion(&adcdone_data.done); + + dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__); + + mc13xxx_lock(mc13xxx); + + if (mc13783->adcflags & MC13783_ADC_WORKING) { + ret = -EBUSY; + goto out; + } + + mc13783->adcflags |= MC13783_ADC_WORKING; + + mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0); + + adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2; + adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC; + + if (channel > 7) + adc1 |= MC13783_ADC1_ADSEL; + + switch (mode) { + case MC13783_ADC_MODE_TS: + adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 | + MC13783_ADC0_TSMOD1; + adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; + break; + + case MC13783_ADC_MODE_SINGLE_CHAN: + adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; + adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT; + adc1 |= MC13783_ADC1_RAND; + break; + + case MC13783_ADC_MODE_MULT_CHAN: + adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; + adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; + break; + + default: + mc13783_unlock(mc13783); + return -EINVAL; + } + + dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__); + mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE, + mc13783_handler_adcdone, __func__, &adcdone_data); + mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE); + + mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0); + mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1); + + mc13xxx_unlock(mc13xxx); + + ret = wait_for_completion_interruptible_timeout(&adcdone_data.done, HZ); + + if (!ret) + ret = -ETIMEDOUT; + + mc13xxx_lock(mc13xxx); + + mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data); + + if (ret > 0) + for (i = 0; i < 4; ++i) { + ret = mc13xxx_reg_read(mc13xxx, + MC13783_ADC2, &sample[i]); + if (ret) + break; + } + + if (mode == MC13783_ADC_MODE_TS) + /* restore TSMOD */ + mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0); + + mc13783->adcflags &= ~MC13783_ADC_WORKING; +out: + mc13xxx_unlock(mc13xxx); + + return ret; +} +EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); + +static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, + const char *format, void *pdata, size_t pdata_size) +{ + char buf[30]; + const char *name = mc13xxx_get_chipname(mc13xxx); + + struct mfd_cell cell = { + .platform_data = pdata, + .data_size = pdata_size, + }; + + /* there is no asnprintf in the kernel :-( */ + if (snprintf(buf, sizeof(buf), format, name) > sizeof(buf)) + return -E2BIG; + + cell.name = kmemdup(buf, strlen(buf) + 1, GFP_KERNEL); + if (!cell.name) + return -ENOMEM; + + return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0); +} + +static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) +{ + return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0); +} + +static int mc13xxx_probe(struct spi_device *spi) +{ + struct mc13xxx *mc13xxx; + struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev); + enum mc13xxx_id id; + int ret; + + 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) + goto err_revision; + + /* mask all irqs */ + ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK0, 0x00ffffff); + if (ret) + goto err_mask; + + ret = mc13xxx_reg_write(mc13xxx, MC13XXX_IRQMASK1, 0x00ffffff); + if (ret) + goto err_mask; + + ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx); + + if (ret) { +err_mask: +err_revision: + mutex_unlock(&mc13xxx->lock); + dev_set_drvdata(&spi->dev, NULL); + kfree(mc13xxx); + return ret; + } + + mc13xxx_unlock(mc13xxx); + + if (pdata->flags & MC13XXX_USE_ADC) + mc13xxx_add_subdevice(mc13xxx, "%s-adc"); + + if (pdata->flags & MC13XXX_USE_CODEC) + mc13xxx_add_subdevice(mc13xxx, "%s-codec"); + + if (pdata->flags & MC13XXX_USE_REGULATOR) { + struct mc13xxx_regulator_platform_data regulator_pdata = { + .num_regulators = pdata->num_regulators, + .regulators = pdata->regulators, + }; + + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", + ®ulator_pdata, sizeof(regulator_pdata)); + } + + if (pdata->flags & MC13XXX_USE_RTC) + mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); + + if (pdata->flags & MC13XXX_USE_TOUCHSCREEN) + mc13xxx_add_subdevice(mc13xxx, "%s-ts"); + + if (pdata->flags & MC13XXX_USE_LED) { + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", + pdata->leds, sizeof(*pdata->leds)); + } + + return 0; +} + +static int __devexit mc13xxx_remove(struct spi_device *spi) +{ + struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); + + free_irq(mc13xxx->spidev->irq, mc13xxx); + + mfd_remove_devices(&spi->dev); + + return 0; +} + +static const struct spi_device_id mc13xxx_device_id[] = { + { + .name = "mc13783", + .driver_data = MC13XXX_ID_MC13783, + }, { + .name = "mc13892", + .driver_data = MC13XXX_ID_MC13892, + }, { + /* sentinel */ + } +}; + +static struct spi_driver mc13xxx_driver = { + .id_table = mc13xxx_device_id, + .driver = { + .name = "mc13xxx", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .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); + +MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC"); +MODULE_AUTHOR("Uwe Kleine-Koenig "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index 5f6aff55eeb7..b4c741e352c2 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -1,5 +1,5 @@ /* - * Copyright 2009 Pengutronix + * Copyright 2009-2010 Pengutronix * Uwe Kleine-Koenig * * This program is free software; you can redistribute it and/or modify it under @@ -9,31 +9,84 @@ #ifndef __LINUX_MFD_MC13783_H #define __LINUX_MFD_MC13783_H -#include +#include struct mc13783; -void mc13783_lock(struct mc13783 *mc13783); -void mc13783_unlock(struct mc13783 *mc13783); - -int mc13783_reg_read(struct mc13783 *mc13783, unsigned int offset, u32 *val); -int mc13783_reg_write(struct mc13783 *mc13783, unsigned int offset, u32 val); -int mc13783_reg_rmw(struct mc13783 *mc13783, unsigned int offset, - u32 mask, u32 val); - -int mc13783_get_flags(struct mc13783 *mc13783); - -int mc13783_irq_request(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev); -int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev); -int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev); - -int mc13783_irq_mask(struct mc13783 *mc13783, int irq); -int mc13783_irq_unmask(struct mc13783 *mc13783, int irq); -int mc13783_irq_status(struct mc13783 *mc13783, int irq, - int *enabled, int *pending); -int mc13783_irq_ack(struct mc13783 *mc13783, int irq); +struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783); + +static inline void mc13783_lock(struct mc13783 *mc13783) +{ + mc13xxx_lock(mc13783_to_mc13xxx(mc13783)); +} + +static inline void mc13783_unlock(struct mc13783 *mc13783) +{ + mc13xxx_unlock(mc13783_to_mc13xxx(mc13783)); +} + +static inline int mc13783_reg_read(struct mc13783 *mc13783, + unsigned int offset, u32 *val) +{ + return mc13xxx_reg_read(mc13783_to_mc13xxx(mc13783), offset, val); +} + +static inline int mc13783_reg_write(struct mc13783 *mc13783, + unsigned int offset, u32 val) +{ + return mc13xxx_reg_write(mc13783_to_mc13xxx(mc13783), offset, val); +} + +static inline int mc13783_reg_rmw(struct mc13783 *mc13783, + unsigned int offset, u32 mask, u32 val) +{ + return mc13xxx_reg_rmw(mc13783_to_mc13xxx(mc13783), offset, mask, val); +} + +static inline int mc13783_get_flags(struct mc13783 *mc13783) +{ + return mc13xxx_get_flags(mc13783_to_mc13xxx(mc13783)); +} + +static inline int mc13783_irq_request(struct mc13783 *mc13783, int irq, + irq_handler_t handler, const char *name, void *dev) +{ + return mc13xxx_irq_request(mc13783_to_mc13xxx(mc13783), irq, + handler, name, dev); +} + +static inline int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq, + irq_handler_t handler, const char *name, void *dev) +{ + return mc13xxx_irq_request_nounmask(mc13783_to_mc13xxx(mc13783), irq, + handler, name, dev); +} + +static inline int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev) +{ + return mc13xxx_irq_free(mc13783_to_mc13xxx(mc13783), irq, dev); +} + +static inline int mc13783_irq_mask(struct mc13783 *mc13783, int irq) +{ + return mc13xxx_irq_mask(mc13783_to_mc13xxx(mc13783), irq); +} + +static inline int mc13783_irq_unmask(struct mc13783 *mc13783, int irq) +{ + return mc13xxx_irq_unmask(mc13783_to_mc13xxx(mc13783), irq); +} +static inline int mc13783_irq_status(struct mc13783 *mc13783, int irq, + int *enabled, int *pending) +{ + return mc13xxx_irq_status(mc13783_to_mc13xxx(mc13783), + irq, enabled, pending); +} + +static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) +{ + return mc13xxx_irq_ack(mc13783_to_mc13xxx(mc13783), irq); +} #define MC13783_ADC0 43 #define MC13783_ADC0_ADREFEN (1 << 10) @@ -48,96 +101,18 @@ int mc13783_irq_ack(struct mc13783 *mc13783, int irq); MC13783_ADC0_TSMOD1 | \ MC13783_ADC0_TSMOD2) -struct mc13783_led_platform_data { -#define MC13783_LED_MD 0 -#define MC13783_LED_AD 1 -#define MC13783_LED_KP 2 -#define MC13783_LED_R1 3 -#define MC13783_LED_G1 4 -#define MC13783_LED_B1 5 -#define MC13783_LED_R2 6 -#define MC13783_LED_G2 7 -#define MC13783_LED_B2 8 -#define MC13783_LED_R3 9 -#define MC13783_LED_G3 10 -#define MC13783_LED_B3 11 -#define MC13783_LED_MAX MC13783_LED_B3 - int id; - const char *name; - const char *default_trigger; - -/* Three or two bits current selection depending on the led */ - char max_current; -}; - -struct mc13783_leds_platform_data { - int num_leds; - struct mc13783_led_platform_data *led; - -#define MC13783_LED_TRIODE_MD (1 << 0) -#define MC13783_LED_TRIODE_AD (1 << 1) -#define MC13783_LED_TRIODE_KP (1 << 2) -#define MC13783_LED_BOOST_EN (1 << 3) -#define MC13783_LED_TC1HALF (1 << 4) -#define MC13783_LED_SLEWLIMTC (1 << 5) -#define MC13783_LED_SLEWLIMBL (1 << 6) -#define MC13783_LED_TRIODE_TC1 (1 << 7) -#define MC13783_LED_TRIODE_TC2 (1 << 8) -#define MC13783_LED_TRIODE_TC3 (1 << 9) - int flags; - -#define MC13783_LED_AB_DISABLED 0 -#define MC13783_LED_AB_MD1 1 -#define MC13783_LED_AB_MD12 2 -#define MC13783_LED_AB_MD123 3 -#define MC13783_LED_AB_MD1234 4 -#define MC13783_LED_AB_MD1234_AD1 5 -#define MC13783_LED_AB_MD1234_AD12 6 -#define MC13783_LED_AB_MD1_AD 7 - char abmode; - -#define MC13783_LED_ABREF_200MV 0 -#define MC13783_LED_ABREF_400MV 1 -#define MC13783_LED_ABREF_600MV 2 -#define MC13783_LED_ABREF_800MV 3 - char abref; - -#define MC13783_LED_PERIOD_10MS 0 -#define MC13783_LED_PERIOD_100MS 1 -#define MC13783_LED_PERIOD_500MS 2 -#define MC13783_LED_PERIOD_2S 3 - char bl_period; - char tc1_period; - char tc2_period; - char tc3_period; -}; - -/* to be cleaned up */ -struct regulator_init_data; - -struct mc13783_regulator_init_data { - int id; - struct regulator_init_data *init_data; -}; - -struct mc13783_regulator_platform_data { - int num_regulators; - struct mc13783_regulator_init_data *regulators; -}; - -struct mc13783_platform_data { - int num_regulators; - struct mc13783_regulator_init_data *regulators; - struct mc13783_leds_platform_data *leds; - -#define MC13783_USE_TOUCHSCREEN (1 << 0) -#define MC13783_USE_CODEC (1 << 1) -#define MC13783_USE_ADC (1 << 2) -#define MC13783_USE_RTC (1 << 3) -#define MC13783_USE_REGULATOR (1 << 4) -#define MC13783_USE_LED (1 << 5) - unsigned int flags; -}; +#define mc13783_regulator_init_data mc13xxx_regulator_init_data +#define mc13783_regulator_platform_data mc13xxx_regulator_platform_data +#define mc13783_led_platform_data mc13xxx_led_platform_data +#define mc13783_leds_platform_data mc13xxx_leds_platform_data + +#define mc13783_platform_data mc13xxx_platform_data +#define MC13783_USE_TOUCHSCREEN MC13XXX_USE_TOUCHSCREEN +#define MC13783_USE_CODEC MC13XXX_USE_CODEC +#define MC13783_USE_ADC MC13XXX_USE_ADC +#define MC13783_USE_RTC MC13XXX_USE_RTC +#define MC13783_USE_REGULATOR MC13XXX_USE_REGULATOR +#define MC13783_USE_LED MC13XXX_USE_LED #define MC13783_ADC_MODE_TS 1 #define MC13783_ADC_MODE_SINGLE_CHAN 2 @@ -181,46 +156,46 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, #define MC13783_REGU_PWGT1SPI 31 #define MC13783_REGU_PWGT2SPI 32 -#define MC13783_IRQ_ADCDONE 0 -#define MC13783_IRQ_ADCBISDONE 1 -#define MC13783_IRQ_TS 2 +#define MC13783_IRQ_ADCDONE MC13XXX_IRQ_ADCDONE +#define MC13783_IRQ_ADCBISDONE MC13XXX_IRQ_ADCBISDONE +#define MC13783_IRQ_TS MC13XXX_IRQ_TS #define MC13783_IRQ_WHIGH 3 #define MC13783_IRQ_WLOW 4 -#define MC13783_IRQ_CHGDET 6 +#define MC13783_IRQ_CHGDET MC13XXX_IRQ_CHGDET #define MC13783_IRQ_CHGOV 7 -#define MC13783_IRQ_CHGREV 8 -#define MC13783_IRQ_CHGSHORT 9 -#define MC13783_IRQ_CCCV 10 -#define MC13783_IRQ_CHGCURR 11 -#define MC13783_IRQ_BPON 12 -#define MC13783_IRQ_LOBATL 13 -#define MC13783_IRQ_LOBATH 14 +#define MC13783_IRQ_CHGREV MC13XXX_IRQ_CHGREV +#define MC13783_IRQ_CHGSHORT MC13XXX_IRQ_CHGSHORT +#define MC13783_IRQ_CCCV MC13XXX_IRQ_CCCV +#define MC13783_IRQ_CHGCURR MC13XXX_IRQ_CHGCURR +#define MC13783_IRQ_BPON MC13XXX_IRQ_BPON +#define MC13783_IRQ_LOBATL MC13XXX_IRQ_LOBATL +#define MC13783_IRQ_LOBATH MC13XXX_IRQ_LOBATH #define MC13783_IRQ_UDP 15 #define MC13783_IRQ_USB 16 #define MC13783_IRQ_ID 19 #define MC13783_IRQ_SE1 21 #define MC13783_IRQ_CKDET 22 #define MC13783_IRQ_UDM 23 -#define MC13783_IRQ_1HZ 24 -#define MC13783_IRQ_TODA 25 +#define MC13783_IRQ_1HZ MC13XXX_IRQ_1HZ +#define MC13783_IRQ_TODA MC13XXX_IRQ_TODA #define MC13783_IRQ_ONOFD1 27 #define MC13783_IRQ_ONOFD2 28 #define MC13783_IRQ_ONOFD3 29 -#define MC13783_IRQ_SYSRST 30 -#define MC13783_IRQ_RTCRST 31 -#define MC13783_IRQ_PC 32 -#define MC13783_IRQ_WARM 33 -#define MC13783_IRQ_MEMHLD 34 +#define MC13783_IRQ_SYSRST MC13XXX_IRQ_SYSRST +#define MC13783_IRQ_RTCRST MC13XXX_IRQ_RTCRST +#define MC13783_IRQ_PC MC13XXX_IRQ_PC +#define MC13783_IRQ_WARM MC13XXX_IRQ_WARM +#define MC13783_IRQ_MEMHLD MC13XXX_IRQ_MEMHLD #define MC13783_IRQ_PWRRDY 35 -#define MC13783_IRQ_THWARNL 36 -#define MC13783_IRQ_THWARNH 37 -#define MC13783_IRQ_CLK 38 +#define MC13783_IRQ_THWARNL MC13XXX_IRQ_THWARNL +#define MC13783_IRQ_THWARNH MC13XXX_IRQ_THWARNH +#define MC13783_IRQ_CLK MC13XXX_IRQ_CLK #define MC13783_IRQ_SEMAF 39 #define MC13783_IRQ_MC2B 41 #define MC13783_IRQ_HSDET 42 #define MC13783_IRQ_HSL 43 #define MC13783_IRQ_ALSPTH 44 #define MC13783_IRQ_AHSSHORT 45 -#define MC13783_NUM_IRQ 46 +#define MC13783_NUM_IRQ MC13XXX_NUM_IRQ -#endif /* __LINUX_MFD_MC13783_H */ +#endif /* ifndef __LINUX_MFD_MC13783_H */ diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h new file mode 100644 index 000000000000..a1d391b40e68 --- /dev/null +++ b/include/linux/mfd/mc13xxx.h @@ -0,0 +1,154 @@ +/* + * Copyright 2009-2010 Pengutronix + * Uwe Kleine-Koenig + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + */ +#ifndef __LINUX_MFD_MC13XXX_H +#define __LINUX_MFD_MC13XXX_H + +#include + +struct mc13xxx; + +void mc13xxx_lock(struct mc13xxx *mc13xxx); +void mc13xxx_unlock(struct mc13xxx *mc13xxx); + +int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val); +int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val); +int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset, + u32 mask, u32 val); + +int mc13xxx_get_flags(struct mc13xxx *mc13xxx); + +int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq, + irq_handler_t handler, const char *name, void *dev); +int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq, + irq_handler_t handler, const char *name, void *dev); +int mc13xxx_irq_free(struct mc13xxx *mc13xxx, int irq, void *dev); + +int mc13xxx_irq_mask(struct mc13xxx *mc13xxx, int irq); +int mc13xxx_irq_unmask(struct mc13xxx *mc13xxx, int irq); +int mc13xxx_irq_status(struct mc13xxx *mc13xxx, int irq, + int *enabled, int *pending); +int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq); + +int mc13xxx_get_flags(struct mc13xxx *mc13xxx); + +#define MC13XXX_IRQ_ADCDONE 0 +#define MC13XXX_IRQ_ADCBISDONE 1 +#define MC13XXX_IRQ_TS 2 +#define MC13XXX_IRQ_CHGDET 6 +#define MC13XXX_IRQ_CHGREV 8 +#define MC13XXX_IRQ_CHGSHORT 9 +#define MC13XXX_IRQ_CCCV 10 +#define MC13XXX_IRQ_CHGCURR 11 +#define MC13XXX_IRQ_BPON 12 +#define MC13XXX_IRQ_LOBATL 13 +#define MC13XXX_IRQ_LOBATH 14 +#define MC13XXX_IRQ_1HZ 24 +#define MC13XXX_IRQ_TODA 25 +#define MC13XXX_IRQ_SYSRST 30 +#define MC13XXX_IRQ_RTCRST 31 +#define MC13XXX_IRQ_PC 32 +#define MC13XXX_IRQ_WARM 33 +#define MC13XXX_IRQ_MEMHLD 34 +#define MC13XXX_IRQ_THWARNL 36 +#define MC13XXX_IRQ_THWARNH 37 +#define MC13XXX_IRQ_CLK 38 + +#define MC13XXX_NUM_IRQ 46 + +struct regulator_init_data; + +struct mc13xxx_regulator_init_data { + int id; + struct regulator_init_data *init_data; +}; + +struct mc13xxx_regulator_platform_data { + int num_regulators; + struct mc13xxx_regulator_init_data *regulators; +}; + +struct mc13xxx_led_platform_data { +#define MC13783_LED_MD 0 +#define MC13783_LED_AD 1 +#define MC13783_LED_KP 2 +#define MC13783_LED_R1 3 +#define MC13783_LED_G1 4 +#define MC13783_LED_B1 5 +#define MC13783_LED_R2 6 +#define MC13783_LED_G2 7 +#define MC13783_LED_B2 8 +#define MC13783_LED_R3 9 +#define MC13783_LED_G3 10 +#define MC13783_LED_B3 11 +#define MC13783_LED_MAX MC13783_LED_B3 + int id; + const char *name; + const char *default_trigger; + +/* Three or two bits current selection depending on the led */ + char max_current; +}; + +struct mc13xxx_leds_platform_data { + int num_leds; + struct mc13xxx_led_platform_data *led; + +#define MC13783_LED_TRIODE_MD (1 << 0) +#define MC13783_LED_TRIODE_AD (1 << 1) +#define MC13783_LED_TRIODE_KP (1 << 2) +#define MC13783_LED_BOOST_EN (1 << 3) +#define MC13783_LED_TC1HALF (1 << 4) +#define MC13783_LED_SLEWLIMTC (1 << 5) +#define MC13783_LED_SLEWLIMBL (1 << 6) +#define MC13783_LED_TRIODE_TC1 (1 << 7) +#define MC13783_LED_TRIODE_TC2 (1 << 8) +#define MC13783_LED_TRIODE_TC3 (1 << 9) + int flags; + +#define MC13783_LED_AB_DISABLED 0 +#define MC13783_LED_AB_MD1 1 +#define MC13783_LED_AB_MD12 2 +#define MC13783_LED_AB_MD123 3 +#define MC13783_LED_AB_MD1234 4 +#define MC13783_LED_AB_MD1234_AD1 5 +#define MC13783_LED_AB_MD1234_AD12 6 +#define MC13783_LED_AB_MD1_AD 7 + char abmode; + +#define MC13783_LED_ABREF_200MV 0 +#define MC13783_LED_ABREF_400MV 1 +#define MC13783_LED_ABREF_600MV 2 +#define MC13783_LED_ABREF_800MV 3 + char abref; + +#define MC13783_LED_PERIOD_10MS 0 +#define MC13783_LED_PERIOD_100MS 1 +#define MC13783_LED_PERIOD_500MS 2 +#define MC13783_LED_PERIOD_2S 3 + char bl_period; + char tc1_period; + char tc2_period; + char tc3_period; +}; + +struct mc13xxx_platform_data { +#define MC13XXX_USE_TOUCHSCREEN (1 << 0) +#define MC13XXX_USE_CODEC (1 << 1) +#define MC13XXX_USE_ADC (1 << 2) +#define MC13XXX_USE_RTC (1 << 3) +#define MC13XXX_USE_REGULATOR (1 << 4) +#define MC13XXX_USE_LED (1 << 5) + unsigned int flags; + + int num_regulators; + struct mc13xxx_regulator_init_data *regulators; + struct mc13xxx_leds_platform_data *leds; +}; + +#endif /* ifndef __LINUX_MFD_MC13XXX_H */ -- cgit v1.2.3 From 72f2e2c763edc41f8eead042b6ff933acb0378e2 Mon Sep 17 00:00:00 2001 From: kishore kadiyala Date: Fri, 24 Sep 2010 17:13:20 +0000 Subject: mfd: Adding twl6030 mmc card detect support for MMC1 Adding card detect callback function and card detect configuration function for MMC1 Controller on OMAP4. Card detect configuration function does initial configuration of the MMC Control & PullUp-PullDown registers of Phoenix. For MMC1 Controller, card detect interrupt source is twl6030 which is non-gpio. The card detect call back function provides card present/absent status by reading MMC Control register present on twl6030. Since OMAP4 doesn't use any GPIO line as used in OMAP3 for card detect, the suspend/resume initialization which was done in omap_hsmmc_gpio_init previously is moved to the probe thus making it generic for both OMAP3 & OMAP4. Cc: Tony Lindgren Cc: Andrew Morton Cc: Madhusudhan Chikkature Cc: Adrian Hunter Signed-off-by: Kishore Kadiyala Signed-off-by: Samuel Ortiz --- arch/arm/mach-omap2/board-4430sdp.c | 7 +++- drivers/mfd/twl6030-irq.c | 73 +++++++++++++++++++++++++++++++++++++ drivers/mmc/host/omap_hsmmc.c | 4 +- include/linux/i2c/twl.h | 31 ++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 69a4ae971e41..df5a425a49d1 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -269,9 +269,14 @@ static int omap4_twl6030_hsmmc_late_init(struct device *dev) struct omap_mmc_platform_data *pdata = dev->platform_data; /* Setting MMC1 Card detect Irq */ - if (pdev->id == 0) + if (pdev->id == 0) { + ret = twl6030_mmc_card_detect_config(); + if (ret) + pr_err("Failed configuring MMC1 card detect\n"); pdata->slots[0].card_detect_irq = TWL6030_IRQ_BASE + MMCDETECT_INTR_OFFSET; + pdata->slots[0].card_detect = twl6030_mmc_card_detect; + } return ret; } diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index 10bf228ad626..2d3bb82dbf24 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -36,6 +36,7 @@ #include #include #include +#include /* * TWL6030 (unlike its predecessors, which had two level interrupt handling) @@ -223,6 +224,78 @@ int twl6030_interrupt_mask(u8 bit_mask, u8 offset) } EXPORT_SYMBOL(twl6030_interrupt_mask); +int twl6030_mmc_card_detect_config(void) +{ + int ret; + u8 reg_val = 0; + + /* Unmasking the Card detect Interrupt line for MMC1 from Phoenix */ + twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, + REG_INT_MSK_LINE_B); + twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, + REG_INT_MSK_STS_B); + /* + * Intially Configuring MMC_CTRL for receving interrupts & + * Card status on TWL6030 for MMC1 + */ + ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, TWL6030_MMCCTRL); + if (ret < 0) { + pr_err("twl6030: Failed to read MMCCTRL, error %d\n", ret); + return ret; + } + reg_val &= ~VMMC_AUTO_OFF; + reg_val |= SW_FC; + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, TWL6030_MMCCTRL); + if (ret < 0) { + pr_err("twl6030: Failed to write MMCCTRL, error %d\n", ret); + return ret; + } + + /* Configuring PullUp-PullDown register */ + ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, + TWL6030_CFG_INPUT_PUPD3); + if (ret < 0) { + pr_err("twl6030: Failed to read CFG_INPUT_PUPD3, error %d\n", + ret); + return ret; + } + reg_val &= ~(MMC_PU | MMC_PD); + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, reg_val, + TWL6030_CFG_INPUT_PUPD3); + if (ret < 0) { + pr_err("twl6030: Failed to write CFG_INPUT_PUPD3, error %d\n", + ret); + return ret; + } + return 0; +} +EXPORT_SYMBOL(twl6030_mmc_card_detect_config); + +int twl6030_mmc_card_detect(struct device *dev, int slot) +{ + int ret = -EIO; + u8 read_reg = 0; + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->id) { + /* TWL6030 provide's Card detect support for + * only MMC1 controller. + */ + pr_err("Unkown MMC controller %d in %s\n", pdev->id, __func__); + return ret; + } + /* + * BIT0 of MMC_CTRL on TWL6030 provides card status for MMC1 + * 0 - Card not present ,1 - Card present + */ + ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, &read_reg, + TWL6030_MMCCTRL); + if (ret >= 0) + ret = read_reg & STS_MMC; + return ret; +} +EXPORT_SYMBOL(twl6030_mmc_card_detect); + int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) { diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e865032a52eb..82a1079bbdc7 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -483,8 +483,6 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata) int ret; if (gpio_is_valid(pdata->slots[0].switch_pin)) { - pdata->suspend = omap_hsmmc_suspend_cdirq; - pdata->resume = omap_hsmmc_resume_cdirq; if (pdata->slots[0].cover) pdata->slots[0].get_cover_state = omap_hsmmc_get_cover_state; @@ -2218,6 +2216,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) "Unable to grab MMC CD IRQ\n"); goto err_irq_cd; } + pdata->suspend = omap_hsmmc_suspend_cdirq; + pdata->resume = omap_hsmmc_resume_cdirq; } omap_hsmmc_disable_irq(host); diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 53089516c17a..c760991b354a 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -141,6 +141,16 @@ #define TWL6030_CHARGER_CTRL_INT_MASK 0x10 #define TWL6030_CHARGER_FAULT_INT_MASK 0x60 +#define TWL6030_MMCCTRL 0xEE +#define VMMC_AUTO_OFF (0x1 << 3) +#define SW_FC (0x1 << 2) +#define STS_MMC 0x1 + +#define TWL6030_CFG_INPUT_PUPD3 0xF2 +#define MMC_PU (0x1 << 3) +#define MMC_PD (0x1 << 2) + + #define TWL4030_CLASS_ID 0x4030 #define TWL6030_CLASS_ID 0x6030 @@ -173,6 +183,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl6030_interrupt_unmask(u8 bit_mask, u8 offset); int twl6030_interrupt_mask(u8 bit_mask, u8 offset); +/* Card detect Configuration for MMC1 Controller on OMAP4 */ +#ifdef CONFIG_TWL4030_CORE +int twl6030_mmc_card_detect_config(void); +#else +static inline int twl6030_mmc_card_detect_config(void) +{ + pr_debug("twl6030_mmc_card_detect_config not supported\n"); + return 0; +} +#endif + +/* MMC1 Controller on OMAP4 uses Phoenix irq for Card detect */ +#ifdef CONFIG_TWL4030_CORE +int twl6030_mmc_card_detect(struct device *dev, int slot); +#else +static inline int twl6030_mmc_card_detect(struct device *dev, int slot) +{ + pr_debug("Call back twl6030_mmc_card_detect not supported\n"); + return -EIO; +} +#endif /*----------------------------------------------------------------------*/ /* -- cgit v1.2.3 From 509bd4764c110b89bb3d09a5b6621fd31dc58044 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 27 Sep 2010 14:32:24 +0200 Subject: mfd: Support for ICs compliant with max8998 Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Acked-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/mfd/max8998.c | 5 +++-- include/linux/mfd/max8998-private.h | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 310fd8054f35..a720f412cd15 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -133,6 +133,7 @@ static int max8998_i2c_probe(struct i2c_client *i2c, max8998->dev = &i2c->dev; max8998->i2c = i2c; max8998->irq = i2c->irq; + max8998->type = id->driver_data; if (pdata) { max8998->ono = pdata->ono; max8998->irq_base = pdata->irq_base; @@ -169,8 +170,8 @@ static int max8998_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id max8998_i2c_id[] = { - { "max8998", 0 }, - { "lp3974", 0 }, + { "max8998", TYPE_MAX8998 }, + { "lp3974", TYPE_LP3974}, { } }; MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index 170f665c7cdd..0ff42116d5dd 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -101,6 +101,13 @@ enum { MAX8998_IRQ_NR, }; +/* MAX8998 various variants */ +enum { + TYPE_MAX8998 = 0, /* Default */ + TYPE_LP3974, /* National version of MAX8998 */ + TYPE_LP3979, /* Added AVS */ +}; + #define MAX8998_IRQ_DCINF_MASK (1 << 2) #define MAX8998_IRQ_DCINR_MASK (1 << 3) #define MAX8998_IRQ_JIGF_MASK (1 << 4) @@ -123,6 +130,8 @@ enum { #define MAX8998_IRQ_LOBAT1_MASK (1 << 0) #define MAX8998_IRQ_LOBAT2_MASK (1 << 1) +#define MAX8998_ENRAMP (1 << 4) + /** * struct max8998_dev - max8998 master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) @@ -135,6 +144,7 @@ enum { * @ono: power onoff IRQ number for max8998 * @irq_masks_cur: currently active value * @irq_masks_cache: cached hardware value + * @type: indicate which max8998 "variant" is used */ struct max8998_dev { struct device *dev; @@ -148,6 +158,7 @@ struct max8998_dev { int ono; u8 irq_masks_cur[MAX8998_NUM_IRQ_REGS]; u8 irq_masks_cache[MAX8998_NUM_IRQ_REGS]; + int type; }; int max8998_irq_init(struct max8998_dev *max8998); -- cgit v1.2.3 From 889cd5a60f880e0a56b7b769d0b74eb222e6896c Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 27 Sep 2010 14:32:25 +0200 Subject: regulator: max8998 BUCK1/2 internal voltages and indexes defined BUCK1/2 internal voltages and indexes defined in the struct max8998_data max_get_voltage_register now uses index values to chose proper register More generic BUCK1/2 registers names provided Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Acked-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- drivers/regulator/max8998.c | 10 ++++++++-- include/linux/mfd/max8998-private.h | 12 ++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 1e5bd504fbad..7ae0639c1394 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -39,6 +39,11 @@ struct max8998_data { struct max8998_dev *iodev; int num_regulators; struct regulator_dev **rdev; + u8 buck1_vol[4]; /* voltages for selection */ + u8 buck2_vol[2]; + unsigned int buck1_idx; /* index to last changed voltage */ + /* value in a set */ + unsigned int buck2_idx; }; struct voltage_map_desc { @@ -218,6 +223,7 @@ static int max8998_get_voltage_register(struct regulator_dev *rdev, int *_reg, int *_shift, int *_mask) { int ldo = max8998_get_ldo(rdev); + struct max8998_data *max8998 = rdev_get_drvdata(rdev); int reg, shift = 0, mask = 0xff; switch (ldo) { @@ -254,10 +260,10 @@ static int max8998_get_voltage_register(struct regulator_dev *rdev, reg = MAX8998_REG_LDO12 + (ldo - MAX8998_LDO12); break; case MAX8998_BUCK1: - reg = MAX8998_REG_BUCK1_DVSARM1; + reg = MAX8998_REG_BUCK1_VOLTAGE1 + max8998->buck1_idx; break; case MAX8998_BUCK2: - reg = MAX8998_REG_BUCK2_DVSINT1; + reg = MAX8998_REG_BUCK2_VOLTAGE1 + max8998->buck2_idx; break; case MAX8998_BUCK3: reg = MAX8998_REG_BUCK3; diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index 0ff42116d5dd..7363dea6bbcd 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -48,12 +48,12 @@ enum { MAX8998_REG_ONOFF2, MAX8998_REG_ONOFF3, MAX8998_REG_ONOFF4, - MAX8998_REG_BUCK1_DVSARM1, - MAX8998_REG_BUCK1_DVSARM2, - MAX8998_REG_BUCK1_DVSARM3, - MAX8998_REG_BUCK1_DVSARM4, - MAX8998_REG_BUCK2_DVSINT1, - MAX8998_REG_BUCK2_DVSINT2, + MAX8998_REG_BUCK1_VOLTAGE1, + MAX8998_REG_BUCK1_VOLTAGE2, + MAX8998_REG_BUCK1_VOLTAGE3, + MAX8998_REG_BUCK1_VOLTAGE4, + MAX8998_REG_BUCK2_VOLTAGE1, + MAX8998_REG_BUCK2_VOLTAGE2, MAX8998_REG_BUCK3, MAX8998_REG_BUCK4, MAX8998_REG_LDO2_LDO3, -- cgit v1.2.3 From 58aa6334fbf5cf420a47cfd2718a0b299f40a379 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 27 Sep 2010 14:32:26 +0200 Subject: mfd: Voltages and GPIOs platform_data definitions for max8998 Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Acked-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Samuel Ortiz --- include/linux/mfd/max8998.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index d47ed4c190fe..f8c9f884aff2 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -70,12 +70,24 @@ struct max8998_regulator_data { * @num_regulators: number of regultors used * @irq_base: base IRQ number for max8998, required for IRQs * @ono: power onoff IRQ number for max8998 + * @buck1_max_voltage1: BUCK1 maximum alowed voltage register 1 + * @buck1_max_voltage2: BUCK1 maximum alowed voltage register 2 + * @buck2_max_voltage: BUCK2 maximum alowed voltage + * @buck1_set1: BUCK1 gpio pin 1 to set output voltage + * @buck1_set2: BUCK1 gpio pin 2 to set output voltage + * @buck2_set3: BUCK2 gpio pin to set output voltage */ struct max8998_platform_data { struct max8998_regulator_data *regulators; int num_regulators; int irq_base; int ono; + int buck1_max_voltage1; + int buck1_max_voltage2; + int buck2_max_voltage; + int buck1_set1; + int buck1_set2; + int buck2_set3; }; #endif /* __LINUX_MFD_MAX8998_H */ -- cgit v1.2.3 From e5b486841d572c5ac83c798f82f4f67cbbac5320 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Oct 2010 23:57:56 +0200 Subject: mfd: Factor out WM831x I2C I/O from the core driver In preparation for the addition of SPI support for the WM831x move the I2C specific code into a separate file with a separate Kconfig option so the I2C support can be excluded from the build. Also update the 1133-EV1 PMIC module support for SMDK6410 to use the new symbol. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- arch/arm/mach-s3c64xx/Kconfig | 1 + drivers/mfd/Kconfig | 15 +++-- drivers/mfd/Makefile | 1 + drivers/mfd/wm831x-core.c | 138 ++------------------------------------ drivers/mfd/wm831x-i2c.c | 143 ++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm831x/core.h | 12 ++++ 6 files changed, 171 insertions(+), 139 deletions(-) create mode 100644 drivers/mfd/wm831x-i2c.c (limited to 'include/linux') diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig index 1e4d78af7d84..546db5cb8929 100644 --- a/arch/arm/mach-s3c64xx/Kconfig +++ b/arch/arm/mach-s3c64xx/Kconfig @@ -185,6 +185,7 @@ config SMDK6410_WM1192_EV1 select REGULATOR_WM831X select S3C24XX_GPIO_EXTRA64 select MFD_WM831X + select MFD_WM831X_I2C help The Wolfson Microelectronics 1192-EV1 is a WM831x based PMIC daughtercard for the Samsung SMDK6410 reference platform. diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6c6b9f02d177..2fb1e892331c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -315,14 +315,19 @@ config MFD_WM8400 the functionality of the device. config MFD_WM831X - bool "Support Wolfson Microelectronics WM831x/2x PMICs" + bool + depends on GENERIC_HARDIRQS + +config MFD_WM831X_I2C + bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C" select MFD_CORE + select MFD_WM831X depends on I2C=y && GENERIC_HARDIRQS help - Support for the Wolfson Microelecronics WM831x and WM832x PMICs. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. + Support for the Wolfson Microelecronics WM831x and WM832x PMICs + when controlled using I2C. 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_WM8350 bool diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 70b26999dc81..c9ef41b13bf3 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o obj-$(CONFIG_MFD_WM8400) += wm8400-core.o wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o obj-$(CONFIG_MFD_WM831X) += wm831x.o +obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o wm8350-objs += wm8350-irq.o obj-$(CONFIG_MFD_WM8350) += wm8350.o diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index ad36579bc815..7d2563fc15c6 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -90,15 +89,6 @@ int wm831x_isinkv_values[WM831X_ISINK_MAX_ISEL + 1] = { }; EXPORT_SYMBOL_GPL(wm831x_isinkv_values); -enum wm831x_parent { - WM8310 = 0x8310, - WM8311 = 0x8311, - WM8312 = 0x8312, - WM8320 = 0x8320, - WM8321 = 0x8321, - WM8325 = 0x8325, -}; - static int wm831x_reg_locked(struct wm831x *wm831x, unsigned short reg) { if (!wm831x->locked) @@ -1447,7 +1437,7 @@ static struct mfd_cell backlight_devs[] = { /* * Instantiate the generic non-control parts of the device. */ -static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) +int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) { struct wm831x_pdata *pdata = wm831x->dev->platform_data; int rev; @@ -1673,7 +1663,7 @@ err: return ret; } -static void wm831x_device_exit(struct wm831x *wm831x) +void wm831x_device_exit(struct wm831x *wm831x) { wm831x_otp_exit(wm831x); mfd_remove_devices(wm831x->dev); @@ -1683,7 +1673,7 @@ static void wm831x_device_exit(struct wm831x *wm831x) kfree(wm831x); } -static int wm831x_device_suspend(struct wm831x *wm831x) +int wm831x_device_suspend(struct wm831x *wm831x) { int reg, mask; @@ -1719,126 +1709,6 @@ static int wm831x_device_suspend(struct wm831x *wm831x) return 0; } -static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *dest) -{ - struct i2c_client *i2c = wm831x->control_data; - int ret; - u16 r = cpu_to_be16(reg); - - ret = i2c_master_send(i2c, (unsigned char *)&r, 2); - if (ret < 0) - return ret; - if (ret != 2) - return -EIO; - - ret = i2c_master_recv(i2c, dest, bytes); - if (ret < 0) - return ret; - if (ret != bytes) - return -EIO; - return 0; -} - -/* Currently we allocate the write buffer on the stack; this is OK for - * small writes - if we need to do large writes this will need to be - * revised. - */ -static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, - int bytes, void *src) -{ - struct i2c_client *i2c = wm831x->control_data; - unsigned char msg[bytes + 2]; - int ret; - - reg = cpu_to_be16(reg); - memcpy(&msg[0], ®, 2); - memcpy(&msg[2], src, bytes); - - ret = i2c_master_send(i2c, msg, bytes + 2); - if (ret < 0) - return ret; - if (ret < bytes + 2) - return -EIO; - - return 0; -} - -static int wm831x_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct wm831x *wm831x; - - wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); - if (wm831x == NULL) - return -ENOMEM; - - i2c_set_clientdata(i2c, wm831x); - wm831x->dev = &i2c->dev; - wm831x->control_data = i2c; - wm831x->read_dev = wm831x_i2c_read_device; - wm831x->write_dev = wm831x_i2c_write_device; - - return wm831x_device_init(wm831x, id->driver_data, i2c->irq); -} - -static int wm831x_i2c_remove(struct i2c_client *i2c) -{ - struct wm831x *wm831x = i2c_get_clientdata(i2c); - - wm831x_device_exit(wm831x); - - return 0; -} - -static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) -{ - struct wm831x *wm831x = i2c_get_clientdata(i2c); - - return wm831x_device_suspend(wm831x); -} - -static const struct i2c_device_id wm831x_i2c_id[] = { - { "wm8310", WM8310 }, - { "wm8311", WM8311 }, - { "wm8312", WM8312 }, - { "wm8320", WM8320 }, - { "wm8321", WM8321 }, - { "wm8325", WM8325 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); - - -static struct i2c_driver wm831x_i2c_driver = { - .driver = { - .name = "wm831x", - .owner = THIS_MODULE, - }, - .probe = wm831x_i2c_probe, - .remove = wm831x_i2c_remove, - .suspend = wm831x_i2c_suspend, - .id_table = wm831x_i2c_id, -}; - -static int __init wm831x_i2c_init(void) -{ - int ret; - - ret = i2c_add_driver(&wm831x_i2c_driver); - if (ret != 0) - pr_err("Failed to register wm831x I2C driver: %d\n", ret); - - return ret; -} -subsys_initcall(wm831x_i2c_init); - -static void __exit wm831x_i2c_exit(void) -{ - i2c_del_driver(&wm831x_i2c_driver); -} -module_exit(wm831x_i2c_exit); - -MODULE_DESCRIPTION("I2C support for the WM831X AudioPlus PMIC"); +MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c new file mode 100644 index 000000000000..156b19859e81 --- /dev/null +++ b/drivers/mfd/wm831x-i2c.c @@ -0,0 +1,143 @@ +/* + * wm831x-i2c.c -- I2C access for Wolfson WM831x PMICs + * + * Copyright 2009,2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = wm831x->control_data; + int ret; + u16 r = cpu_to_be16(reg); + + ret = i2c_master_send(i2c, (unsigned char *)&r, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + ret = i2c_master_recv(i2c, dest, bytes); + if (ret < 0) + return ret; + if (ret != bytes) + return -EIO; + return 0; +} + +/* Currently we allocate the write buffer on the stack; this is OK for + * small writes - if we need to do large writes this will need to be + * revised. + */ +static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, + int bytes, void *src) +{ + struct i2c_client *i2c = wm831x->control_data; + unsigned char msg[bytes + 2]; + int ret; + + reg = cpu_to_be16(reg); + memcpy(&msg[0], ®, 2); + memcpy(&msg[2], src, bytes); + + ret = i2c_master_send(i2c, msg, bytes + 2); + if (ret < 0) + return ret; + if (ret < bytes + 2) + return -EIO; + + return 0; +} + +static int wm831x_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm831x *wm831x; + + wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); + if (wm831x == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm831x); + wm831x->dev = &i2c->dev; + wm831x->control_data = i2c; + wm831x->read_dev = wm831x_i2c_read_device; + wm831x->write_dev = wm831x_i2c_write_device; + + return wm831x_device_init(wm831x, id->driver_data, i2c->irq); +} + +static int wm831x_i2c_remove(struct i2c_client *i2c) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + wm831x_device_exit(wm831x); + + return 0; +} + +static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + return wm831x_device_suspend(wm831x); +} + +static const struct i2c_device_id wm831x_i2c_id[] = { + { "wm8310", WM8310 }, + { "wm8311", WM8311 }, + { "wm8312", WM8312 }, + { "wm8320", WM8320 }, + { "wm8321", WM8321 }, + { "wm8325", WM8325 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); + + +static struct i2c_driver wm831x_i2c_driver = { + .driver = { + .name = "wm831x", + .owner = THIS_MODULE, + }, + .probe = wm831x_i2c_probe, + .remove = wm831x_i2c_remove, + .suspend = wm831x_i2c_suspend, + .id_table = wm831x_i2c_id, +}; + +static int __init wm831x_i2c_init(void) +{ + int ret; + + ret = i2c_add_driver(&wm831x_i2c_driver); + if (ret != 0) + pr_err("Failed to register wm831x I2C driver: %d\n", ret); + + return ret; +} +subsys_initcall(wm831x_i2c_init); + +static void __exit wm831x_i2c_exit(void) +{ + i2c_del_driver(&wm831x_i2c_driver); +} +module_exit(wm831x_i2c_exit); diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index eb5bd4e0e03c..a1239c48b41a 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -238,6 +238,15 @@ struct regulator_dev; #define WM831X_NUM_IRQ_REGS 5 +enum wm831x_parent { + WM8310 = 0x8310, + WM8311 = 0x8311, + WM8312 = 0x8312, + WM8320 = 0x8320, + WM8321 = 0x8321, + WM8325 = 0x8325, +}; + struct wm831x { struct mutex io_lock; @@ -285,6 +294,9 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg, int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int count, u16 *buf); +int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq); +void wm831x_device_exit(struct wm831x *wm831x); +int wm831x_device_suspend(struct wm831x *wm831x); int wm831x_irq_init(struct wm831x *wm831x, int irq); void wm831x_irq_exit(struct wm831x *wm831x); -- cgit v1.2.3