summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 ae6c2e7f8151..4bb7f8acc81b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -103,3 +103,4 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.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