summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Cherry <tcherry@nvidia.com>2011-01-21 14:44:49 -0800
committerVarun Colbert <vcolbert@nvidia.com>2011-02-02 17:20:19 -0800
commitc478f5bf6f141c81ebac3f618f1249bf34301a14 (patch)
tree84d2af278de2d451df4ca9c9ed87528625c06da4
parentcea34b2178fb828afefbddc8183b82c841dc3752 (diff)
max8907c MFD driver: add second i2c bus and irq handling
Clean up portions of max8907c.c driver Add capability for bulk i2c read and write Add capability for second i2c bus for rtc Add capability for irq handling for rtc and battery charger Change-Id: I6c2c2c42591aee766635a2e32a7404bb2591f4a7 Reviewed-on: http://git-master/r/16613 Tested-by: Thomas Cherry <tcherry@nvidia.com> Reviewed-by: Sachin Nikam <snikam@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/max8907c-irq.c359
-rw-r--r--drivers/mfd/max8907c.c165
-rw-r--r--include/linux/mfd/max8907c.h56
4 files changed, 507 insertions, 74 deletions
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 84d0070a06f8..fdfc6f01aa82 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -77,3 +77,4 @@ obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_MAX8907C) += max8907c.o
+obj-$(CONFIG_MFD_MAX8907C) += max8907c-irq.o
diff --git a/drivers/mfd/max8907c-irq.c b/drivers/mfd/max8907c-irq.c
new file mode 100644
index 000000000000..e9773c0fe00e
--- /dev/null
+++ b/drivers/mfd/max8907c-irq.c
@@ -0,0 +1,359 @@
+/*
+ * Battery driver for Maxim MAX8907C
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ * Based on driver/mfd/max8925-core.c, Copyright (C) 2009-2010 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907c.h>
+
+struct max8907c_irq_data {
+ int reg;
+ int mask_reg;
+ int enable; /* enable or not */
+ int offs; /* bit offset in mask register */
+ bool is_rtc;
+};
+
+static struct max8907c_irq_data max8907c_irqs[] = {
+ [MAX8907C_IRQ_VCHG_DC_OVP] = {
+ .reg = MAX8907C_REG_CHG_IRQ1,
+ .mask_reg = MAX8907C_REG_CHG_IRQ1_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907C_IRQ_VCHG_DC_F] = {
+ .reg = MAX8907C_REG_CHG_IRQ1,
+ .mask_reg = MAX8907C_REG_CHG_IRQ1_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907C_IRQ_VCHG_DC_R] = {
+ .reg = MAX8907C_REG_CHG_IRQ1,
+ .mask_reg = MAX8907C_REG_CHG_IRQ1_MASK,
+ .offs = 1 << 2,
+ },
+ [MAX8907C_IRQ_VCHG_THM_OK_R] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907C_IRQ_VCHG_THM_OK_F] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907C_IRQ_VCHG_MBATTLOW_F] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 2,
+ },
+ [MAX8907C_IRQ_VCHG_MBATTLOW_R] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 3,
+ },
+ [MAX8907C_IRQ_VCHG_RST] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 4,
+ },
+ [MAX8907C_IRQ_VCHG_DONE] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 5,
+ },
+ [MAX8907C_IRQ_VCHG_TOPOFF] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 6,
+ },
+ [MAX8907C_IRQ_VCHG_TMR_FAULT] = {
+ .reg = MAX8907C_REG_CHG_IRQ2,
+ .mask_reg = MAX8907C_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 7,
+ },
+ [MAX8907C_IRQ_GPM_RSTIN] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907C_IRQ_GPM_MPL] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907C_IRQ_GPM_SW_3SEC] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 2,
+ },
+ [MAX8907C_IRQ_GPM_EXTON_F] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 3,
+ },
+ [MAX8907C_IRQ_GPM_EXTON_R] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 4,
+ },
+ [MAX8907C_IRQ_GPM_SW_1SEC] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 5,
+ },
+ [MAX8907C_IRQ_GPM_SW_F] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 6,
+ },
+ [MAX8907C_IRQ_GPM_SW_R] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 7,
+ },
+ [MAX8907C_IRQ_GPM_SYSCKEN_F] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ2,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ2_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907C_IRQ_GPM_SYSCKEN_R] = {
+ .reg = MAX8907C_REG_ON_OFF_IRQ2,
+ .mask_reg = MAX8907C_REG_ON_OFF_IRQ2_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907C_IRQ_RTC_ALARM1] = {
+ .reg = MAX8907C_REG_RTC_IRQ,
+ .mask_reg = MAX8907C_REG_RTC_IRQ_MASK,
+ .offs = 1 << 2,
+ .is_rtc = true,
+ },
+ [MAX8907C_IRQ_RTC_ALARM0] = {
+ .reg = MAX8907C_REG_RTC_IRQ,
+ .mask_reg = MAX8907C_REG_RTC_IRQ_MASK,
+ .offs = 1 << 3,
+ .is_rtc = true,
+ },
+};
+
+static inline struct max8907c_irq_data *irq_to_max8907c(struct max8907c *chip,
+ int irq)
+{
+ return &max8907c_irqs[irq - chip->irq_base];
+}
+
+static irqreturn_t max8907c_irq(int irq, void *data)
+{
+ struct max8907c *chip = data;
+ struct max8907c_irq_data *irq_data;
+ struct i2c_client *i2c;
+ int read_reg = -1, value = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+ irq_data = &max8907c_irqs[i];
+
+ if (irq_data->is_rtc)
+ i2c = chip->i2c_rtc;
+ else
+ i2c = chip->i2c_power;
+
+ if (read_reg != irq_data->reg) {
+ read_reg = irq_data->reg;
+ value = max8907c_reg_read(i2c, irq_data->reg);
+ }
+
+ if (value & irq_data->enable)
+ handle_nested_irq(chip->irq_base + i);
+ }
+ return IRQ_HANDLED;
+}
+
+static void max8907c_irq_lock(unsigned int irq)
+{
+ struct max8907c *chip = get_irq_chip_data(irq);
+
+ mutex_lock(&chip->irq_lock);
+}
+
+static void max8907c_irq_sync_unlock(unsigned int irq)
+{
+ struct max8907c *chip = get_irq_chip_data(irq);
+ struct max8907c_irq_data *irq_data;
+ static unsigned char cache_chg[2] = {0xff, 0xff};
+ static unsigned char cache_on[2] = {0xff, 0xff};
+ static unsigned char cache_rtc = 0xff;
+ unsigned char irq_chg[2], irq_on[2];
+ unsigned char irq_rtc;
+ int i;
+
+ /* Load cached value. In initial, all IRQs are masked */
+ irq_chg[0] = cache_chg[0];
+ irq_chg[1] = cache_chg[1];
+ irq_on[0] = cache_on[0];
+ irq_on[1] = cache_on[1];
+ irq_rtc = cache_rtc;
+ for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+ irq_data = &max8907c_irqs[i];
+ /* 1 -- disable, 0 -- enable */
+ switch (irq_data->mask_reg) {
+ case MAX8907C_REG_CHG_IRQ1_MASK:
+ irq_chg[0] &= ~irq_data->enable;
+ break;
+ case MAX8907C_REG_CHG_IRQ2_MASK:
+ irq_chg[1] &= ~irq_data->enable;
+ break;
+ case MAX8907C_REG_ON_OFF_IRQ1_MASK:
+ irq_on[0] &= ~irq_data->enable;
+ break;
+ case MAX8907C_REG_ON_OFF_IRQ2_MASK:
+ irq_on[1] &= ~irq_data->enable;
+ break;
+ case MAX8907C_REG_RTC_IRQ_MASK:
+ irq_rtc &= ~irq_data->enable;
+ break;
+ default:
+ dev_err(chip->dev, "wrong IRQ\n");
+ break;
+ }
+ }
+ /* update mask into registers */
+ if (cache_chg[0] != irq_chg[0]) {
+ cache_chg[0] = irq_chg[0];
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ1_MASK,
+ irq_chg[0]);
+ }
+ if (cache_chg[1] != irq_chg[1]) {
+ cache_chg[1] = irq_chg[1];
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ2_MASK,
+ irq_chg[1]);
+ }
+ if (cache_on[0] != irq_on[0]) {
+ cache_on[0] = irq_on[0];
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1_MASK,
+ irq_on[0]);
+ }
+ if (cache_on[1] != irq_on[1]) {
+ cache_on[1] = irq_on[1];
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2_MASK,
+ irq_on[1]);
+ }
+ if (cache_rtc != irq_rtc) {
+ cache_rtc = irq_rtc;
+ max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ_MASK,
+ irq_rtc);
+ }
+
+ mutex_unlock(&chip->irq_lock);
+}
+
+static void max8907c_irq_enable(unsigned int irq)
+{
+ struct max8907c *chip = get_irq_chip_data(irq);
+ max8907c_irqs[irq - chip->irq_base].enable
+ = max8907c_irqs[irq - chip->irq_base].offs;
+}
+
+static void max8907c_irq_disable(unsigned int irq)
+{
+ struct max8907c *chip = get_irq_chip_data(irq);
+ max8907c_irqs[irq - chip->irq_base].enable = 0;
+}
+
+static struct irq_chip max8907c_irq_chip = {
+ .name = "max8907c",
+ .bus_lock = max8907c_irq_lock,
+ .bus_sync_unlock = max8907c_irq_sync_unlock,
+ .enable = max8907c_irq_enable,
+ .disable = max8907c_irq_disable,
+};
+
+int max8907c_irq_init(struct max8907c *chip, int irq, int irq_base)
+{
+ unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+ struct irq_desc *desc;
+ int i, ret;
+ int __irq;
+
+ if (!irq_base || !irq) {
+ dev_warn(chip->dev, "No interrupt support\n");
+ return -EINVAL;
+ }
+ /* clear all interrupts */
+ max8907c_reg_read(chip->i2c_power, MAX8907C_REG_CHG_IRQ1);
+ max8907c_reg_read(chip->i2c_power, MAX8907C_REG_CHG_IRQ2);
+ max8907c_reg_read(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1);
+ max8907c_reg_read(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2);
+ max8907c_reg_read(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ);
+ /* mask all interrupts */
+ max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_ALARM0_CNTL, 0);
+ max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_ALARM1_CNTL, 0);
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ1_MASK, 0xff);
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_CHG_IRQ2_MASK, 0xff);
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ1_MASK, 0xff);
+ max8907c_reg_write(chip->i2c_power, MAX8907C_REG_ON_OFF_IRQ2_MASK, 0xff);
+ max8907c_reg_write(chip->i2c_rtc, MAX8907C_REG_RTC_IRQ_MASK, 0xff);
+
+ mutex_init(&chip->irq_lock);
+ chip->core_irq = irq;
+ chip->irq_base = irq_base;
+ desc = irq_to_desc(chip->core_irq);
+
+ /* register with genirq */
+ for (i = 0; i < ARRAY_SIZE(max8907c_irqs); i++) {
+ __irq = i + chip->irq_base;
+ set_irq_chip_data(__irq, chip);
+ set_irq_chip_and_handler(__irq, &max8907c_irq_chip,
+ handle_edge_irq);
+ set_irq_nested_thread(__irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(__irq, IRQF_VALID);
+#else
+ set_irq_noprobe(__irq);
+#endif
+ }
+
+ ret = request_threaded_irq(irq, NULL, max8907c_irq, flags,
+ "max8907c", chip);
+ if (ret) {
+ dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
+ chip->core_irq = 0;
+ }
+
+ return ret;
+}
+
+int max8907c_suspend(struct i2c_client *i2c, pm_message_t state)
+{
+ struct max8907c *max8907c = i2c_get_clientdata(i2c);
+
+ disable_irq(max8907c->core_irq);
+
+ return 0;
+}
+
+int max8907c_resume(struct i2c_client *i2c)
+{
+ struct max8907c *max8907c = i2c_get_clientdata(i2c);
+
+ enable_irq(max8907c->core_irq);
+
+ return 0;
+}
+
+void max8907c_irq_free(struct max8907c *chip)
+{
+ if (chip->core_irq)
+ free_irq(chip->core_irq, chip);
+}
+
diff --git a/drivers/mfd/max8907c.c b/drivers/mfd/max8907c.c
index 0ceccbab7e37..8475c4be50e9 100644
--- a/drivers/mfd/max8907c.c
+++ b/drivers/mfd/max8907c.c
@@ -17,20 +17,57 @@
static struct mfd_cell cells[] = {
{.name = "max8907-regulator",},
+ {.name = "max8907c-rtc",},
};
-int max8907c_reg_read(struct device *dev, u8 reg)
+static int max8907c_i2c_read(struct i2c_client *i2c, u8 reg, u8 count, u8 *dest)
{
- struct i2c_client *i2c = to_i2c_client(dev);
- struct max8907c *max8907c = dev_get_drvdata(dev);
- u8 val;
- int ret;
+ struct i2c_msg xfer[2];
+ int ret = 0;
- mutex_lock(&max8907c->io_lock);
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = I2C_M_NOSTART;
+ xfer[0].len = 1;
+ xfer[0].buf = &reg;
- ret = max8907c->read_dev(i2c, reg, 1, &val);
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = count;
+ xfer[1].buf = dest;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int max8907c_i2c_write(struct i2c_client *i2c, u8 reg, u8 count, const u8 *src)
+{
+ u8 msg[0x100 + 1];
+ int ret = 0;
+
+ msg[0] = reg;
+ memcpy(&msg[1], src, count);
+
+ ret = i2c_master_send(i2c, msg, count + 1);
+ if (ret < 0)
+ return ret;
+ if (ret != count + 1)
+ return -EIO;
+
+ return 0;
+}
+
+int max8907c_reg_read(struct i2c_client *i2c, u8 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = max8907c_i2c_read(i2c, reg, 1, &val);
- mutex_unlock(&max8907c->io_lock);
pr_debug("max8907c: reg read reg=%x, val=%x\n",
(unsigned int)reg, (unsigned int)val);
@@ -40,43 +77,72 @@ int max8907c_reg_read(struct device *dev, u8 reg)
}
EXPORT_SYMBOL_GPL(max8907c_reg_read);
-int max8907c_reg_write(struct device *dev, u8 reg, u8 val)
+int max8907c_reg_bulk_read(struct i2c_client *i2c, u8 reg, u8 count, u8 *val)
{
- struct i2c_client *i2c = to_i2c_client(dev);
- struct max8907c *max8907c = dev_get_drvdata(dev);
+ int ret;
+
+ ret = max8907c_i2c_read(i2c, reg, count, val);
+
+ pr_debug("max8907c: reg read reg=%x, val=%x\n",
+ (unsigned int)reg, (unsigned int)*val);
+
+ if (ret != 0)
+ pr_err("Failed to read max8907c I2C driver: %d\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max8907c_reg_bulk_read);
+
+int max8907c_reg_write(struct i2c_client *i2c, u8 reg, u8 val)
+{
+ struct max8907c *max8907c = i2c_get_clientdata(i2c);
int ret;
pr_debug("max8907c: reg write reg=%x, val=%x\n",
(unsigned int)reg, (unsigned int)val);
+
mutex_lock(&max8907c->io_lock);
+ ret = max8907c_i2c_write(i2c, reg, 1, &val);
+ mutex_unlock(&max8907c->io_lock);
+
+ if (ret != 0)
+ pr_err("Failed to write max8907c I2C driver: %d\n", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max8907c_reg_write);
- ret = max8907c->write_dev(i2c, reg, 1, &val);
+int max8907c_reg_bulk_write(struct i2c_client *i2c, u8 reg, u8 count, u8 *val)
+{
+ struct max8907c *max8907c = i2c_get_clientdata(i2c);
+ int ret;
+ pr_debug("max8907c: reg write reg=%x, val=%x\n",
+ (unsigned int)reg, (unsigned int)*val);
+
+ mutex_lock(&max8907c->io_lock);
+ ret = max8907c_i2c_write(i2c, reg, count, val);
mutex_unlock(&max8907c->io_lock);
if (ret != 0)
pr_err("Failed to write max8907c I2C driver: %d\n", ret);
return ret;
}
-EXPORT_SYMBOL_GPL(max8907c_reg_write);
+EXPORT_SYMBOL_GPL(max8907c_reg_bulk_write);
-int max8907c_set_bits(struct device *dev, u8 reg, u8 mask, u8 val)
+int max8907c_set_bits(struct i2c_client *i2c, u8 reg, u8 mask, u8 val)
{
- struct i2c_client *i2c = to_i2c_client(dev);
- struct max8907c *max8907c = dev_get_drvdata(dev);
+ struct max8907c *max8907c = i2c_get_clientdata(i2c);
u8 tmp;
int ret;
pr_debug("max8907c: reg write reg=%02X, val=%02X, mask=%02X\n",
(unsigned int)reg, (unsigned int)val, (unsigned int)mask);
- mutex_lock(&max8907c->io_lock);
- ret = max8907c->read_dev(i2c, reg, 1, &tmp);
+ mutex_lock(&max8907c->io_lock);
+ ret = max8907c_i2c_read(i2c, reg, 1, &tmp);
if (ret == 0) {
val = (tmp & ~mask) | (val & mask);
- ret = max8907c->write_dev(i2c, reg, 1, &val);
+ ret = max8907c_i2c_write(i2c, reg, 1, &val);
}
-
mutex_unlock(&max8907c->io_lock);
if (ret != 0)
@@ -85,49 +151,6 @@ int max8907c_set_bits(struct device *dev, u8 reg, u8 mask, u8 val)
}
EXPORT_SYMBOL_GPL(max8907c_set_bits);
-static int max8907c_i2c_read(void *io_data, u8 reg, u8 count, u8 * dest)
-{
- struct i2c_client *i2c = (struct i2c_client *)io_data;
- struct i2c_msg xfer[2];
- int ret = 0;
-
- xfer[0].addr = i2c->addr;
- xfer[0].flags = I2C_M_NOSTART;
- xfer[0].len = 1;
- xfer[0].buf = &reg;
-
- xfer[1].addr = i2c->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = count;
- xfer[1].buf = dest;
-
- ret = i2c_transfer(i2c->adapter, xfer, 2);
- if (ret < 0)
- return ret;
- if (ret != 2)
- return -EIO;
-
- return 0;
-}
-
-static int max8907c_i2c_write(void *io_data, u8 reg, u8 count, const u8 * src)
-{
- struct i2c_client *i2c = (struct i2c_client *)io_data;
- u8 msg[0x100 + 1];
- int ret = 0;
-
- msg[0] = reg;
- memcpy(&msg[1], src, count);
-
- ret = i2c_master_send(i2c, msg, count + 1);
- if (ret < 0)
- return ret;
- if (ret != count + 1)
- return -EIO;
-
- return 0;
-}
-
static int max8907c_remove_subdev(struct device *dev, void *unused)
{
platform_device_unregister(to_platform_device(dev));
@@ -177,11 +200,15 @@ static int max8907c_i2c_probe(struct i2c_client *i2c,
if (max8907c == NULL)
return -ENOMEM;
- max8907c->read_dev = max8907c_i2c_read;
- max8907c->write_dev = max8907c_i2c_write;
max8907c->dev = &i2c->dev;
+ dev_set_drvdata(max8907c->dev, max8907c);
+
+ max8907c->i2c_power = i2c;
i2c_set_clientdata(i2c, max8907c);
+ max8907c->i2c_rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ i2c_set_clientdata(max8907c->i2c_rtc, max8907c);
+
mutex_init(&max8907c->io_lock);
for (i = 0; i < ARRAY_SIZE(cells); i++)
@@ -189,11 +216,14 @@ static int max8907c_i2c_probe(struct i2c_client *i2c,
ret = mfd_add_devices(max8907c->dev, -1, cells, ARRAY_SIZE(cells),
NULL, 0);
if (ret != 0) {
+ i2c_unregister_device(max8907c->i2c_rtc);
kfree(max8907c);
pr_debug("max8907c: failed to add MFD devices %X\n", ret);
return ret;
}
+ max8907c_irq_init(max8907c, i2c->irq, pdata->irq_base);
+
ret = max8097c_add_subdevs(max8907c, pdata);
return ret;
@@ -203,7 +233,10 @@ static int max8907c_i2c_remove(struct i2c_client *i2c)
{
struct max8907c *max8907c = i2c_get_clientdata(i2c);
+ max8907c_remove_subdevs(max8907c);
+ i2c_unregister_device(max8907c->i2c_rtc);
mfd_remove_devices(max8907c->dev);
+ max8907c_irq_free(max8907c);
kfree(max8907c);
return 0;
@@ -223,6 +256,8 @@ static struct i2c_driver max8907c_i2c_driver = {
},
.probe = max8907c_i2c_probe,
.remove = max8907c_i2c_remove,
+ .suspend = max8907c_suspend,
+ .resume = max8907c_resume,
.id_table = max8907c_i2c_id,
};
diff --git a/include/linux/mfd/max8907c.h b/include/linux/mfd/max8907c.h
index c78849a32860..d4d62d4a6421 100644
--- a/include/linux/mfd/max8907c.h
+++ b/include/linux/mfd/max8907c.h
@@ -145,7 +145,7 @@
#define MAX8907C_REG_RTC_STATUS 0x1A
#define MAX8907C_REG_RTC_CNTL 0x1B
#define MAX8907C_REG_RTC_IRQ 0x1C
-#define MAX8907C_REG_IRQ_IRQ_MASK 0x1D
+#define MAX8907C_REG_RTC_IRQ_MASK 0x1D
#define MAX8907C_REG_MPL_CNTL 0x1E
/* ADC and Touch Screen Controller register map */
@@ -162,22 +162,60 @@
#define MAX8907C_MASK_OUT5V_ENSRC 0x0E
#define MAX8907C_MASK_OUT5V_EN 0x01
-struct max8907c {
- struct device *dev;
+#define RTC_I2C_ADDR 0x68
- int (*read_dev) (void *io_data, u8 reg, u8 count, u8 * dst);
- int (*write_dev) (void *io_data, u8 reg, u8 count, const u8 * src);
+/* IRQ definitions */
+enum {
+ MAX8907C_IRQ_VCHG_DC_OVP,
+ MAX8907C_IRQ_VCHG_DC_F,
+ MAX8907C_IRQ_VCHG_DC_R,
+ MAX8907C_IRQ_VCHG_THM_OK_R,
+ MAX8907C_IRQ_VCHG_THM_OK_F,
+ MAX8907C_IRQ_VCHG_MBATTLOW_F,
+ MAX8907C_IRQ_VCHG_MBATTLOW_R,
+ MAX8907C_IRQ_VCHG_RST,
+ MAX8907C_IRQ_VCHG_DONE,
+ MAX8907C_IRQ_VCHG_TOPOFF,
+ MAX8907C_IRQ_VCHG_TMR_FAULT,
+ MAX8907C_IRQ_GPM_RSTIN,
+ MAX8907C_IRQ_GPM_MPL,
+ MAX8907C_IRQ_GPM_SW_3SEC,
+ MAX8907C_IRQ_GPM_EXTON_F,
+ MAX8907C_IRQ_GPM_EXTON_R,
+ MAX8907C_IRQ_GPM_SW_1SEC,
+ MAX8907C_IRQ_GPM_SW_F,
+ MAX8907C_IRQ_GPM_SW_R,
+ MAX8907C_IRQ_GPM_SYSCKEN_F,
+ MAX8907C_IRQ_GPM_SYSCKEN_R,
+ MAX8907C_IRQ_RTC_ALARM1,
+ MAX8907C_IRQ_RTC_ALARM0,
+ MAX8907C_NR_IRQS,
+};
- struct mutex io_lock;
+struct max8907c {
+ struct device *dev;
+ struct mutex io_lock;
+ struct mutex irq_lock;
+ struct i2c_client *i2c_power;
+ struct i2c_client *i2c_rtc;
+ int irq_base;
+ int core_irq;
};
struct max8907c_platform_data {
int num_subdevs;
struct platform_device **subdevs;
+ int irq_base;
};
-int max8907c_reg_read(struct device *dev, u8 reg);
-int max8907c_reg_write(struct device *dev, u8 reg, u8 val);
-int max8907c_set_bits(struct device *dev, u8 reg, u8 mask, u8 val);
+int max8907c_reg_read(struct i2c_client *i2c, u8 reg);
+int max8907c_reg_bulk_read(struct i2c_client *i2c, u8 reg, u8 count, u8 *val);
+int max8907c_reg_write(struct i2c_client *i2c, u8 reg, u8 val);
+int max8907c_reg_bulk_write(struct i2c_client *i2c, u8 reg, u8 count, u8 *val);
+int max8907c_set_bits(struct i2c_client *i2c, u8 reg, u8 mask, u8 val);
+int max8907c_irq_init(struct max8907c *chip, int irq, int irq_base);
+void max8907c_irq_free(struct max8907c *chip);
+int max8907c_suspend(struct i2c_client *i2c, pm_message_t state);
+int max8907c_resume(struct i2c_client *i2c);
#endif