diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-11-12 15:28:39 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2012-11-12 15:28:39 +0100 |
commit | f987e832a9e79d2ce8009a5ea9c7b677624b3b30 (patch) | |
tree | 0dd09a5e6b4c60ee0a9916907dfc2cda83f3e496 /drivers/mfd | |
parent | f737b7f46a72c099cf8ac88baff02fbf61b1a47c (diff) | |
parent | fc993d9bc48f772133d8cd156c67c296477db070 (diff) |
Merge branch 'l4t/l4t-r16-r2' into colibri
Conflicts:
arch/arm/mach-tegra/tegra3_usb_phy.c
arch/arm/mach-tegra/usb_phy.c
drivers/usb/gadget/tegra_udc.c
drivers/usb/otg/Makefile
drivers/video/tegra/fb.c
sound/soc/tegra/tegra_pcm.c
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 42 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 4 | ||||
-rw-r--r-- | drivers/mfd/max77665.c | 371 | ||||
-rw-r--r-- | drivers/mfd/tlv320aic3262-core.c | 885 | ||||
-rw-r--r-- | drivers/mfd/tlv320aic3262-irq.c | 204 | ||||
-rw-r--r-- | drivers/mfd/tps65090.c | 385 | ||||
-rw-r--r-- | drivers/mfd/tps80031.c | 417 |
7 files changed, 1833 insertions, 475 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f37d3ec02123..10dba0cbda97 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -270,6 +270,11 @@ config TWL6040_CORE select MFD_CORE default n +config AIC3262_CODEC + bool + select MFD_CORE + default n + config MFD_STMPE bool "Support STMicroelectronics STMPE" depends on I2C=y && GENERIC_HARDIRQS @@ -353,6 +358,19 @@ config PMIC_ADP5520 individual components like LCD backlight, LEDs, GPIOs and Kepad under the corresponding menus. +config MFD_MAX77665 + bool "Maxim Semiconductor MAX77665 Companion PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + Say yes here to support for Maxim Semiconductor MAX77665. + This is a Power Management IC with Flash, Fuel Gauge, Haptic, + MUIC controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX8925 bool "Maxim Semiconductor MAX8925 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -783,6 +801,18 @@ config MFD_PM8XXX_IRQ config TPS65911_COMPARATOR tristate +config MFD_TPS65090 + bool "TPS65090 Power Management chips" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + If you say yes here you get support for the TPS65090 series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. + config MFD_AAT2870_CORE bool "Support for the AnalogicTech AAT2870" select MFD_CORE @@ -804,18 +834,6 @@ config MFD_TPS6591X additional drivers must be enabled in order to use the functionality of the device. -config MFD_TPS65090 - bool "TPS65090 Power Management chips" - depends on I2C && GENERIC_HARDIRQS - select MFD_CORE - select REGMAP_I2C - help - If you say yes here you get support for the TPS65090 series of - Power Management chips. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. - config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 6634515b64e4..c8dc50450219 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o +obj-$(CONFIG_AIC3262_CODEC) += tlv320aic3262-core.o tlv320aic3262-irq.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o @@ -67,6 +68,7 @@ endif obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o +obj-$(CONFIG_MFD_MAX77665) += max77665.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o @@ -101,9 +103,9 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o +obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_TPS6591X) += tps6591x.o -obj-$(CONFIG_MFD_TPS65090) += tps65090.o obj-$(CONFIG_MFD_TPS80031) += tps80031.o obj-$(CONFIG_GPADC_TPS80031) += tps8003x-gpadc.o obj-$(CONFIG_MFD_MAX8907C) += max8907c.o diff --git a/drivers/mfd/max77665.c b/drivers/mfd/max77665.c new file mode 100644 index 000000000000..b66712ba5737 --- /dev/null +++ b/drivers/mfd/max77665.c @@ -0,0 +1,371 @@ +/* + * Core driver for MAXIM MAX77665 + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max77665.h> +#include <linux/slab.h> + +#define MAX77665_INT_STS 0x22 +#define MAX77665_INT_MSK 0x23 +#define MAX77665_PMIC_FLASH 0x00 ... 0x10 +#define MAX77665_PMIC_PMIC 0x20 ... 0x2D +#define MAX77665_PMIC_CHARGER 0xB0 ... 0xC6 +#define MAX77665_MUIC 0x00 ... 0x0E +#define MAX77665_HAPTIC 0x00 ... 0x10 + +static u8 max77665_i2c_slave_address[] = { + [MAX77665_I2C_SLAVE_PMIC] = 0x66, + [MAX77665_I2C_SLAVE_MUIC] = 0x25, + [MAX77665_I2C_SLAVE_HAPTIC] = 0x48, +}; + +struct max77665_irq_data { + int bit; +}; + +#define MAX77665_IRQ(_id, _bit_pos) \ + [MAX77665_IRQ_##_id] = { \ + .bit = (_bit_pos), \ + } + +static const struct max77665_irq_data max77665_irqs[] = { + MAX77665_IRQ(CHARGER, 0), + MAX77665_IRQ(TOP_SYS, 1), + MAX77665_IRQ(FLASH, 2), + MAX77665_IRQ(MUIC, 3), +}; + +static struct mfd_cell max77665s[] = { + {.name = "max77665-charger",}, + {.name = "max77665-flash",}, + {.name = "max77665-muic",}, + {.name = "max77665-haptic",}, +}; + +static void max77665_irq_lock(struct irq_data *data) +{ + struct max77665 *max77665 = irq_data_get_irq_chip_data(data); + + mutex_lock(&max77665->irq_lock); +} + +static void max77665_irq_mask(struct irq_data *irq_data) +{ + struct max77665 *max77665 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - max77665->irq_base; + const struct max77665_irq_data *data = &max77665_irqs[__irq]; + int ret; + + ret = max77665_set_bits(max77665->dev, MAX77665_I2C_SLAVE_PMIC, + MAX77665_INT_MSK, data->bit); + if (ret < 0) + dev_err(max77665->dev, + "Clearing mask reg failed e = %d\n", ret); +} + +static void max77665_irq_unmask(struct irq_data *irq_data) +{ + struct max77665 *max77665 = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - max77665->irq_base; + const struct max77665_irq_data *data = &max77665_irqs[__irq]; + int ret; + + ret = max77665_clr_bits(max77665->dev, MAX77665_I2C_SLAVE_PMIC, + MAX77665_INT_MSK, data->bit); + if (ret < 0) + dev_err(max77665->dev, + "Setting mask reg failed e = %d\n", ret); +} + +static void max77665_irq_sync_unlock(struct irq_data *data) +{ + struct max77665 *max77665 = irq_data_get_irq_chip_data(data); + + mutex_unlock(&max77665->irq_lock); +} + +static irqreturn_t max77665_irq(int irq, void *data) +{ + struct max77665 *max77665 = data; + int ret = 0; + u8 status = 0; + unsigned long int acks = 0; + int i; + + ret = max77665_read(max77665->dev, MAX77665_I2C_SLAVE_PMIC, + MAX77665_INT_STS, &status); + if (ret < 0) { + dev_err(max77665->dev, + "failed to read status regi, e %d\n", ret); + return IRQ_NONE; + } + acks = status; + for_each_set_bit(i, &acks, ARRAY_SIZE(max77665_irqs)) + handle_nested_irq(max77665->irq_base + i); + return acks ? IRQ_HANDLED : IRQ_NONE; +} + +#ifdef CONFIG_PM_SLEEP +static int max77665_irq_set_wake(struct irq_data *data, unsigned int enable) +{ + struct max77665 *max77665 = irq_data_get_irq_chip_data(data); + + return irq_set_irq_wake(max77665->irq_base, enable); +} + +#else +#define max77665_irq_set_wake NULL +#endif + +static int __devinit max77665_irq_init(struct max77665 *max77665, int irq, + int irq_base) +{ + int i, ret; + + if (irq_base <= 0) { + dev_err(max77665->dev, "IRQ base not set, int not supported\n"); + return -EINVAL; + } + + mutex_init(&max77665->irq_lock); + + ret = max77665_write(max77665->dev, MAX77665_I2C_SLAVE_PMIC, + MAX77665_INT_MSK, 0xFF); + if (ret < 0) { + dev_err(max77665->dev, + "Int mask reg write failed, e %d\n", ret); + return ret; + } + + max77665->irq_base = irq_base; + max77665->irq_chip.name = "max77665"; + max77665->irq_chip.irq_mask = max77665_irq_mask; + max77665->irq_chip.irq_unmask = max77665_irq_unmask; + max77665->irq_chip.irq_bus_lock = max77665_irq_lock; + max77665->irq_chip.irq_bus_sync_unlock = max77665_irq_sync_unlock; + max77665->irq_chip.irq_set_wake = max77665_irq_set_wake; + + for (i = 0; i < ARRAY_SIZE(max77665_irqs); i++) { + int __irq = i + max77665->irq_base; + irq_set_chip_data(__irq, max77665); + irq_set_chip_and_handler(__irq, &max77665->irq_chip, + handle_simple_irq); + irq_set_nested_thread(__irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(__irq, IRQF_VALID); +#endif + } + + ret = request_threaded_irq(irq, NULL, max77665_irq, IRQF_ONESHOT, + "max77665", max77665); + if (ret < 0) { + dev_err(max77665->dev, "Int registration failed, e %d\n", ret); + return ret; + } + + device_init_wakeup(max77665->dev, 1); + return ret; +} + +static bool rd_wr_reg_pmic(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX77665_PMIC_FLASH: + case MAX77665_PMIC_PMIC: + case MAX77665_PMIC_CHARGER: + return true; + default: + dev_err(dev, "non-existing reg %s() reg 0x%x\n", __func__, reg); + return false; + } +} + +static bool rd_wr_reg_muic(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX77665_MUIC: + return true; + default: + dev_err(dev, "non-existing reg %s() reg 0x%x\n", __func__, reg); + return false; + } +} + +static bool rd_wr_reg_haptic(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX77665_HAPTIC: + return true; + default: + dev_err(dev, "non-existing reg %s() reg 0x%x\n", __func__, reg); + return false; + } +} + +static const struct regmap_config max77665_regmap_config[] = { + { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, + .writeable_reg = rd_wr_reg_pmic, + .readable_reg = rd_wr_reg_pmic, + .cache_type = REGCACHE_RBTREE, + }, { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0E, + .writeable_reg = rd_wr_reg_muic, + .readable_reg = rd_wr_reg_muic, + .cache_type = REGCACHE_RBTREE, + }, { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x10, + .writeable_reg = rd_wr_reg_haptic, + .readable_reg = rd_wr_reg_haptic, + .cache_type = REGCACHE_RBTREE, + }, +}; + +static int __devinit max77665_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max77665_platform_data *pdata = client->dev.platform_data; + struct max77665 *max77665; + struct i2c_client *slv_client; + int ret; + int i; + + if (!pdata) { + dev_err(&client->dev, "max77665 requires platform data\n"); + return -EINVAL; + } + + max77665 = devm_kzalloc(&client->dev, sizeof(*max77665), GFP_KERNEL); + if (!max77665) { + dev_err(&client->dev, "mem alloc for max77665 failed\n"); + return -ENOMEM; + } + + max77665->dev = &client->dev; + + for (i = 0; i < MAX77665_I2C_SLAVE_MAX; ++i) { + slv_client = max77665->client[i]; + if (i == 0) + slv_client = client; + else + slv_client = i2c_new_dummy(client->adapter, + max77665_i2c_slave_address[i]); + if (!slv_client) { + dev_err(&client->dev, "can't attach client %d\n", i); + ret = -ENOMEM; + goto err_exit; + } + i2c_set_clientdata(slv_client, max77665); + + max77665->regmap[i] = devm_regmap_init_i2c(slv_client, + &max77665_regmap_config[i]); + if (IS_ERR(max77665->regmap[i])) { + ret = PTR_ERR(max77665->regmap[i]); + dev_err(&client->dev, + "regmap %d init failed with err: %d\n", i, ret); + goto err_exit; + } + } + + if (client->irq > 0) + max77665_irq_init(max77665, client->irq, pdata->irq_base); + + ret = mfd_add_devices(max77665->dev, -1, max77665s, + ARRAY_SIZE(max77665s), NULL, 0); + if (ret) { + dev_err(&client->dev, "add mfd devices failed with err: %d\n", + ret); + goto err_irq_exit; + } + + return 0; + +err_irq_exit: + if (client->irq > 0) + free_irq(client->irq, max77665); +err_exit: + for (i = 0; i < MAX77665_I2C_SLAVE_MAX; ++i) { + slv_client = max77665->client[i]; + if (slv_client && slv_client != client) + i2c_unregister_device(slv_client); + } + return ret; +} + +static int __devexit max77665_i2c_remove(struct i2c_client *client) +{ + struct max77665 *max77665 = i2c_get_clientdata(client); + int i; + struct i2c_client *slv_client; + + mfd_remove_devices(max77665->dev); + if (client->irq > 0) + free_irq(client->irq, max77665); + + for (i = 0; i < MAX77665_I2C_SLAVE_MAX; ++i) { + slv_client = max77665->client[i]; + if (slv_client && slv_client != client) + i2c_unregister_device(slv_client); + } + + return 0; +} + +static const struct i2c_device_id max77665_id_table[] = { + { "max77665", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max77665_id_table); + +static struct i2c_driver max77665_driver = { + .driver = { + .name = "max77665", + .owner = THIS_MODULE, + }, + .probe = max77665_i2c_probe, + .remove = __devexit_p(max77665_i2c_remove), + .id_table = max77665_id_table, +}; + +static int __init max77665_init(void) +{ + return i2c_add_driver(&max77665_driver); +} +subsys_initcall(max77665_init); + +static void __exit max77665_exit(void) +{ + i2c_del_driver(&max77665_driver); +} +module_exit(max77665_exit); + +MODULE_DESCRIPTION("MAXIM MAX77665 core driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tlv320aic3262-core.c b/drivers/mfd/tlv320aic3262-core.c new file mode 100644 index 000000000000..7b61c7497a45 --- /dev/null +++ b/drivers/mfd/tlv320aic3262-core.c @@ -0,0 +1,885 @@ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> +#include <linux/mfd/core.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/machine.h> +#include <linux/gpio.h> + +#include <linux/mfd/tlv320aic3262-core.h> +#include <linux/mfd/tlv320aic3262-registers.h> +#define DEBUG +struct aic3262_gpio { + unsigned int reg; + u8 mask; + u8 shift; +}; +struct aic3262_gpio aic3262_gpio_control[] = { + { + .reg = AIC3262_GPIO1_IO_CNTL, + .mask = AIC3262_GPIO_D6_D2, + .shift = AIC3262_GPIO_D2_SHIFT, + }, + { + .reg = AIC3262_GPIO2_IO_CNTL, + .mask = AIC3262_GPIO_D6_D2, + .shift = AIC3262_GPIO_D2_SHIFT, + }, + { + .reg = AIC3262_GPI1_EN, + .mask = AIC3262_GPI1_D2_D1, + .shift = AIC3262_GPIO_D1_SHIFT, + }, + { + .reg = AIC3262_GPI2_EN, + .mask = AIC3262_GPI2_D5_D4, + .shift = AIC3262_GPIO_D4_SHIFT, + }, + { + .reg = AIC3262_GPO1_OUT_CNTL, + .mask = AIC3262_GPO1_D4_D1, + .shift = AIC3262_GPIO_D1_SHIFT, + }, +}; +static int aic3262_read(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + int ret; + int i; + u8 *buf = dest; + + BUG_ON(bytes <= 0); + + ret = aic3262->read_dev(aic3262, reg, bytes, dest); + if (ret < 0) + return ret; + + for (i = 0; i < bytes ; i++) { + dev_vdbg(aic3262->dev, "Read %04x from R%d(0x%x)\n", + buf[i], reg + i, reg + i); + } + + return ret; +} + +/** + * aic3262_reg_read: Read a single TLV320AIC3262 register. + * + * @aic3262: Device to read from. + * @reg: Register to read. + */ +int aic3262_reg_read(struct aic3262 *aic3262, unsigned int reg) +{ + unsigned char val; + int ret; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_read(aic3262, reg, 1, &val); + + mutex_unlock(&aic3262->io_lock); + + if (ret < 0) + return ret; + else + return val; +} +EXPORT_SYMBOL_GPL(aic3262_reg_read); + +/** + * aic3262_bulk_read: Read multiple TLV320AIC3262 registers + * + * @aic3262: Device to read from + * @reg: First register + * @count: Number of registers + * @buf: Buffer to fill. The data will be returned big endian. + */ +int aic3262_bulk_read(struct aic3262 *aic3262, unsigned int reg, + int count, u8 *buf) +{ + int ret; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_read(aic3262, reg, count, buf); + + mutex_unlock(&aic3262->io_lock); + + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_bulk_read); + +static int aic3262_write(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src) +{ + const u8 *buf = src; + int i; + + BUG_ON(bytes <= 0); + + for (i = 0; i < bytes ; i++) { + dev_vdbg(aic3262->dev, "Write %04x to R%d(0x%x)\n", + buf[i], reg + i, reg + i); + } + + return aic3262->write_dev(aic3262, reg, bytes, src); +} + +/** + * aic3262_reg_write: Write a single TLV320AIC3262 register. + * + * @aic3262: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + */ +int aic3262_reg_write(struct aic3262 *aic3262, unsigned int reg, + unsigned char val) +{ + int ret; + + + mutex_lock(&aic3262->io_lock); + + dev_dbg(aic3262->dev, "w 30 %x %x", reg, val); + ret = aic3262_write(aic3262, reg, 1, &val); + + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_reg_write); + +/** + * aic3262_bulk_write: Write multiple TLV320AIC3262 registers + * + * @aic3262: Device to write to + * @reg: First register + * @count: Number of registers + * @buf: Buffer to write from. Data must be big-endian formatted. + */ +int aic3262_bulk_write(struct aic3262 *aic3262, unsigned int reg, + int count, const u8 *buf) +{ + int ret; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_write(aic3262, reg, count, buf); + + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_bulk_write); + +/** + * aic3262_set_bits: Set the value of a bitfield in a TLV320AIC3262 register + * + * @aic3262: Device to write to. + * @reg: Register to write to. + * @mask: Mask of bits to set. + * @val: Value to set (unshifted) + */ +int aic3262_set_bits(struct aic3262 *aic3262, unsigned int reg, + unsigned char mask, unsigned char val) +{ + int ret; + u8 r; + + mutex_lock(&aic3262->io_lock); + + ret = aic3262_read(aic3262, reg, 1, &r); + if (ret < 0) + goto out; + + + r &= ~mask; + r |= (val & mask); + + dev_dbg(aic3262->dev, "w 30 %x %x", reg, r); + ret = aic3262_write(aic3262, reg, 1, &r); + +out: + mutex_unlock(&aic3262->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aic3262_set_bits); + +/** + * aic3262_wait_bits: wait for a value of a bitfield in a TLV320AIC3262 register + * + * @aic3262: Device to write to. + * @reg: Register to write to. + * @mask: Mask of bits to set. + * @val: Value to set (unshifted) + * @sleep: mdelay value in each iteration in milliseconds + * @count: iteration count for timeout + */ +int aic3262_wait_bits(struct aic3262 *aic3262, unsigned int reg, + unsigned char mask, unsigned char val, int sleep, int counter) +{ + int status; + int timeout = sleep*counter; + + status = aic3262_reg_read(aic3262, reg); + while (((status & mask) != val) && counter) { + mdelay(sleep); + status = aic3262_reg_read(aic3262, reg); + counter--; + }; + if (!counter) + dev_err(aic3262->dev, + "wait_bits timedout (%d millisecs). lastval 0x%x\n", + timeout, status); + return counter; +} +EXPORT_SYMBOL_GPL(aic3262_wait_bits); + +/* to be changed -- Mukund*/ +static struct resource aic3262_codec_resources[] = { + { + .start = AIC3262_IRQ_HEADSET_DETECT, + .end = AIC3262_IRQ_SPEAKER_OVER_TEMP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource aic3262_gpio_resources[] = { + { + .start = AIC3262_GPIO1, + .end = AIC3262_GPO1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell aic3262_devs[] = { + { + .name = "tlv320aic3262-codec", + .num_resources = ARRAY_SIZE(aic3262_codec_resources), + .resources = aic3262_codec_resources, + }, + + { + .name = "tlv320aic3262-gpio", + .num_resources = ARRAY_SIZE(aic3262_gpio_resources), + .resources = aic3262_gpio_resources, + .pm_runtime_no_callbacks = true, + }, +}; + + +#ifdef CONFIG_PM +static int aic3262_suspend(struct device *dev) +{ + struct aic3262 *aic3262 = dev_get_drvdata(dev); + + aic3262->suspended = true; + + return 0; +} + +static int aic3262_resume(struct device *dev) +{ + struct aic3262 *aic3262 = dev_get_drvdata(dev); + + + aic3262->suspended = false; + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(aic3262_pm_ops, aic3262_suspend, aic3262_resume, + NULL); +#endif + + +/* + * Instantiate the generic non-control parts of the device. + */ +static int aic3262_device_init(struct aic3262 *aic3262, int irq) +{ + struct aic3262_pdata *pdata = aic3262->dev->platform_data; + const char *devname; + int ret, i; + u8 revID, pgID; + unsigned int naudint = 0; + u8 resetVal = 1; + + mutex_init(&aic3262->io_lock); + dev_set_drvdata(aic3262->dev, aic3262); + if (pdata) { + if (pdata->gpio_reset) { + ret = gpio_request(pdata->gpio_reset, + "aic3262-reset-pin"); + if (ret != 0) { + dev_err(aic3262->dev, + "Failed to reset aic3262 using gpio %d\n", + pdata->gpio_reset); + goto err_return; + } + gpio_direction_output(pdata->gpio_reset, 1); + mdelay(5); + gpio_direction_output(pdata->gpio_reset, 0); + mdelay(5); + gpio_direction_output(pdata->gpio_reset, 1); + mdelay(5); + } + } + + + /* run the codec through software reset */ + ret = aic3262_reg_write(aic3262, AIC3262_RESET_REG, resetVal); + if (ret < 0) { + dev_err(aic3262->dev, "Could not write to AIC3262 register\n"); + goto err_return; + } + + mdelay(10); + + ret = aic3262_reg_read(aic3262, AIC3262_REV_PG_ID); + if (ret < 0) { + dev_err(aic3262->dev, "Failed to read ID register\n"); + goto err_return; + } + revID = (ret & AIC3262_REV_MASK) >> AIC3262_REV_SHIFT; + pgID = (ret & AIC3262_PG_MASK) >> AIC3262_PG_SHIFT; + switch (revID) { + case 3: + devname = "TLV320AIC3262"; + if (aic3262->type != TLV320AIC3262) + dev_warn(aic3262->dev, "Device registered as type %d\n", + aic3262->type); + aic3262->type = TLV320AIC3262; + break; + case 1: + devname = "TLV320AIC3262"; + if (aic3262->type != TLV320AIC3262) + dev_warn(aic3262->dev, "Device registered as type %d\n", + aic3262->type); + aic3262->type = TLV320AIC3262; + break; + + default: + dev_err(aic3262->dev, "Device is not a TLV320AIC3262, ID is %x\n", + ret); + ret = -EINVAL; + goto err_return; + + } + + dev_info(aic3262->dev, "%s revision %c\n", devname, 'D' + ret); + + + if (pdata) { + if (pdata->gpio_irq == 1) { + naudint = gpio_to_irq(pdata->naudint_irq); + gpio_request(pdata->naudint_irq, "aic3262-gpio-irq"); + gpio_direction_input(pdata->naudint_irq); + } else + naudint = pdata->naudint_irq; + + aic3262->irq = naudint; + aic3262->irq_base = pdata->irq_base; + for (i = 0; i < AIC3262_NUM_GPIO; i++) { + if (pdata->gpio[i].used) { + /* Direction is input */ + if (pdata->gpio[i].in) { + /* set direction to input for GPIO, + and enable for GPI */ + aic3262_set_bits(aic3262, + aic3262_gpio_control[i].reg, + aic3262_gpio_control[i].mask, + 0x1 << + aic3262_gpio_control[i].shift); + + if (pdata->gpio[i].in_reg) + /* Some input modes, does not + need extra registers to be + written */ + aic3262_set_bits(aic3262, + pdata->gpio[i].in_reg, + pdata->gpio[i]. + in_reg_bitmask, + pdata->gpio[i].value << + pdata->gpio[i]. + in_reg_shift); + } else { + /* Direction si output */ + aic3262_set_bits(aic3262, + aic3262_gpio_control[i].reg, + aic3262_gpio_control[i].mask, + pdata->gpio[i].value << + aic3262_gpio_control[i].shift); + } + } else + aic3262_set_bits(aic3262, + aic3262_gpio_control[i].reg, + aic3262_gpio_control[i].mask, 0x0); + } + } + + if (naudint) { + /* codec interrupt */ + ret = aic3262_irq_init(aic3262); + if (ret) + goto err_irq; + } + + ret = mfd_add_devices(aic3262->dev, -1, + aic3262_devs, ARRAY_SIZE(aic3262_devs), + NULL, 0); + if (ret != 0) { + dev_err(aic3262->dev, "Failed to add children: %d\n", ret); + goto err_irq; + } + + pm_runtime_enable(aic3262->dev); + pm_runtime_resume(aic3262->dev); + + return 0; + +err_irq: + aic3262_irq_exit(aic3262); +err_return: + kfree(aic3262); + return ret; +} + +static void aic3262_device_exit(struct aic3262 *aic3262) +{ + pm_runtime_disable(aic3262->dev); + mfd_remove_devices(aic3262->dev); + aic3262_irq_exit(aic3262); + kfree(aic3262); +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + + +static int aic3262_i2c_read_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + struct i2c_client *i2c = aic3262->control_data; + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + char *value; + int ret; + u8 buf[2]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page */ + buf[0] = 0x0; + buf[1] = 0x0; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + buf[0] = 127; + buf[1] = book; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + ret = i2c_master_send(i2c, (unsigned char *) buf, 2); + + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->page_no = page; + } + + /* Send the required offset */ + buf[0] = offset ; + ret = i2c_master_send(i2c, (unsigned char *)buf, 1); + if (ret < 0) + return ret; + if (ret != 1) + return -EIO; + + ret = i2c_master_recv(i2c, dest, bytes); + value = dest; + if (ret < 0) + return ret; + if (ret != bytes) + return -EIO; + return ret; +} + +static int aic3262_i2c_write_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src) +{ + struct i2c_client *i2c = aic3262->control_data; + int ret; + + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + + u8 buf[2]; + u8 write_buf[bytes + 1]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page*/ + buf[0] = 0x0; + buf[1] = 0x0; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + buf[0] = 127; + buf[1] = book; + ret = i2c_master_send(i2c, (unsigned char *)buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + ret = i2c_master_send(i2c, (unsigned char *) buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + aic3262->page_no = page; + } + write_buf[0] = offset; + memcpy(&write_buf[1], src, bytes); + ret = i2c_master_send(i2c, write_buf, bytes + 1); + if (ret < 0) + return ret; + if (ret != (bytes + 1)) + return -EIO; + + return 0; +} + +static int aic3262_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic3262 *aic3262; + + aic3262 = kzalloc(sizeof(struct aic3262), GFP_KERNEL); + if (aic3262 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, aic3262); + aic3262->dev = &i2c->dev; + aic3262->control_data = i2c; + aic3262->read_dev = aic3262_i2c_read_device; + aic3262->write_dev = aic3262_i2c_write_device; + aic3262->type = id->driver_data; + aic3262->book_no = 255; + aic3262->page_no = 255; + + return aic3262_device_init(aic3262, i2c->irq); +} + +static int aic3262_i2c_remove(struct i2c_client *i2c) +{ + struct aic3262 *aic3262 = i2c_get_clientdata(i2c); + + aic3262_device_exit(aic3262); + + return 0; +} + +static const struct i2c_device_id aic3262_i2c_id[] = { + { "tlv320aic3262", TLV320AIC3262 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic3262_i2c_id); + + +static struct i2c_driver aic3262_i2c_driver = { + .driver = { + .name = "tlv320aic3262", + .owner = THIS_MODULE, + .pm = &aic3262_pm_ops, + }, + .probe = aic3262_i2c_probe, + .remove = aic3262_i2c_remove, + .id_table = aic3262_i2c_id, +}; + +static int __init aic3262_i2c_init(void) +{ + int ret; + ret = i2c_add_driver(&aic3262_i2c_driver); + if (ret != 0) + pr_err("Failed to register aic3262 I2C driver: %d\n", ret); + + return ret; +} +module_init(aic3262_i2c_init); + +static void __exit aic3262_i2c_exit(void) +{ + i2c_del_driver(&aic3262_i2c_driver); +} +module_exit(aic3262_i2c_exit); +#endif +#if defined(CONFIG_SPI_MASTER) +/* TODO: UGLY + * NVidia's CS differs from what TI requires on the SPI bus. So before + * we do any write/read we pull down the CS gpio :( + * The problem is in spi_read. + * Can we set the flag spi_transfer.cs_change during read so that CS is + * pulled low until the next transaction occurs + * (spi_read requires a spi_write followed by spi_read) + */ +#include <linux/gpio.h> +#include "../../../arch/arm/mach-tegra/gpio-names.h" +#include <linux/delay.h> +#define SPI_CS TEGRA_GPIO_PX3 +#define CS(a) gpio_set_value(SPI_CS, a) +void nvidia_spi_cs_en(bool stop) +{ + if (stop) { + CS(1); + udelay(1); + } else { + CS(0); + udelay(1); + } + return; +} +static int aic3262_spi_read_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, void *dest) +{ + struct spi_device *spi = aic3262->control_data; + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + u8 *write_read_buf; + unsigned int i; + unsigned int time; + unsigned int last_count; + unsigned int spi_read_bufsize = max(32, SMP_CACHE_BYTES)-1; + struct spi_message message; + struct spi_transfer x[2]; + int ret; + u8 buf[2]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page */ + + buf[0] = 0x0; + buf[1] = 0x0; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + buf[0] = (127 << 1) ; + buf[1] = book; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + aic3262->page_no = page; + } + + buf[0] = (offset << 1) | (0x01) ; + memset(x, 0, sizeof x); + spi_message_init(&message); + x[0].len = 1; + x[0].tx_buf = buf; + x[1].len = bytes; + x[1].rx_buf = dest ; + + spi_message_add_tail(&x[0], &message); + spi_message_add_tail(&x[1], &message); + + nvidia_spi_cs_en(0); + ret = spi_sync(spi, &message); + nvidia_spi_cs_en(1); + if (ret < 0) + return ret; + + return bytes; + +} +/* NVidia's CS differs from what TI requires on the SPI bus. So before + * we do any write/read we pull down the CS gpio :( + */ +static int aic3262_spi_write_device(struct aic3262 *aic3262, unsigned int reg, + int bytes, const void *src) +{ + struct spi_device *spi = aic3262->control_data; + int ret; + + union aic326x_reg_union *aic_reg = (union aic326x_reg_union *) ® + + u8 buf[2]; + u8 write_buf[bytes + 1]; + u8 page, book, offset; + page = aic_reg->aic326x_register.page; + book = aic_reg->aic326x_register.book; + offset = aic_reg->aic326x_register.offset; + if (aic3262->book_no != book) { + /* We should change to page 0. + Change the book by writing to offset 127 of page 0 + Change the page back to whatever was set before change page */ + + buf[0] = 0x0; + buf[1] = 0x0; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + buf[0] = (127 << 1) ; + buf[1] = book; + + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *)buf, 2); + nvidia_spi_cs_en(1); + + if (ret < 0) + return ret; + aic3262->book_no = book; + aic3262->page_no = 0x0; + } + + if (aic3262->page_no != page) { + buf[0] = 0x0; + buf[1] = page; + nvidia_spi_cs_en(0); + ret = spi_write(spi, (unsigned char *) buf, 2); + nvidia_spi_cs_en(1); + if (ret < 0) + return ret; + aic3262->page_no = page; + } + write_buf[0] = offset << 1 ; + memcpy(&write_buf[1], src, bytes); + nvidia_spi_cs_en(0); + ret = spi_write(spi, write_buf, bytes + 1); + nvidia_spi_cs_en(1); + if (ret < 0) + return ret; + + return bytes; +} + +static int aic3262_spi_probe(struct spi_device *spi) +{ + struct aic3262 *aic3262; + + aic3262 = kzalloc(sizeof(struct aic3262), GFP_KERNEL); + if (aic3262 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, aic3262); + aic3262->dev = &spi->dev; + aic3262->control_data = spi; + aic3262->read_dev = aic3262_spi_read_device; + aic3262->write_dev = aic3262_spi_write_device; + spi->bits_per_word = 8; + spi->mode = SPI_MODE_1; + spi->max_speed_hz = 4000*1000; + spi_setup(spi); + + if (strcmp(spi->modalias, "tlv320aic3262") == 0) + aic3262->type = TLV320AIC3262; + aic3262->book_no = 255; + aic3262->page_no = 255; + + return aic3262_device_init(aic3262, spi->irq); +} + +static int aic3262_spi_remove(struct spi_device *spi) +{ + struct aic3262 *aic3262 = spi_get_drvdata(spi); + + aic3262_device_exit(aic3262); + + return 0; +} + +static struct spi_driver aic3262_spi_driver = { + .driver = { + .name = "tlv320aic3262", + .owner = THIS_MODULE, + .pm = &aic3262_pm_ops, + }, + .probe = aic3262_spi_probe, + .remove = aic3262_spi_remove, +}; + +static int __init aic3262_spi_init(void) +{ + int ret; + ret = spi_register_driver(&aic3262_spi_driver); + if (ret != 0) + pr_err("Failed to register aic3262 SPI driver: %d\n", ret); + + return ret; +} +module_init(aic3262_spi_init); + +static void __exit aic3262_spi_exit(void) +{ + spi_unregister_driver(&aic3262_spi_driver); +} +module_exit(aic3262_spi_exit); +#endif + +MODULE_DESCRIPTION("Core support for the TLV320AIC3262 audio CODEC"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mukund Navada <navada@ti.com>"); diff --git a/drivers/mfd/tlv320aic3262-irq.c b/drivers/mfd/tlv320aic3262-irq.c new file mode 100644 index 000000000000..7e7a5499f3e5 --- /dev/null +++ b/drivers/mfd/tlv320aic3262-irq.c @@ -0,0 +1,204 @@ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/interrupt.h> + +#include <linux/mfd/tlv320aic3262-core.h> +#include <linux/mfd/tlv320aic3262-registers.h> + +#include <linux/delay.h> +struct aic3262_irq_data { + int mask; + int status; +}; + +static struct aic3262_irq_data aic3262_irqs[] = { + { + .mask = AIC3262_HEADSET_IN_MASK, + .status = AIC3262_HEADSET_PLUG_UNPLUG_INT, + }, + { + .mask = AIC3262_BUTTON_PRESS_MASK, + .status = AIC3262_BUTTON_PRESS_INT, + }, + { + .mask = AIC3262_DAC_DRC_THRES_MASK, + .status = AIC3262_LEFT_DRC_THRES_INT | AIC3262_RIGHT_DRC_THRES_INT, + }, + { + .mask = AIC3262_AGC_NOISE_MASK, + .status = AIC3262_LEFT_AGC_NOISE_INT | AIC3262_RIGHT_AGC_NOISE_INT, + }, + { + .mask = AIC3262_OVER_CURRENT_MASK, + .status = AIC3262_LEFT_OUTPUT_DRIVER_OVERCURRENT_INT + | AIC3262_RIGHT_OUTPUT_DRIVER_OVERCURRENT_INT, + }, + { + .mask = AIC3262_OVERFLOW_MASK, + .status = + AIC3262_LEFT_DAC_OVERFLOW_INT | AIC3262_RIGHT_DAC_OVERFLOW_INT | + AIC3262_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT | + AIC3262_LEFT_ADC_OVERFLOW_INT | AIC3262_RIGHT_ADC_OVERFLOW_INT | + AIC3262_MINIDSP_D_BARREL_SHIFT_OVERFLOW_INT, + }, + { + .mask = AIC3262_SPK_OVERCURRENT_MASK, + .status = AIC3262_SPK_OVER_CURRENT_INT, + }, + +}; + +struct aic3262_gpio_data { + +}; + +static inline struct aic3262_irq_data *irq_to_aic3262_irq(struct aic3262 + *aic3262, int irq) +{ + return &aic3262_irqs[irq - aic3262->irq_base]; +} + +static void aic3262_irq_lock(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + + mutex_lock(&aic3262->irq_lock); +} + +static void aic3262_irq_sync_unlock(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + + /* write back to hardware any change in irq mask */ + if (aic3262->irq_masks_cur != aic3262->irq_masks_cache) { + aic3262->irq_masks_cache = aic3262->irq_masks_cur; + aic3262_reg_write(aic3262, AIC3262_INT1_CNTL, + aic3262->irq_masks_cur); + } + + mutex_unlock(&aic3262->irq_lock); +} + +static void aic3262_irq_unmask(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + struct aic3262_irq_data *irq_data = + irq_to_aic3262_irq(aic3262, data->irq); + + aic3262->irq_masks_cur |= irq_data->mask; +} + +static void aic3262_irq_mask(struct irq_data *data) +{ + struct aic3262 *aic3262 = irq_data_get_irq_chip_data(data); + struct aic3262_irq_data *irq_data = + irq_to_aic3262_irq(aic3262, data->irq); + + aic3262->irq_masks_cur &= ~irq_data->mask; +} + +static struct irq_chip aic3262_irq_chip = { + .name = "tlv320aic3262", + .irq_bus_lock = aic3262_irq_lock, + .irq_bus_sync_unlock = aic3262_irq_sync_unlock, + .irq_mask = aic3262_irq_mask, + .irq_unmask = aic3262_irq_unmask, +}; + +static irqreturn_t aic3262_irq_thread(int irq, void *data) +{ + struct aic3262 *aic3262 = data; + u8 status[4]; + int i = 0; + /* Reading the sticky bit registers acknowledges + the interrupt to the device */ + aic3262_bulk_read(aic3262, AIC3262_INT_STICKY_FLAG1, 4, status); + + /* report */ + if (status[2] & aic3262_irqs[AIC3262_IRQ_HEADSET_DETECT].status) + handle_nested_irq(aic3262->irq_base); + + if (status[2] & aic3262_irqs[AIC3262_IRQ_BUTTON_PRESS].status) + handle_nested_irq(aic3262->irq_base + 1); + if (status[2] & aic3262_irqs[AIC3262_IRQ_DAC_DRC].status) + handle_nested_irq(aic3262->irq_base + 2); + if (status[3] & aic3262_irqs[AIC3262_IRQ_AGC_NOISE].status) + handle_nested_irq(aic3262->irq_base + 3); + if (status[2] & aic3262_irqs[AIC3262_IRQ_OVER_CURRENT].status) + handle_nested_irq(aic3262->irq_base + 4); + if (status[0] & aic3262_irqs[AIC3262_IRQ_OVERFLOW_EVENT].status) + handle_nested_irq(aic3262->irq_base + 5); + if (status[3] & aic3262_irqs[AIC3262_IRQ_SPEAKER_OVER_TEMP].status) + handle_nested_irq(aic3262->irq_base + 6); + + /* ack unmasked irqs */ + /* No need to acknowledge the interrupt on AIC3262 */ + + return IRQ_HANDLED; +} + +int aic3262_irq_init(struct aic3262 *aic3262) +{ + int cur_irq, ret; + + mutex_init(&aic3262->irq_lock); + + /* mask the individual interrupt sources */ + aic3262->irq_masks_cur = 0x0; + aic3262->irq_masks_cache = 0x0; + aic3262_reg_write(aic3262, AIC3262_INT1_CNTL, 0x0); + + if (!aic3262->irq) { + dev_warn(aic3262->dev, + "no interrupt specified, no interrupts\n"); + aic3262->irq_base = 0; + return 0; + } + + if (!aic3262->irq_base) { + dev_err(aic3262->dev, + "no interrupt base specified, no interrupts\n"); + return 0; + } + + /* Register them with genirq */ + for (cur_irq = aic3262->irq_base; + cur_irq < aic3262->irq_base + ARRAY_SIZE(aic3262_irqs); + cur_irq++) { + irq_set_chip_data(cur_irq, aic3262); + irq_set_chip_and_handler(cur_irq, &aic3262_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + set_irq_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(aic3262->irq, NULL, aic3262_irq_thread, + IRQF_TRIGGER_RISING, + "tlv320aic3262", aic3262); + if (ret) { + dev_err(aic3262->dev, "failed to request IRQ %d: %d\n", + aic3262->irq, ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(aic3262_irq_init); + +void aic3262_irq_exit(struct aic3262 *aic3262) +{ + if (aic3262->irq) + free_irq(aic3262->irq, aic3262); +} +EXPORT_SYMBOL(aic3262_irq_exit); diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 080f97aa264b..25f463de1513 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -1,37 +1,33 @@ /* - * driver/mfd/tps65090.c - * * Core driver for TI TPS65090 PMIC family * - * Copyright (C) 2012 NVIDIA Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/i2c.h> - #include <linux/mfd/core.h> #include <linux/mfd/tps65090.h> -#include <linux/regmap.h> +#include <linux/err.h> + +#define NUM_INT_REG 2 +#define TOTAL_NUM_REG 0x18 /* interrupt status registers */ #define TPS65090_INT_STS 0x0 @@ -41,95 +37,41 @@ #define TPS65090_INT_MSK 0x2 #define TPS65090_INT_MSK2 0x3 - -enum irq_type { - EVENT, -}; - struct tps65090_irq_data { u8 mask_reg; u8 mask_pos; - enum irq_type type; }; -#define TPS65090_IRQ(_reg, _mask_pos, _type) \ +#define TPS65090_IRQ(_reg, _mask_pos) \ { \ .mask_reg = (_reg), \ .mask_pos = (_mask_pos), \ - .type = (_type), \ } static const struct tps65090_irq_data tps65090_irqs[] = { - [0] = TPS65090_IRQ(0, 0, EVENT), - [1] = TPS65090_IRQ(0, 1, EVENT), - [2] = TPS65090_IRQ(0, 2, EVENT), - [3] = TPS65090_IRQ(0, 3, EVENT), - [4] = TPS65090_IRQ(0, 4, EVENT), - [5] = TPS65090_IRQ(0, 5, EVENT), - [6] = TPS65090_IRQ(0, 6, EVENT), - [7] = TPS65090_IRQ(0, 7, EVENT), - [8] = TPS65090_IRQ(1, 0, EVENT), - [9] = TPS65090_IRQ(1, 1, EVENT), - [10] = TPS65090_IRQ(1, 2, EVENT), - [11] = TPS65090_IRQ(1, 3, EVENT), - [12] = TPS65090_IRQ(1, 4, EVENT), - [13] = TPS65090_IRQ(1, 5, EVENT), - [14] = TPS65090_IRQ(1, 6, EVENT), - [15] = TPS65090_IRQ(1, 7, EVENT), + [0] = TPS65090_IRQ(0, 0), + [1] = TPS65090_IRQ(0, 1), + [2] = TPS65090_IRQ(0, 2), + [3] = TPS65090_IRQ(0, 3), + [4] = TPS65090_IRQ(0, 4), + [5] = TPS65090_IRQ(0, 5), + [6] = TPS65090_IRQ(0, 6), + [7] = TPS65090_IRQ(0, 7), + [8] = TPS65090_IRQ(1, 0), + [9] = TPS65090_IRQ(1, 1), + [10] = TPS65090_IRQ(1, 2), + [11] = TPS65090_IRQ(1, 3), + [12] = TPS65090_IRQ(1, 4), + [13] = TPS65090_IRQ(1, 5), + [14] = TPS65090_IRQ(1, 6), + [15] = TPS65090_IRQ(1, 7), }; -struct tps65090 { - struct mutex lock; - struct device *dev; - struct i2c_client *client; - struct regmap *rmap; - struct irq_chip irq_chip; - struct mutex irq_lock; - int irq_base; - u32 irq_en; - u8 mask_cache[2]; - u8 mask_reg[2]; +static struct mfd_cell tps65090s[] = { + { + .name = "tps65910-pmic", + }, }; -int tps65090_write(struct device *dev, int reg, uint8_t val) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_write(tps->rmap, reg, val); -} -int tps65090_read(struct device *dev, int reg, uint8_t *val) -{ - int rval, ret = 0; - struct tps65090 *tps = dev_get_drvdata(dev); - ret = regmap_read(tps->rmap, reg, &rval); - *val = rval; - return 0; -} -EXPORT_SYMBOL_GPL(tps65090_read); - -int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_update_bits_lazy(tps->rmap, reg, BIT(bit_num), ~0u); - -} -EXPORT_SYMBOL_GPL(tps65090_set_bits); - -int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num) -{ - struct tps65090 *tps = dev_get_drvdata(dev); - return regmap_update_bits_lazy(tps->rmap, reg, BIT(bit_num), 0u); -} -EXPORT_SYMBOL_GPL(tps65090_clr_bits); - -static int __remove_subdev(struct device *dev, void *unused) -{ - platform_device_unregister(to_platform_device(dev)); - return 0; -} - -static int tps65090_remove_subdevs(struct tps65090 *tps65090) -{ - return device_for_each_child(tps65090->dev, NULL, __remove_subdev); -} static void tps65090_irq_lock(struct irq_data *data) { @@ -141,135 +83,97 @@ static void tps65090_irq_lock(struct irq_data *data) static void tps65090_irq_mask(struct irq_data *irq_data) { struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps65090->irq_base; + unsigned int __irq = irq_data->hwirq; const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - if (data->type == EVENT) - tps65090->mask_reg[data->mask_reg] |= (1 << data->mask_pos); - else - tps65090->mask_reg[data->mask_reg] |= (3 << data->mask_pos); - - tps65090->irq_en &= ~(1 << __irq); + tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), + data->mask_pos); } static void tps65090_irq_unmask(struct irq_data *irq_data) { struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq_data->irq - tps65090->irq_base; const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - if (data->type == EVENT) { - tps65090->mask_reg[data->mask_reg] &= ~(1 << data->mask_pos); - tps65090->irq_en |= (1 << __irq); - } + tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), + data->mask_pos); } static void tps65090_irq_sync_unlock(struct irq_data *data) { struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); - int i; - - for (i = 0; i < ARRAY_SIZE(tps65090->mask_reg); i++) { - if (tps65090->mask_reg[i] != tps65090->mask_cache[i]) { - if (!WARN_ON(tps65090_write(tps65090->dev, - TPS65090_INT_MSK + 2*i, - tps65090->mask_reg[i]))) - tps65090->mask_cache[i] = tps65090->mask_reg[i]; - } - } mutex_unlock(&tps65090->irq_lock); } -static int tps65090_irq_set_type(struct irq_data *irq_data, unsigned int type) -{ - struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); - - unsigned int __irq = irq_data->irq - tps65090->irq_base; - const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; - - if (data->type != EVENT) /* add support for GPIO, if needed */ - return -EINVAL; - - return 0; -} - static irqreturn_t tps65090_irq(int irq, void *data) { struct tps65090 *tps65090 = data; int ret = 0; - u8 tmp[3]; - u8 int_ack; - u32 acks, mask = 0; + u8 status = 0, mask = 0; + unsigned long int acks = 0; int i; - for (i = 0; i < 3; i++) { - ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + 2*i, - &tmp[i]); + for (i = 0; i < NUM_INT_REG; i++) { + ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask); if (ret < 0) { dev_err(tps65090->dev, - "failed to read interrupt status\n"); + "failed to read mask reg [addr:%d]\n", + TPS65090_INT_MSK + i); return IRQ_NONE; } - if (tmp[i]) { - /* Ack only those interrupts which are enabled */ - int_ack = tmp[i] & (~(tps65090->mask_cache[i])); + ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i, + &status); + if (ret < 0) { + dev_err(tps65090->dev, + "failed to read status reg [addr:%d]\n", + TPS65090_INT_STS + i); + return IRQ_NONE; + } + if (status) { + /* Ack only those interrupts which are not masked */ + status &= (~mask); ret = tps65090_write(tps65090->dev, - TPS65090_INT_STS + 2*i, int_ack); + TPS65090_INT_STS + i, status); if (ret < 0) { dev_err(tps65090->dev, "failed to write interrupt status\n"); return IRQ_NONE; } + acks |= (status << (i * 8)); } } - acks = (tmp[2] << 16) | (tmp[1] << 8) | tmp[0]; - - for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) { - if (tps65090_irqs[i].type == EVENT) - mask = (1 << (tps65090_irqs[i].mask_pos - + tps65090_irqs[i].mask_reg*8)); - else - return -EINVAL; - if ((acks & mask) && (tps65090->irq_en & (1 << i))) - handle_nested_irq(tps65090->irq_base + i); - } - return IRQ_HANDLED; + for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs)) + handle_nested_irq(tps65090->irq_base + i); + return acks ? IRQ_HANDLED : IRQ_NONE; } static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq, - int irq_base) + int irq_base) { int i, ret; if (!irq_base) { - dev_warn(tps65090->dev, "No interrupt support on IRQ base\n"); + dev_err(tps65090->dev, "IRQ base not set\n"); return -EINVAL; } mutex_init(&tps65090->irq_lock); - tps65090->mask_reg[0] = 0xFF; - tps65090->mask_reg[1] = 0xFF; - for (i = 0; i < 2; i++) { - tps65090->mask_cache[i] = tps65090->mask_reg[i]; - tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, - tps65090->mask_cache[i]); - } + for (i = 0; i < NUM_INT_REG; i++) + tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF); - for (i = 0; i < 2; i++) + for (i = 0; i < NUM_INT_REG; i++) tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff); tps65090->irq_base = irq_base; - tps65090->irq_chip.name = "tps65090"; tps65090->irq_chip.irq_mask = tps65090_irq_mask; tps65090->irq_chip.irq_unmask = tps65090_irq_unmask; tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock; tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock; - tps65090->irq_chip.irq_set_type = tps65090_irq_set_type; for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) { int __irq = i + tps65090->irq_base; @@ -292,90 +196,21 @@ static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq, return ret; } -static int __devinit tps65090_add_subdevs(struct tps65090 *tps65090, - struct tps65090_platform_data *pdata) -{ - struct tps65090_subdev_info *subdev; - struct platform_device *pdev; - int i, ret = 0; - - for (i = 0; i < pdata->num_subdevs; i++) { - subdev = &pdata->subdevs[i]; - - pdev = platform_device_alloc(subdev->name, subdev->id); - - pdev->dev.parent = tps65090->dev; - pdev->dev.platform_data = subdev->platform_data; - - ret = platform_device_add(pdev); - if (ret) - goto failed; - } - return 0; - -failed: - tps65090_remove_subdevs(tps65090); - return ret; -} -#ifdef CONFIG_DEBUG_FS -#include <linux/debugfs.h> -#include <linux/seq_file.h> -static void print_regs(const char *header, struct seq_file *s, - struct i2c_client *client, int start_offset, - int end_offset) +static bool is_volatile_reg(struct device *dev, unsigned int reg) { - int reg_val; - int i; - int ret; - struct tps65090 *tps = s->private; - seq_printf(s, "%s\n", header); - for (i = start_offset; i <= end_offset; ++i) { - ret = regmap_read(tps->rmap, i, ®_val); - if (ret >= 0) - seq_printf(s, "Reg 0x%02x Value 0x%02x\n", i, reg_val); - } - seq_printf(s, "------------------\n"); -} - -static int dbg_tps_show(struct seq_file *s, void *unused) -{ - struct tps65090 *tps = s->private; - struct i2c_client *client = tps->client; - - seq_printf(s, "TPS65090 Registers\n"); - seq_printf(s, "------------------\n"); - - print_regs("All Regs", s, client, 0x0, 24); - return 0; -} - -static int dbg_tps_open(struct inode *inode, struct file *file) -{ - return single_open(file, dbg_tps_show, inode->i_private); -} - -static const struct file_operations debug_fops = { - .open = dbg_tps_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void __init tps65090_debuginit(struct tps65090 *tps) -{ - (void)debugfs_create_file("tps65090", S_IRUGO, NULL, - tps, &debug_fops); -} -#else -static void __init tps65090_debuginit(struct tps65090 *tpsi) -{ - return; + if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS)) + return true; + else + return false; } -#endif static const struct regmap_config tps65090_regmap_config = { .reg_bits = 8, .val_bits = 8, + .max_register = TOTAL_NUM_REG, + .num_reg_defaults_raw = TOTAL_NUM_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = is_volatile_reg, }; static int __devinit tps65090_i2c_probe(struct i2c_client *client, @@ -387,50 +222,47 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client, if (!pdata) { dev_err(&client->dev, "tps65090 requires platform data\n"); - return -ENOTSUPP; + return -EINVAL; } - tps65090 = kzalloc(sizeof(struct tps65090), GFP_KERNEL); - if (tps65090 == NULL) + tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL); + if (!tps65090) { + dev_err(&client->dev, "mem alloc for tps65090 failed\n"); return -ENOMEM; + } - tps65090->client = client; tps65090->dev = &client->dev; i2c_set_clientdata(client, tps65090); - mutex_init(&tps65090->lock); + tps65090->rmap = devm_regmap_init_i2c(client, &tps65090_regmap_config); + if (IS_ERR(tps65090->rmap)) { + ret = PTR_ERR(tps65090->rmap); + dev_err(&client->dev, "regmap_init failed with err: %d\n", ret); + return ret; + } if (client->irq) { - ret = tps65090_irq_init(tps65090, client->irq, - pdata->irq_base); + ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base); if (ret) { - dev_err(&client->dev, "IRQ init failed: %d\n", ret); - goto err_irq_init; + dev_err(&client->dev, "IRQ init failed with err: %d\n", + ret); + return ret; } } - ret = tps65090_add_subdevs(tps65090, pdata); + ret = mfd_add_devices(tps65090->dev, -1, tps65090s, + ARRAY_SIZE(tps65090s), NULL, 0); if (ret) { - dev_err(&client->dev, "add devices failed: %d\n", ret); - goto err_add_devs; + dev_err(&client->dev, "add mfd devices failed with err: %d\n", + ret); + goto err_irq_exit; } - tps65090->rmap = regmap_init_i2c(tps65090->client, - &tps65090_regmap_config); - if (IS_ERR(tps65090->rmap)) { - dev_err(&client->dev, "regmap_init failed: %ld\n", - PTR_ERR(tps65090->rmap)); - goto err_add_devs; - }; - tps65090_debuginit(tps65090); - return 0; -err_add_devs: +err_irq_exit: if (client->irq) free_irq(client->irq, tps65090); -err_irq_init: - kfree(tps65090); return ret; } @@ -438,29 +270,34 @@ static int __devexit tps65090_i2c_remove(struct i2c_client *client) { struct tps65090 *tps65090 = i2c_get_clientdata(client); + mfd_remove_devices(tps65090->dev); if (client->irq) free_irq(client->irq, tps65090); - regmap_exit(tps65090->rmap); - kfree(tps65090); return 0; } -#ifdef CONFIG_PM -static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state) + +#ifdef CONFIG_PM_SLEEP +static int tps65090_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); if (client->irq) disable_irq(client->irq); return 0; } -static int tps65090_i2c_resume(struct i2c_client *client) +static int tps65090_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); if (client->irq) enable_irq(client->irq); return 0; } #endif +static const struct dev_pm_ops tps65090_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume) +}; static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, @@ -472,13 +309,10 @@ static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", .owner = THIS_MODULE, + .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, .remove = __devexit_p(tps65090_i2c_remove), -#ifdef CONFIG_PM - .suspend = tps65090_i2c_suspend, - .resume = tps65090_i2c_resume, -#endif .id_table = tps65090_id_table, }; @@ -495,4 +329,5 @@ static void __exit tps65090_exit(void) module_exit(tps65090_exit); MODULE_DESCRIPTION("TPS65090 core driver"); -MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index f0f2ce1f6840..002b7d022d70 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -30,6 +30,7 @@ #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/pm.h> +#include <linux/regmap.h> #include <linux/mfd/core.h> #include <linux/mfd/tps80031.h> @@ -108,6 +109,43 @@ #define TPS80031_BBSPOR_CFG 0xE6 #define TPS80031_BBSPOR_CHG_EN 0x8 +/* Valid Address ranges */ +#define TPS80031_ID0_PMIC_SLAVE_SMPS_DVS 0x55 ... 0x5C + +#define TPS80031_ID1_RTC 0x00 ... 0x16 +#define TPS80031_ID1_MEMORY 0x17 ... 0x1E +#define TPS80031_ID1_PMC_MASTER 0x1F ... 0x2D +#define TPS80031_ID1_PMC_SLAVE_MISC 0x31 ... 0x34 +#define TPS80031_ID1_PMC_SLAVE_SMPS 0x40 ... 0x68 +#define TPS80031_ID1_PMC_SLAVE_LDO 0x80 ... 0xA7 +#define TPS80031_ID1_PMC_SLAVE_REOSURCES 0XAD ... 0xD0 +#define TPS80031_ID1_PMC_PREQ_ASSIGN 0XD7 ... 0xDF +#define TPS80031_ID1_PMC_MISC 0xE2 ... 0xEF +#define TPS80031_ID1_PMC_PU_PD_HZ 0xF0 ... 0xF6 +#define TPS80031_ID1_PMC_BACKUP 0xFA + +#define TPS80031_ID2_USB 0x00 ... 0x1A +#define TPS80031_ID2_GPADC_CONTROL 0x2E ... 0x36 +#define TPS80031_ID2_GPADC_RESULTS 0x37 ... 0x3C +#define TPS80031_ID2_AUXILLIARIES 0x90 ... 0x9C +#define TPS80031_ID2_CUSTOM 0xA0 ... 0xB9 +#define TPS80031_ID2_PWM 0xBA ... 0xBE +#define TPS80031_ID2_FUEL_GAUSE 0xC0 ... 0xCB +#define TPS80031_ID2_INTERFACE_INTERRUPTS 0xD0 ... 0xD8 +#define TPS80031_ID2_CHARGER 0xDA ... 0xF5 + +#define TPS80031_ID3_TEST_LDO 0x00 ... 0x09 +#define TPS80031_ID3_TEST_SMPS 0x10 ... 0x2B +#define TPS80031_ID3_TEST_POWER 0x30 ... 0x36 +#define TPS80031_ID3_TEST_CHARGER 0x40 ... 0x48 +#define TPS80031_ID3_TEST_AUXILIIARIES 0x50 ... 0xB1 + +#define TPS80031_ID3_DIEID 0xC0 ... 0xC8 +#define TPS80031_ID3_TRIM_PHOENIX 0xCC ... 0xEA +#define TPS80031_ID3_TRIM_CUSTOM 0xEC ... 0xED + +#define TPS80031_MAX_REGISTER 0x100 + struct tps80031_pupd_data { u8 reg; u8 pullup_bit; @@ -256,159 +294,84 @@ struct tps80031 { u8 cont_int_en; u8 prev_cont_stat1; struct tps80031_client tps_clients[TPS_NUM_SLAVES]; + struct regmap *regmap[TPS_NUM_SLAVES]; }; -static inline int __tps80031_read(struct i2c_client *client, - int reg, uint8_t *val) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret < 0) { - dev_err(&client->dev, - "failed reading from addr 0x%02x, reg 0x%02x\n", - client->addr, reg); - return ret; - } - - *val = (uint8_t)ret; - - return 0; -} - -static inline int __tps80031_reads(struct i2c_client *client, int reg, - int len, uint8_t *val) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(client, reg, len, val); - if (ret < 0) { - dev_err(&client->dev, - "failed reading from addr 0x%02x, reg 0x%02x\n", - client->addr, reg); - return ret; - } - - return 0; -} - -static inline int __tps80031_write(struct i2c_client *client, - int reg, uint8_t val) -{ - int ret; - ret = i2c_smbus_write_byte_data(client, reg, val); - if (ret < 0) { - dev_err(&client->dev, - "failed writing 0x%02x to 0x%02x\n", val, reg); - return ret; - } - - return 0; -} - -static inline int __tps80031_writes(struct i2c_client *client, int reg, - int len, uint8_t *val) -{ - int ret; - - ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); - if (ret < 0) { - dev_err(&client->dev, "failed writings to 0x%02x\n", reg); - return ret; - } +/* TPS80031 sub mfd devices */ +static struct mfd_cell tps80031_cell[] = { + { + .name = "tps80031-regulators", + }, + { + .name = "tps80031-rtc", + }, + { + .name = "tps80031-gpadc", + }, + { + .name = "tps80031-battery-gauge", + }, + { + .name = "tps80031-charger", + }, +}; - return 0; -} int tps80031_write(struct device *dev, int sid, int reg, uint8_t val) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; - int ret; - mutex_lock(&tps->lock); - ret = __tps80031_write(tps->client, reg, val); - mutex_unlock(&tps->lock); - - return ret; + return regmap_write(tps80031->regmap[sid], reg, val); } EXPORT_SYMBOL_GPL(tps80031_write); int tps80031_writes(struct device *dev, int sid, int reg, int len, uint8_t *val) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; - int ret; - mutex_lock(&tps->lock); - ret = __tps80031_writes(tps->client, reg, len, val); - mutex_unlock(&tps->lock); - - return ret; + return regmap_bulk_write(tps80031->regmap[sid], reg, val, len); } EXPORT_SYMBOL_GPL(tps80031_writes); int tps80031_read(struct device *dev, int sid, int reg, uint8_t *val) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; + unsigned int ival; + int ret; - return __tps80031_read(tps->client, reg, val); + ret = regmap_read(tps80031->regmap[sid], reg, &ival); + if (ret < 0) { + dev_err(dev, "failed reading from reg 0x%02x\n", reg); + return ret; + } + + *val = ival; + return ret; } EXPORT_SYMBOL_GPL(tps80031_read); int tps80031_reads(struct device *dev, int sid, int reg, int len, uint8_t *val) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; - return __tps80031_reads(tps->client, reg, len, val); + return regmap_bulk_read(tps80031->regmap[sid], reg, val, len); } EXPORT_SYMBOL_GPL(tps80031_reads); int tps80031_set_bits(struct device *dev, int sid, int reg, uint8_t bit_mask) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; - uint8_t reg_val; - int ret = 0; - - mutex_lock(&tps->lock); - - ret = __tps80031_read(tps->client, reg, ®_val); - if (ret) - goto out; - if ((reg_val & bit_mask) != bit_mask) { - reg_val |= bit_mask; - ret = __tps80031_write(tps->client, reg, reg_val); - } -out: - mutex_unlock(&tps->lock); - return ret; + return regmap_update_bits(tps80031->regmap[sid], reg, + bit_mask, bit_mask); } EXPORT_SYMBOL_GPL(tps80031_set_bits); int tps80031_clr_bits(struct device *dev, int sid, int reg, uint8_t bit_mask) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; - uint8_t reg_val; - int ret = 0; - - mutex_lock(&tps->lock); - ret = __tps80031_read(tps->client, reg, ®_val); - if (ret) - goto out; - - if (reg_val & bit_mask) { - reg_val &= ~bit_mask; - ret = __tps80031_write(tps->client, reg, reg_val); - } -out: - mutex_unlock(&tps->lock); - return ret; + return regmap_update_bits(tps80031->regmap[sid], reg, bit_mask, 0); } EXPORT_SYMBOL_GPL(tps80031_clr_bits); @@ -416,23 +379,8 @@ int tps80031_update(struct device *dev, int sid, int reg, uint8_t val, uint8_t mask) { struct tps80031 *tps80031 = dev_get_drvdata(dev); - struct tps80031_client *tps = &tps80031->tps_clients[sid]; - uint8_t reg_val; - int ret = 0; - mutex_lock(&tps->lock); - - ret = __tps80031_read(tps->client, reg, ®_val); - if (ret) - goto out; - - if ((reg_val & mask) != val) { - reg_val = (reg_val & ~mask) | (val & mask); - ret = __tps80031_write(tps->client, reg, reg_val); - } -out: - mutex_unlock(&tps->lock); - return ret; + return regmap_update_bits(tps80031->regmap[sid], reg, mask, val); } EXPORT_SYMBOL_GPL(tps80031_update); @@ -446,12 +394,12 @@ int tps80031_force_update(struct device *dev, int sid, int reg, uint8_t val, mutex_lock(&tps->lock); - ret = __tps80031_read(tps->client, reg, ®_val); + ret = tps80031_read(dev, sid, reg, ®_val); if (ret) goto out; reg_val = (reg_val & ~mask) | (val & mask); - ret = __tps80031_write(tps->client, reg, reg_val); + ret = tps80031_write(dev, sid, reg, reg_val); out: mutex_unlock(&tps->lock); @@ -544,7 +492,8 @@ static void tps80031_power_off(void) if (!tps->client) return; dev_info(&tps->client->dev, "switching off PMU\n"); - __tps80031_write(tps->client, TPS80031_PHOENIX_DEV_ON, DEVOFF); + tps80031_write(&tps->client->dev, SLAVE_ID1, + TPS80031_PHOENIX_DEV_ON, DEVOFF); } static void tps80031_pupd_init(struct tps80031 *tps80031, @@ -589,7 +538,7 @@ static void tps80031_init_ext_control(struct tps80031 *tps80031, /* Clear all external control for this rail */ for (i = 0; i < 9; ++i) { - tps80031_write(tps80031->dev, SLAVE_ID1, + ret = tps80031_write(tps80031->dev, SLAVE_ID1, TPS80031_PREQ1_RES_ASS_A + i, 0); if (ret < 0) dev_err(tps80031->dev, "%s() Error in clearing " @@ -613,14 +562,14 @@ static int tps80031_gpio_get(struct gpio_chip *gc, unsigned offset) uint8_t trans; int ret; - ret = __tps80031_read(tps->client, + ret = tps80031_read(&tps->client->dev, SLAVE_ID1, pmc_ext_control_base[offset] + EXT_CONTROL_CFG_STATE, &state); if (ret) return ret; if (state != 0) { - ret = __tps80031_read(tps->client, + ret = tps80031_read(&tps->client->dev, SLAVE_ID1, pmc_ext_control_base[offset] + EXT_CONTROL_CFG_TRANS, &trans); if (ret) @@ -742,17 +691,6 @@ static void tps80031_gpio_init(struct tps80031 *tps80031, dev_warn(tps80031->dev, "GPIO registration failed: %d\n", ret); } -static int __remove_subdev(struct device *dev, void *unused) -{ - platform_device_unregister(to_platform_device(dev)); - return 0; -} - -static int tps80031_remove_subdevs(struct tps80031 *tps80031) -{ - return device_for_each_child(tps80031->dev, NULL, __remove_subdev); -} - static void tps80031_irq_lock(struct irq_data *data) { struct tps80031 *tps80031 = irq_data_get_irq_chip_data(data); @@ -1075,32 +1013,6 @@ static void tps80031_clk32k_init(struct tps80031 *tps80031, } } -static int __devinit tps80031_add_subdevs(struct tps80031 *tps80031, - struct tps80031_platform_data *pdata) -{ - struct tps80031_subdev_info *subdev; - struct platform_device *pdev; - int i, ret = 0; - - for (i = 0; i < pdata->num_subdevs; i++) { - subdev = &pdata->subdevs[i]; - - pdev = platform_device_alloc(subdev->name, subdev->id); - - pdev->dev.parent = tps80031->dev; - pdev->dev.platform_data = subdev->platform_data; - - ret = platform_device_add(pdev); - if (ret) - goto failed; - } - return 0; - -failed: - tps80031_remove_subdevs(tps80031); - return ret; -} - #ifdef CONFIG_DEBUG_FS #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -1115,7 +1027,7 @@ static void print_regs(const char *header, struct seq_file *s, seq_printf(s, "%s\n", header); for (i = start_offset; i <= end_offset; ++i) { - ret = __tps80031_read(tps->client, i, ®_val); + ret = tps80031_read(&tps->client->dev, sid, i, ®_val); if (ret >= 0) seq_printf(s, "Addr = 0x%02x Reg 0x%02x Value 0x%02x\n", tps->client->addr, i, reg_val); @@ -1189,11 +1101,116 @@ static void __init tps80031_debuginit(struct tps80031 *tpsi) } #endif +static bool rd_wr_reg_id0(struct device *dev, unsigned int reg) +{ + switch(reg) { + case TPS80031_ID0_PMIC_SLAVE_SMPS_DVS: + return true; + default: + pr_err("non-existing reg %s() %d reg %x\n", __func__, __LINE__, reg); + BUG(); + return false; + } +} + +static bool rd_wr_reg_id1(struct device *dev, unsigned int reg) +{ + switch(reg) { + case TPS80031_ID1_RTC: + case TPS80031_ID1_MEMORY: + case TPS80031_ID1_PMC_MASTER: + case TPS80031_ID1_PMC_SLAVE_SMPS: + case TPS80031_ID1_PMC_SLAVE_MISC: + case TPS80031_ID1_PMC_SLAVE_LDO: + case TPS80031_ID1_PMC_SLAVE_REOSURCES: + case TPS80031_ID1_PMC_PREQ_ASSIGN: + case TPS80031_ID1_PMC_MISC: + case TPS80031_ID1_PMC_PU_PD_HZ: + case TPS80031_ID1_PMC_BACKUP: + return true; + default: + pr_err("non-existing reg %s() %d reg %x\n", __func__, __LINE__, reg); + BUG(); + return false; + } +} + +static bool rd_wr_reg_id2(struct device *dev, unsigned int reg) +{ + switch(reg) { + case TPS80031_ID2_USB: + case TPS80031_ID2_GPADC_CONTROL: + case TPS80031_ID2_GPADC_RESULTS: + case TPS80031_ID2_AUXILLIARIES: + case TPS80031_ID2_CUSTOM: + case TPS80031_ID2_PWM: + case TPS80031_ID2_FUEL_GAUSE: + case TPS80031_ID2_INTERFACE_INTERRUPTS: + case TPS80031_ID2_CHARGER: + return true; + default: + pr_err("non-existing reg %s() %d reg %x\n", __func__, __LINE__, reg); + BUG(); + return false; + } +} +static bool rd_wr_reg_id3(struct device *dev, unsigned int reg) +{ + switch(reg) { + case TPS80031_ID3_TEST_LDO: + case TPS80031_ID3_TEST_SMPS: + case TPS80031_ID3_TEST_POWER: + case TPS80031_ID3_TEST_CHARGER: + case TPS80031_ID3_TEST_AUXILIIARIES: + case TPS80031_ID3_DIEID: + case TPS80031_ID3_TRIM_PHOENIX: + case TPS80031_ID3_TRIM_CUSTOM: + return true; + default: + pr_err("non-existing reg %s() %d reg %x\n", __func__, __LINE__, reg); + BUG(); + return false; + } +} + +static const struct regmap_config tps80031_regmap_configs[] = { + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id0, + .readable_reg = rd_wr_reg_id0, + .max_register = TPS80031_MAX_REGISTER - 1, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id1, + .readable_reg = rd_wr_reg_id1, + .max_register = TPS80031_MAX_REGISTER - 1, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id2, + .readable_reg = rd_wr_reg_id2, + .max_register = TPS80031_MAX_REGISTER - 1, + }, + { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg_id3, + .readable_reg = rd_wr_reg_id3, + .max_register = TPS80031_MAX_REGISTER - 1, + }, +}; + static int __devexit tps80031_i2c_remove(struct i2c_client *client) { struct tps80031 *tps80031 = i2c_get_clientdata(client); int i; + mfd_remove_devices(tps80031->dev); + if (client->irq) free_irq(client->irq, tps80031); @@ -1209,7 +1226,6 @@ static int __devexit tps80031_i2c_remove(struct i2c_client *client) mutex_destroy(&tps->lock); } - kfree(tps80031); return 0; } @@ -1246,9 +1262,11 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, dev_info(&client->dev, "Jtag version 0x%02x and Eeprom version 0x%02x\n", jtag_ver, ep_ver); - tps80031 = kzalloc(sizeof(struct tps80031), GFP_KERNEL); - if (tps80031 == NULL) + tps80031 = devm_kzalloc(&client->dev, sizeof(*tps80031), GFP_KERNEL); + if (!tps80031) { + dev_err(&client->dev, "Memory alloc for tps80031 failed\n"); return -ENOMEM; + } tps80031->es_version = jtag_ver; tps80031->dev = &client->dev; @@ -1270,10 +1288,19 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, if (!tps->client) { dev_err(&client->dev, "can't attach client %d\n", i); ret = -ENOMEM; - goto fail; + goto fail_client_reg; } i2c_set_clientdata(tps->client, tps80031); mutex_init(&tps->lock); + + tps80031->regmap[i] = devm_regmap_init_i2c(tps->client, + &tps80031_regmap_configs[i]); + if (IS_ERR(tps80031->regmap[i])) { + ret = PTR_ERR(tps80031->regmap[i]); + dev_err(&client->dev, + "regmap %d init failed, err %d\n", i, ret); + goto fail_client_reg; + } } if (client->irq) { @@ -1281,7 +1308,7 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, pdata->irq_base); if (ret) { dev_err(&client->dev, "IRQ init failed: %d\n", ret); - goto fail; + goto fail_client_reg; } } @@ -1289,10 +1316,11 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, tps80031_init_ext_control(tps80031, pdata); - ret = tps80031_add_subdevs(tps80031, pdata); - if (ret) { - dev_err(&client->dev, "add devices failed: %d\n", ret); - goto fail; + ret = mfd_add_devices(tps80031->dev, -1, + tps80031_cell, ARRAY_SIZE(tps80031_cell), NULL, 0); + if (ret < 0) { + dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret); + goto fail_mfd_add; } tps80031_gpio_init(tps80031, pdata); @@ -1310,14 +1338,24 @@ static int __devinit tps80031_i2c_probe(struct i2c_client *client, return 0; -fail: - tps80031_i2c_remove(client); +fail_mfd_add: + if (client->irq) + free_irq(client->irq, tps80031); +fail_client_reg: + for (i = 0; i < TPS_NUM_SLAVES; i++) { + struct tps80031_client *tps = &tps80031->tps_clients[i]; + if (tps->client && tps->client != client) + i2c_unregister_device(tps->client); + tps80031->tps_clients[i].client = NULL; + mutex_destroy(&tps->lock); + } return ret; } #ifdef CONFIG_PM -static int tps80031_i2c_suspend(struct i2c_client *client, pm_message_t state) +static int tps80031_i2c_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct tps80031 *tps80031 = i2c_get_clientdata(client); if (client->irq) disable_irq(client->irq); @@ -1325,14 +1363,22 @@ static int tps80031_i2c_suspend(struct i2c_client *client, pm_message_t state) return 0; } -static int tps80031_i2c_resume(struct i2c_client *client) +static int tps80031_i2c_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct tps80031 *tps80031 = i2c_get_clientdata(client); tps80031_backup_battery_charger_control(tps80031, 1); if (client->irq) enable_irq(client->irq); return 0; } +static const struct dev_pm_ops tps80031_dev_pm_ops = { + .suspend = tps80031_i2c_suspend, + .resume = tps80031_i2c_resume, +}; +#define TPS80031_DEV_PM (&tps80031_dev_pm_ops) +#else +#define TPS80031_DEV_PM NULL #endif @@ -1346,13 +1392,10 @@ static struct i2c_driver tps80031_driver = { .driver = { .name = "tps80031", .owner = THIS_MODULE, + .pm = TPS80031_DEV_PM, }, .probe = tps80031_i2c_probe, .remove = __devexit_p(tps80031_i2c_remove), -#ifdef CONFIG_PM - .suspend = tps80031_i2c_suspend, - .resume = tps80031_i2c_resume, -#endif .id_table = tps80031_id_table, }; |