summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>2012-11-12 15:28:39 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2012-11-12 15:28:39 +0100
commitf987e832a9e79d2ce8009a5ea9c7b677624b3b30 (patch)
tree0dd09a5e6b4c60ee0a9916907dfc2cda83f3e496 /drivers/mfd
parentf737b7f46a72c099cf8ac88baff02fbf61b1a47c (diff)
parentfc993d9bc48f772133d8cd156c67c296477db070 (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/Kconfig42
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/max77665.c371
-rw-r--r--drivers/mfd/tlv320aic3262-core.c885
-rw-r--r--drivers/mfd/tlv320aic3262-irq.c204
-rw-r--r--drivers/mfd/tps65090.c385
-rw-r--r--drivers/mfd/tps80031.c417
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 *) &reg;
+ 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 *) &reg;
+
+ 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 *) &reg;
+ 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 *) &reg;
+
+ 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, &reg_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, &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, &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, &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, &reg_val);
+ ret = tps80031_read(dev, sid, reg, &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, &reg_val);
+ ret = tps80031_read(&tps->client->dev, sid, i, &reg_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,
};