aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/mfd
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig42
-rw-r--r--drivers/mfd/Makefile8
-rw-r--r--drivers/mfd/max77686-irq.c344
-rw-r--r--drivers/mfd/max77686.c407
-rw-r--r--drivers/mfd/max77693-irq.c349
-rw-r--r--drivers/mfd/max77693.c390
-rw-r--r--drivers/mfd/max8698.c161
-rw-r--r--drivers/mfd/max8997-irq.c558
-rw-r--r--drivers/mfd/max8997.c298
-rw-r--r--drivers/mfd/s5m-core.c249
-rw-r--r--drivers/mfd/s5m-irq.c487
-rw-r--r--drivers/mfd/wm8994-core.c242
-rw-r--r--drivers/mfd/wm8994-irq.c12
13 files changed, 3047 insertions, 500 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6ca938a..862aa03 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -349,6 +349,48 @@ config MFD_MAX8998
additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_MAX8698
+ bool "Maxim Semiconductor MAX8698 PMIC Support"
+ depends on I2C=y
+ select MFD_CORE
+ help
+ Say yes here to support for Maxim Semiconductor MAX8698. This is
+ a Power Management IC. This driver provies common support for
+ accessing the device, additional drivers must be enabled in order
+ to use the functionality of the device.
+
+config MFD_MAX77686
+ bool "Maxim Semiconductor MAX77686 PMIC Support"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Say yes here to support for Maxim Semiconductor MAX77686.
+ This is a Power Management IC with RTC 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_MAX77693
+ bool "Maxim Semiconductor MAX77693 PMIC Support"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Say yes here to support for Maxim Semiconductor MAX77963.
+ This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
+ MUIC controls on chip.
+ This driver provides common support for accessing the device;
+ additional drivers
+
+config MFD_S5M_CORE
+ bool "SAMSUNG S5M Series Support"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Support for the Samsung Electronics S5M MFD series.
+ This driver provies common support for accessing the device,
+ additional drivers must be enabled in order to use the functionality
+ of the device
+
config MFD_WM8400
tristate "Support Wolfson Microelectronics WM8400"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d7d47d2..16ffd4c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -64,6 +64,9 @@ max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o
+obj-$(CONFIG_MFD_MAX8698) += max8698.o
+obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
+obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
pcf50633-objs := pcf50633-core.o pcf50633-irq.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
@@ -91,7 +94,8 @@ obj-$(CONFIG_MFD_VX855) += vx855.o
obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
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_MFD_PM8921_CORE) += pm8921-core.o
+obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
+obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
diff --git a/drivers/mfd/max77686-irq.c b/drivers/mfd/max77686-irq.c
new file mode 100644
index 0000000..152a488
--- /dev/null
+++ b/drivers/mfd/max77686-irq.c
@@ -0,0 +1,344 @@
+/*
+ * max77686-irq.c - Interrupt controller support for MAX77686
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Chiwoong Byun <woong.byun@samsung.com>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+
+#undef MAX77686_IRQ_TEST
+
+enum {
+ MAX77686_DEBUG_IRQ_INFO = 1 << 0,
+ MAX77686_DEBUG_IRQ_MASK = 1 << 1,
+ MAX77686_DEBUG_IRQ_INT = 1 << 2,
+};
+
+static int debug_mask = MAX77686_DEBUG_IRQ_INFO | MAX77686_DEBUG_IRQ_MASK |
+ MAX77686_DEBUG_IRQ_INT;
+
+static const u8 max77686_mask_reg[] = {
+ [PMIC_INT1] = MAX77686_REG_INT1MSK,
+ [PMIC_INT2] = MAX77686_REG_INT2MSK,
+ [RTC_INT] = MAX77686_RTC_INTM,
+};
+
+static struct i2c_client *max77686_get_i2c(struct max77686_dev *max77686,
+ enum max77686_irq_source src)
+{
+ switch (src) {
+ case PMIC_INT1 ... PMIC_INT2:
+ return max77686->i2c;
+ case RTC_INT:
+ return max77686->rtc;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+struct max77686_irq_data {
+ int mask;
+ enum max77686_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask) \
+ [(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77686_irq_data max77686_irqs[] = {
+ DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0),
+ DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1),
+ DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2),
+ DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3),
+ DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4),
+ DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5),
+ DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6),
+ DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7),
+ DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0),
+ DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2),
+ DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3),
+ DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4),
+ DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5),
+};
+
+static void max77686_irq_lock(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_debug("%s\n", __func__);
+
+ mutex_lock(&max77686->irqlock);
+}
+
+static void max77686_irq_sync_unlock(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ int i;
+
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ u8 mask_reg = max77686_mask_reg[i];
+ struct i2c_client *i2c = max77686_get_i2c(max77686, i);
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+ __func__, i, mask_reg, max77686->irq_masks_cur[i]);
+
+ if (mask_reg == MAX77686_REG_INVALID ||
+ IS_ERR_OR_NULL(i2c))
+ continue;
+
+ max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
+
+ max77686_write_reg(i2c, max77686_mask_reg[i],
+ max77686->irq_masks_cur[i]);
+ }
+
+ mutex_unlock(&max77686->irqlock);
+}
+
+static const inline struct max77686_irq_data *
+irq_to_max77686_irq(struct max77686_dev *max77686, int irq)
+{
+ return &max77686_irqs[irq - max77686->irq_base];
+}
+
+static void max77686_irq_mask(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ const struct max77686_irq_data *irq_data = irq_to_max77686_irq(max77686,
+ data->irq);
+
+ max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_info("%s: group=%d, cur=0x%x\n",
+ __func__, irq_data->group,
+ max77686->irq_masks_cur[irq_data->group]);
+}
+
+static void max77686_irq_unmask(struct irq_data *data)
+{
+ struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
+ const struct max77686_irq_data *irq_data = irq_to_max77686_irq(max77686,
+ data->irq);
+
+ max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
+ pr_info("%s: group=%d, cur=0x%x\n",
+ __func__, irq_data->group,
+ max77686->irq_masks_cur[irq_data->group]);
+}
+
+static struct irq_chip max77686_irq_chip = {
+ .name = "max77686",
+ .irq_bus_lock = max77686_irq_lock,
+ .irq_bus_sync_unlock = max77686_irq_sync_unlock,
+ .irq_mask = max77686_irq_mask,
+ .irq_unmask = max77686_irq_unmask,
+};
+
+static irqreturn_t max77686_irq_thread(int irq, void *data)
+{
+ struct max77686_dev *max77686 = data;
+ u8 irq_reg[MAX77686_IRQ_GROUP_NR] = {};
+ u8 irq_src;
+ int ret;
+ int i;
+
+ ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INTSRC, &irq_src);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+ /* MAX77686_IRQSRC_RTC may be set even if there are pending at INT1/2 */
+ ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INT1, &irq_reg[0]);
+ ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INT2, &irq_reg[1]);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read pmic interrupt: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: int1=0x%x, int2=0x%x\n",
+ __func__, irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
+
+ if (irq_src & MAX77686_IRQSRC_RTC) {
+#ifdef CONFIG_RTC_DRV_MAX77686
+ ret = max77686_read_reg(max77686->rtc, MAX77686_RTC_INT, &irq_reg[RTC_INT]);
+#else
+ ret = -ENODEV;
+#endif
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read rtc interrupt: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: rtc int=0x%x\n", __func__, irq_reg[RTC_INT]);
+
+ }
+
+ for (i = 0; i < MAX77686_IRQ_NR; i++) {
+ if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask)
+ handle_nested_irq(max77686->irq_base + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int max77686_irq_resume(struct max77686_dev *max77686)
+{
+ if (max77686->irq && max77686->irq_base)
+ max77686_irq_thread(max77686->irq_base, max77686);
+ return 0;
+}
+
+int max77686_irq_init(struct max77686_dev *max77686)
+{
+ int i;
+ int cur_irq;
+ int ret;
+ int val;
+#ifdef MAX77686_IRQ_TEST
+ u8 irq_reg[6] = { };
+ u8 irq_src;
+#endif
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
+ pr_info("%s+\n", __func__);
+
+ if (!max77686->irq_gpio) {
+ dev_warn(max77686->dev, "No interrupt gpio specified.\n");
+ max77686->irq_base = 0;
+ return 0;
+ }
+
+ if (!max77686->irq_base) {
+ dev_err(max77686->dev, "No interrupt base specified.\n");
+ return 0;
+ }
+
+ mutex_init(&max77686->irqlock);
+
+ max77686->irq = gpio_to_irq(max77686->irq_gpio);
+ ret = gpio_request(max77686->irq_gpio, "pmic_irq");
+ if (ret < 0 && ret != -EBUSY) {
+ dev_err(max77686->dev,
+ "Failed to request gpio %d with ret: %d\n",
+ max77686->irq_gpio, ret);
+ return IRQ_NONE;
+ }
+ if (ret == -EBUSY)
+ dev_warn(max77686->dev, "gpio pmic_irq is already requested\n");
+
+ gpio_direction_input(max77686->irq_gpio);
+ val = gpio_get_value(max77686->irq_gpio);
+ gpio_free(max77686->irq_gpio);
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INT)
+ pr_info("%s: gpio_irq=%x\n", __func__, val);
+
+#ifdef MAX77686_IRQ_TEST
+ ret = max77686_read_reg(max77686->i2c, MAX77686_REG_INTSRC, &irq_src);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+ ret = max77686_bulk_read(max77686->i2c, MAX77686_REG_INT1, 6, irq_reg);
+ if (ret < 0) {
+ dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < 6; i++)
+ pr_info("%s: i[%d]=0x%x\n", __func__, i, irq_reg[i]);
+#endif /* MAX77686_IRQ_TEST */
+
+ /* Mask individual interrupt sources */
+ for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
+ struct i2c_client *i2c;
+
+ max77686->irq_masks_cur[i] = 0xff;
+ max77686->irq_masks_cache[i] = 0xff;
+ i2c = max77686_get_i2c(max77686, i);
+
+ if (IS_ERR_OR_NULL(i2c))
+ continue;
+ if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
+ continue;
+
+ max77686_write_reg(i2c, max77686_mask_reg[i], 0xff);
+ }
+
+ /* Register with genirq */
+ for (i = 0; i < MAX77686_IRQ_NR; i++) {
+ cur_irq = i + max77686->irq_base;
+ irq_set_chip_data(cur_irq, max77686);
+ irq_set_chip_and_handler(cur_irq, &max77686_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max77686-irq", max77686);
+
+ if (ret) {
+ dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
+ max77686->irq, ret);
+ return ret;
+ }
+
+ if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
+ pr_info("%s-\n", __func__);
+
+ return 0;
+}
+
+void max77686_irq_exit(struct max77686_dev *max77686)
+{
+ if (max77686->irq)
+ free_irq(max77686->irq, max77686);
+}
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
new file mode 100644
index 0000000..9f5004d
--- /dev/null
+++ b/drivers/mfd/max77686.c
@@ -0,0 +1,407 @@
+/*
+ * max77686.c - mfd core driver for the Maxim 77686
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.com>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/interrupt.h>
+
+#define I2C_ADDR_RTC (0x0C >> 1)
+
+static struct mfd_cell max77686_devs[] = {
+ { .name = "max77686-pmic", },
+#ifdef CONFIG_RTC_DRV_MAX77686
+ { .name = "max77686-rtc", },
+#endif
+};
+
+int max77686_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77686->iolock);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ mutex_unlock(&max77686->iolock);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+ *dest = ret;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_read_reg);
+
+int max77686_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77686->iolock);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&max77686->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_read);
+
+int max77686_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77686->iolock);
+ ret = i2c_smbus_write_byte_data(i2c, reg, value);
+ mutex_unlock(&max77686->iolock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max77686_write_reg);
+
+int max77686_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77686->iolock);
+ ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&max77686->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max77686_bulk_write);
+
+int max77686_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77686->iolock);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ if (ret >= 0) {
+ u8 old_val = ret & 0xff;
+ u8 new_val = (val & mask) | (old_val & (~mask));
+ ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+ }
+ mutex_unlock(&max77686->iolock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max77686_update_reg);
+
+static int max77686_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max77686_dev *max77686;
+ struct max77686_platform_data *pdata = i2c->dev.platform_data;
+ u8 data;
+ int ret = 0;
+
+ max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
+ if (max77686 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max77686);
+ max77686->dev = &i2c->dev;
+ max77686->i2c = i2c;
+ max77686->type = id->driver_data;
+
+ if (!pdata) {
+ ret = -EIO;
+ goto err;
+ }
+
+ max77686->wakeup = pdata->wakeup;
+ max77686->irq_gpio = pdata->irq_gpio;
+ max77686->irq_base = pdata->irq_base;
+ max77686->wtsr_smpl = pdata->wtsr_smpl;
+
+ mutex_init(&max77686->iolock);
+
+ if (max77686_read_reg(i2c, MAX77686_REG_DEVICE_ID, &data) < 0) {
+ dev_err(max77686->dev,
+ "device not found on this channel (this is not an error)\n");
+ ret = -ENODEV;
+ goto err;
+ } else
+ dev_info(max77686->dev, "device found\n");
+
+#ifdef CONFIG_RTC_DRV_MAX77686
+ max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ i2c_set_clientdata(max77686->rtc, max77686);
+#endif
+
+ max77686_irq_init(max77686);
+
+ ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
+ ARRAY_SIZE(max77686_devs), NULL, 0);
+
+ if (ret < 0)
+ goto err_mfd;
+
+ device_init_wakeup(max77686->dev, pdata->wakeup);
+
+ return ret;
+
+err_mfd:
+ mfd_remove_devices(max77686->dev);
+#ifdef CONFIG_RTC_DRV_MAX77686
+ i2c_unregister_device(max77686->rtc);
+#endif
+err:
+ kfree(max77686);
+ return ret;
+}
+
+static int max77686_i2c_remove(struct i2c_client *i2c)
+{
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max77686->dev);
+#ifdef CONFIG_RTC_DRV_MAX77686
+ i2c_unregister_device(max77686->rtc);
+#endif
+ kfree(max77686);
+
+ return 0;
+}
+
+static const struct i2c_device_id max77686_i2c_id[] = {
+ { "max77686", TYPE_MAX77686 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
+
+#ifdef CONFIG_PM
+static int max77686_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ disable_irq(max77686->irq);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(max77686->irq);
+
+ return 0;
+}
+
+static int max77686_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(max77686->irq);
+
+ enable_irq(max77686->irq);
+
+ return max77686_irq_resume(max77686);
+}
+#else
+#define max77686_suspend NULL
+#define max77686_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_HIBERNATION
+
+u8 max77686_dumpaddr_pmic[] = {
+ MAX77686_REG_INT1MSK,
+ MAX77686_REG_INT2MSK,
+ MAX77686_REG_ONOFF_DELAY,
+ MAX77686_REG_MRSTB ,
+ /* Reserved: 0x0B-0x0F */
+ MAX77686_REG_BUCK1CTRL,
+ MAX77686_REG_BUCK1OUT,
+ MAX77686_REG_BUCK2CTRL1,
+ MAX77686_REG_BUCK234FREQ,
+ MAX77686_REG_BUCK2DVS1,
+ MAX77686_REG_BUCK2DVS2,
+ MAX77686_REG_BUCK2DVS3,
+ MAX77686_REG_BUCK2DVS4,
+ MAX77686_REG_BUCK2DVS5,
+ MAX77686_REG_BUCK2DVS6,
+ MAX77686_REG_BUCK2DVS7,
+ MAX77686_REG_BUCK2DVS8,
+ MAX77686_REG_BUCK3CTRL1,
+ /* Reserved: 0x1D */
+ MAX77686_REG_BUCK3DVS1,
+ MAX77686_REG_BUCK3DVS2,
+ MAX77686_REG_BUCK3DVS3,
+ MAX77686_REG_BUCK3DVS4,
+ MAX77686_REG_BUCK3DVS5,
+ MAX77686_REG_BUCK3DVS6,
+ MAX77686_REG_BUCK3DVS7,
+ MAX77686_REG_BUCK3DVS8,
+ MAX77686_REG_BUCK4CTRL1,
+ /* Reserved: 0x27 */
+ MAX77686_REG_BUCK4DVS1,
+ MAX77686_REG_BUCK4DVS2,
+ MAX77686_REG_BUCK4DVS3,
+ MAX77686_REG_BUCK4DVS4,
+ MAX77686_REG_BUCK4DVS5,
+ MAX77686_REG_BUCK4DVS6,
+ MAX77686_REG_BUCK4DVS7,
+ MAX77686_REG_BUCK4DVS8,
+ MAX77686_REG_BUCK5CTRL,
+ MAX77686_REG_BUCK5OUT,
+ MAX77686_REG_BUCK6CTRL,
+ MAX77686_REG_BUCK6OUT,
+ MAX77686_REG_BUCK7CTRL,
+ MAX77686_REG_BUCK7OUT,
+ MAX77686_REG_BUCK8CTRL,
+ MAX77686_REG_BUCK8OUT,
+ MAX77686_REG_BUCK9CTRL,
+ MAX77686_REG_BUCK9OUT,
+ /* Reserved: 0x3A-0x3F */
+ MAX77686_REG_LDO1CTRL1 ,
+ MAX77686_REG_LDO2CTRL1 ,
+ MAX77686_REG_LDO3CTRL1 ,
+ MAX77686_REG_LDO4CTRL1 ,
+ MAX77686_REG_LDO5CTRL1 ,
+ MAX77686_REG_LDO6CTRL1,
+ MAX77686_REG_LDO7CTRL1 ,
+ MAX77686_REG_LDO8CTRL1 ,
+ MAX77686_REG_LDO9CTRL1 ,
+ MAX77686_REG_LDO10CTRL1,
+ MAX77686_REG_LDO11CTRL1,
+ MAX77686_REG_LDO12CTRL1,
+ MAX77686_REG_LDO13CTRL1,
+ MAX77686_REG_LDO14CTRL1,
+ MAX77686_REG_LDO15CTRL1,
+ MAX77686_REG_LDO16CTRL1,
+ MAX77686_REG_LDO17CTRL1,
+ MAX77686_REG_LDO18CTRL1,
+ MAX77686_REG_LDO19CTRL1,
+ MAX77686_REG_LDO20CTRL1,
+ MAX77686_REG_LDO21CTRL1,
+ MAX77686_REG_LDO22CTRL1,
+ MAX77686_REG_LDO23CTRL1,
+ MAX77686_REG_LDO24CTRL1,
+ MAX77686_REG_LDO25CTRL1,
+ MAX77686_REG_LDO26CTRL1,
+ /* Reserved: 0x5A-0x5F */
+ MAX77686_REG_LDO1CTRL2 ,
+ MAX77686_REG_LDO2CTRL2 ,
+ MAX77686_REG_LDO3CTRL2 ,
+ MAX77686_REG_LDO4CTRL2 ,
+ MAX77686_REG_LDO5CTRL2 ,
+ MAX77686_REG_LDO6CTRL2,
+ MAX77686_REG_LDO7CTRL2 ,
+ MAX77686_REG_LDO8CTRL2 ,
+ MAX77686_REG_LDO9CTRL2 ,
+ MAX77686_REG_LDO10CTRL2,
+ MAX77686_REG_LDO11CTRL2,
+ MAX77686_REG_LDO12CTRL2,
+ MAX77686_REG_LDO13CTRL2,
+ MAX77686_REG_LDO14CTRL2,
+ MAX77686_REG_LDO15CTRL2,
+ MAX77686_REG_LDO16CTRL2,
+ MAX77686_REG_LDO17CTRL2,
+ MAX77686_REG_LDO18CTRL2,
+ MAX77686_REG_LDO19CTRL2,
+ MAX77686_REG_LDO20CTRL2,
+ MAX77686_REG_LDO21CTRL2,
+ MAX77686_REG_LDO22CTRL2,
+ MAX77686_REG_LDO23CTRL2,
+ MAX77686_REG_LDO24CTRL2,
+ MAX77686_REG_LDO25CTRL2,
+ MAX77686_REG_LDO26CTRL2,
+ MAX77686_REG_BBAT_CHG,
+ MAX77686_REG_32KHZ,
+};
+
+static int max77686_freeze(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max77686_dumpaddr_pmic); i++)
+ max77686_read_reg(i2c, max77686_dumpaddr_pmic[i],
+ &max77686->reg_dump[i]);
+
+ disable_irq(max77686->irq);
+
+ return 0;
+}
+
+static int max77686_restore(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
+ int i;
+
+ enable_irq(max77686->irq);
+
+ for (i = 0; i < ARRAY_SIZE(max77686_dumpaddr_pmic); i++)
+ max77686_write_reg(i2c, max77686_dumpaddr_pmic[i],
+ max77686->reg_dump[i]);
+
+ return 0;
+}
+#endif
+
+const struct dev_pm_ops max77686_pm = {
+ .suspend = max77686_suspend,
+ .resume = max77686_resume,
+#ifdef CONFIG_HIBERNATION
+ .freeze = max77686_freeze,
+ .thaw = max77686_restore,
+ .restore = max77686_restore,
+#endif
+};
+
+static struct i2c_driver max77686_i2c_driver = {
+ .driver = {
+ .name = "max77686",
+ .owner = THIS_MODULE,
+ .pm = &max77686_pm,
+ },
+ .probe = max77686_i2c_probe,
+ .remove = max77686_i2c_remove,
+ .id_table = max77686_i2c_id,
+};
+
+static int __init max77686_i2c_init(void)
+{
+ return i2c_add_driver(&max77686_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77686_i2c_init);
+
+static void __exit max77686_i2c_exit(void)
+{
+ i2c_del_driver(&max77686_i2c_driver);
+}
+module_exit(max77686_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c
new file mode 100644
index 0000000..a52fc02
--- /dev/null
+++ b/drivers/mfd/max77693-irq.c
@@ -0,0 +1,349 @@
+/*
+ * max77693-irq.c - Interrupt controller support for MAX77693
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * SangYoung Son <hello.son@samsung.com>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max77693-irq.c
+ */
+
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+
+static const u8 max77693_mask_reg[] = {
+ [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK,
+ [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK,
+ [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK,
+ [MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1,
+ [MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2,
+ [MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3,
+};
+
+static struct i2c_client *get_i2c(struct max77693_dev *max77693,
+ enum max77693_irq_source src)
+{
+ switch (src) {
+ case LED_INT ... CHG_INT:
+ return max77693->i2c;
+ case MUIC_INT1 ... MUIC_INT3:
+ return max77693->muic;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+struct max77693_irq_data {
+ int mask;
+ enum max77693_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask) \
+ [(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77693_irq_data max77693_irqs[] = {
+ DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN, LED_INT, 1 << 0),
+ DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT, LED_INT, 1 << 1),
+ DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN, LED_INT, 1 << 2),
+ DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT, LED_INT, 1 << 3),
+ DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH, LED_INT, 1 << 4),
+
+ DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT, TOPSYS_INT, 1 << 0),
+ DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT, TOPSYS_INT, 1 << 1),
+ DECLARE_IRQ(MAX77693_TOPSYS_IRQLOWSYS_INT, TOPSYS_INT, 1 << 3),
+
+ DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I, CHG_INT, 1 << 0),
+ DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I, CHG_INT, 1 << 2),
+ DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I, CHG_INT, 1 << 3),
+ DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I, CHG_INT, 1 << 4),
+ DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I, CHG_INT, 1 << 6),
+
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC, MUIC_INT1, 1 << 0),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADCLOW, MUIC_INT1, 1 << 1),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADCERR, MUIC_INT1, 1 << 2),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K, MUIC_INT1, 1 << 3),
+
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP, MUIC_INT2, 1 << 0),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN, MUIC_INT2, 1 << 1),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR, MUIC_INT2, 1 << 2),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP, MUIC_INT2, 1 << 3),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT, MUIC_INT2, 1 << 4),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM, MUIC_INT2, 1 << 5),
+
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC, MUIC_INT3, 1 << 0),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC, MUIC_INT3, 1 << 1),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP, MUIC_INT3, 1 << 2),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHGERR, MUIC_INT3, 1 << 3),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHGENABLED, MUIC_INT3, 1 << 4),
+ DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BATDET, MUIC_INT3, 1 << 5),
+};
+
+static void max77693_irq_lock(struct irq_data *data)
+{
+ struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+
+ mutex_lock(&max77693->irqlock);
+}
+
+static void max77693_irq_sync_unlock(struct irq_data *data)
+{
+ struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+ int i;
+
+ for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
+ u8 mask_reg = max77693_mask_reg[i];
+ struct i2c_client *i2c = get_i2c(max77693, i);
+
+ if (mask_reg == MAX77693_REG_INVALID ||
+ IS_ERR_OR_NULL(i2c))
+ continue;
+ max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i];
+
+ max77693_write_reg(i2c, max77693_mask_reg[i],
+ max77693->irq_masks_cur[i]);
+ }
+
+ mutex_unlock(&max77693->irqlock);
+}
+
+static const inline struct max77693_irq_data *
+irq_to_max77693_irq(struct max77693_dev *max77693, int irq)
+{
+ return &max77693_irqs[irq - max77693->irq_base];
+}
+
+static void max77693_irq_mask(struct irq_data *data)
+{
+ struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+ const struct max77693_irq_data *irq_data =
+ irq_to_max77693_irq(max77693, data->irq);
+
+ if (irq_data->group >= MAX77693_IRQ_GROUP_NR)
+ return;
+
+ if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
+ max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+ else
+ max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
+}
+
+static void max77693_irq_unmask(struct irq_data *data)
+{
+ struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
+ const struct max77693_irq_data *irq_data =
+ irq_to_max77693_irq(max77693, data->irq);
+
+ if (irq_data->group >= MAX77693_IRQ_GROUP_NR)
+ return;
+
+ if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
+ max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
+ else
+ max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+}
+
+static struct irq_chip max77693_irq_chip = {
+ .name = "max77693",
+ .irq_bus_lock = max77693_irq_lock,
+ .irq_bus_sync_unlock = max77693_irq_sync_unlock,
+ .irq_mask = max77693_irq_mask,
+ .irq_unmask = max77693_irq_unmask,
+};
+
+static irqreturn_t max77693_irq_thread(int irq, void *data)
+{
+ struct max77693_dev *max77693 = data;
+ u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {};
+ u8 irq_src;
+ int ret;
+ int i;
+ pr_debug("%s: irq gpio pre-state(0x%02x)\n", __func__,
+ gpio_get_value(max77693->irq_gpio));
+
+clear_retry:
+ ret = max77693_read_reg(max77693->i2c, MAX77693_PMIC_REG_INTSRC, &irq_src);
+ if (ret < 0) {
+ dev_err(max77693->dev, "Failed to read interrupt source: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+ pr_info("%s: interrupt source(0x%02x)\n", __func__, irq_src);
+
+ if (irq_src & MAX77693_IRQSRC_CHG) {
+ /* CHG_INT */
+ ret = max77693_read_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT,
+ &irq_reg[CHG_INT]);
+ pr_info("%s: charger interrupt(0x%02x)\n", __func__, irq_reg[CHG_INT]);
+ }
+
+ if (irq_src & MAX77693_IRQSRC_TOP) {
+ /* TOPSYS_INT */
+ ret = max77693_read_reg(max77693->i2c, MAX77693_PMIC_REG_TOPSYS_INT,
+ &irq_reg[TOPSYS_INT]);
+ pr_info("%s: topsys interrupt(0x%02x)\n", __func__, irq_reg[TOPSYS_INT]);
+ }
+
+ if (irq_src & MAX77693_IRQSRC_FLASH) {
+ /* LED_INT */
+ ret = max77693_read_reg(max77693->i2c, MAX77693_LED_REG_FLASH_INT,
+ &irq_reg[LED_INT]);
+ pr_info("%s: led interrupt(0x%02x)\n", __func__, irq_reg[LED_INT]);
+ }
+
+ if (irq_src & MAX77693_IRQSRC_MUIC) {
+ /* MUIC INT1 ~ INT3 */
+ max77693_bulk_read(max77693->muic, MAX77693_MUIC_REG_INT1, MAX77693_NUM_IRQ_MUIC_REGS,
+ &irq_reg[MUIC_INT1]);
+ pr_info("%s: muic interrupt(0x%02x, 0x%02x, 0x%02x)\n", __func__,
+ irq_reg[MUIC_INT1], irq_reg[MUIC_INT2], irq_reg[MUIC_INT3]);
+ }
+
+ pr_debug("%s: irq gpio post-state(0x%02x)\n", __func__,
+ gpio_get_value(max77693->irq_gpio));
+
+ if (gpio_get_value(max77693->irq_gpio) == 0)
+ goto clear_retry;
+
+ /* Apply masking */
+ for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
+ if (i >= MUIC_INT1 && i <= MUIC_INT3)
+ irq_reg[i] &= max77693->irq_masks_cur[i];
+ else
+ irq_reg[i] &= ~max77693->irq_masks_cur[i];
+ }
+
+ /* Report */
+ for (i = 0; i < MAX77693_IRQ_NR; i++) {
+ if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask)
+ handle_nested_irq(max77693->irq_base + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int max77693_irq_resume(struct max77693_dev *max77693)
+{
+ int ret = 0;
+ if (max77693->irq && max77693->irq_base)
+ ret = max77693_irq_thread(max77693->irq_base, max77693);
+
+ dev_info(max77693->dev, "%s: irq_resume ret=%d", __func__, ret);
+
+ return ret >= 0 ? 0 : ret;
+}
+
+int max77693_irq_init(struct max77693_dev *max77693)
+{
+ int i;
+ int cur_irq;
+ int ret;
+ u8 i2c_data;
+
+ if (!max77693->irq_gpio) {
+ dev_warn(max77693->dev, "No interrupt specified.\n");
+ max77693->irq_base = 0;
+ return 0;
+ }
+
+ if (!max77693->irq_base) {
+ dev_err(max77693->dev, "No interrupt base specified.\n");
+ return 0;
+ }
+
+ mutex_init(&max77693->irqlock);
+
+ max77693->irq = gpio_to_irq(max77693->irq_gpio);
+ ret = gpio_request(max77693->irq_gpio, "if_pmic_irq");
+ if (ret) {
+ dev_err(max77693->dev, "%s: failed requesting gpio %d\n",
+ __func__, max77693->irq_gpio);
+ return ret;
+ }
+ gpio_direction_input(max77693->irq_gpio);
+ gpio_free(max77693->irq_gpio);
+
+ /* Mask individual interrupt sources */
+ for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
+ struct i2c_client *i2c;
+ /* MUIC IRQ 0:MASK 1:NOT MASK */
+ /* Other IRQ 1:MASK 0:NOT MASK */
+ if (i >= MUIC_INT1 && i <= MUIC_INT3) {
+ max77693->irq_masks_cur[i] = 0x00;
+ max77693->irq_masks_cache[i] = 0x00;
+ } else {
+ max77693->irq_masks_cur[i] = 0xff;
+ max77693->irq_masks_cache[i] = 0xff;
+ }
+ i2c = get_i2c(max77693, i);
+
+ if (IS_ERR_OR_NULL(i2c))
+ continue;
+ if (max77693_mask_reg[i] == MAX77693_REG_INVALID)
+ continue;
+ if (i >= MUIC_INT1 && i <= MUIC_INT3)
+ max77693_write_reg(i2c, max77693_mask_reg[i], 0x00);
+ else
+ max77693_write_reg(i2c, max77693_mask_reg[i], 0xff);
+ }
+
+ /* Register with genirq */
+ for (i = 0; i < MAX77693_IRQ_NR; i++) {
+ cur_irq = i + max77693->irq_base;
+ irq_set_chip_data(cur_irq, max77693);
+ irq_set_chip_and_handler(cur_irq, &max77693_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ /* Unmask max77693 interrupt */
+ ret = max77693_read_reg(max77693->i2c, MAX77693_PMIC_REG_INTSRC_MASK,
+ &i2c_data);
+ if (ret) {
+ dev_err(max77693->dev, "%s: fail to read muic reg\n", __func__);
+ return ret;
+ }
+
+ i2c_data &= ~(MAX77693_IRQSRC_CHG); /* Unmask charger interrupt */
+ i2c_data &= ~(MAX77693_IRQSRC_MUIC); /* Unmask muic interrupt */
+ max77693_write_reg(max77693->i2c, MAX77693_PMIC_REG_INTSRC_MASK,
+ i2c_data);
+
+ ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max77693-irq", max77693);
+
+ if (ret) {
+ dev_err(max77693->dev, "Failed to request IRQ %d: %d\n",
+ max77693->irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void max77693_irq_exit(struct max77693_dev *max77693)
+{
+ if (max77693->irq)
+ free_irq(max77693->irq, max77693);
+}
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
new file mode 100644
index 0000000..ee13340
--- /dev/null
+++ b/drivers/mfd/max77693.c
@@ -0,0 +1,390 @@
+/*
+ * max77693.c - mfd core driver for the Maxim 77693
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * SangYoung Son <hello.son@smasung.com>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/regulator/machine.h>
+
+#define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */
+#define I2C_ADDR_MUIC (0x4A >> 1)
+#define I2C_ADDR_HAPTIC (0x90 >> 1)
+
+static struct mfd_cell max77693_devs[] = {
+ { .name = "max77693-charger", },
+ { .name = "max77693-led", },
+ { .name = "max77693-muic", },
+ { .name = "max77693-safeout", },
+ { .name = "max77693-haptic", },
+};
+
+int max77693_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77693->iolock);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ mutex_unlock(&max77693->iolock);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+ *dest = ret;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max77693_read_reg);
+
+int max77693_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77693->iolock);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&max77693->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max77693_bulk_read);
+
+int max77693_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
+{
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77693->iolock);
+ ret = i2c_smbus_write_byte_data(i2c, reg, value);
+ mutex_unlock(&max77693->iolock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_write_reg);
+
+int max77693_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77693->iolock);
+ ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&max77693->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max77693_bulk_write);
+
+int max77693_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&max77693->iolock);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ if (ret >= 0) {
+ u8 old_val = ret & 0xff;
+ u8 new_val = (val & mask) | (old_val & (~mask));
+ ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+ }
+ mutex_unlock(&max77693->iolock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(max77693_update_reg);
+
+static int max77693_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max77693_dev *max77693;
+ struct max77693_platform_data *pdata = i2c->dev.platform_data;
+ u8 reg_data;
+ int ret = 0;
+
+ max77693 = kzalloc(sizeof(struct max77693_dev), GFP_KERNEL);
+ if (max77693 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max77693);
+ max77693->dev = &i2c->dev;
+ max77693->i2c = i2c;
+ max77693->irq = i2c->irq;
+ max77693->type = id->driver_data;
+ if (pdata) {
+ max77693->irq_base = pdata->irq_base;
+ max77693->irq_gpio = pdata->irq_gpio;
+ max77693->wakeup = pdata->wakeup;
+ } else
+ goto err;
+
+ mutex_init(&max77693->iolock);
+
+ if (max77693_read_reg(i2c, MAX77693_PMIC_REG_PMIC_ID2, &reg_data) < 0) {
+ dev_err(max77693->dev,
+ "device not found on this channel (this is not an error)\n");
+ ret = -ENODEV;
+ goto err;
+ } else {
+ /* print rev */
+ max77693->pmic_rev = (reg_data & 0x7);
+ max77693->pmic_ver = ((reg_data & 0xF8) >> 0x3);
+ pr_info("%s: device found: rev.0x%x, ver.0x%x\n", __func__,
+ max77693->pmic_rev, max77693->pmic_ver);
+ }
+
+ max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
+ i2c_set_clientdata(max77693->muic, max77693);
+
+ max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
+ i2c_set_clientdata(max77693->haptic, max77693);
+
+ ret = max77693_irq_init(max77693);
+ if (ret < 0)
+ goto err_mfd;
+
+ ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
+ ARRAY_SIZE(max77693_devs), NULL, 0);
+ if (ret < 0)
+ goto err_mfd;
+
+ device_init_wakeup(max77693->dev, pdata->wakeup);
+
+ return ret;
+
+err_mfd:
+ mfd_remove_devices(max77693->dev);
+ i2c_unregister_device(max77693->muic);
+ i2c_unregister_device(max77693->haptic);
+err:
+ kfree(max77693);
+ return ret;
+}
+
+static int max77693_i2c_remove(struct i2c_client *i2c)
+{
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max77693->dev);
+ i2c_unregister_device(max77693->muic);
+ i2c_unregister_device(max77693->haptic);
+ kfree(max77693);
+
+ return 0;
+}
+
+static const struct i2c_device_id max77693_i2c_id[] = {
+ { "max77693", TYPE_MAX77693 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max77693_i2c_id);
+
+#ifdef CONFIG_PM
+static int max77693_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(max77693->irq);
+
+ disable_irq(max77693->irq);
+
+ return 0;
+}
+
+static int max77693_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(max77693->irq);
+
+ enable_irq(max77693->irq);
+
+ return max77693_irq_resume(max77693);
+}
+#else
+#define max77693_suspend NULL
+#define max77693_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_HIBERNATION
+
+u8 max77693_dumpaddr_pmic[] = {
+ MAX77693_LED_REG_IFLASH1,
+ MAX77693_LED_REG_IFLASH2,
+ MAX77693_LED_REG_ITORCH,
+ MAX77693_LED_REG_ITORCHTORCHTIMER,
+ MAX77693_LED_REG_FLASH_TIMER,
+ MAX77693_LED_REG_FLASH_EN,
+ MAX77693_LED_REG_MAX_FLASH1,
+ MAX77693_LED_REG_MAX_FLASH2,
+ MAX77693_LED_REG_VOUT_CNTL,
+ MAX77693_LED_REG_VOUT_FLASH1,
+ MAX77693_LED_REG_FLASH_INT_STATUS,
+
+ MAX77693_PMIC_REG_TOPSYS_INT_MASK,
+ MAX77693_PMIC_REG_MAINCTRL1,
+ MAX77693_PMIC_REG_LSCNFG,
+ MAX77693_CHG_REG_CHG_INT_MASK,
+ MAX77693_CHG_REG_CHG_CNFG_00,
+ MAX77693_CHG_REG_CHG_CNFG_01,
+ MAX77693_CHG_REG_CHG_CNFG_02,
+ MAX77693_CHG_REG_CHG_CNFG_03,
+ MAX77693_CHG_REG_CHG_CNFG_04,
+ MAX77693_CHG_REG_CHG_CNFG_05,
+ MAX77693_CHG_REG_CHG_CNFG_06,
+ MAX77693_CHG_REG_CHG_CNFG_07,
+ MAX77693_CHG_REG_CHG_CNFG_08,
+ MAX77693_CHG_REG_CHG_CNFG_09,
+ MAX77693_CHG_REG_CHG_CNFG_10,
+ MAX77693_CHG_REG_CHG_CNFG_11,
+ MAX77693_CHG_REG_CHG_CNFG_12,
+ MAX77693_CHG_REG_CHG_CNFG_13,
+ MAX77693_CHG_REG_CHG_CNFG_14,
+ MAX77693_CHG_REG_SAFEOUT_CTRL,
+};
+
+u8 max77693_dumpaddr_muic[] = {
+ MAX77693_MUIC_REG_INTMASK1,
+ MAX77693_MUIC_REG_INTMASK2,
+ MAX77693_MUIC_REG_INTMASK3,
+ MAX77693_MUIC_REG_CDETCTRL1,
+ MAX77693_MUIC_REG_CDETCTRL2,
+ MAX77693_MUIC_REG_CTRL1,
+ MAX77693_MUIC_REG_CTRL2,
+ MAX77693_MUIC_REG_CTRL3,
+};
+
+
+u8 max77693_dumpaddr_haptic[] = {
+ MAX77693_HAPTIC_REG_CONFIG1,
+ MAX77693_HAPTIC_REG_CONFIG2,
+ MAX77693_HAPTIC_REG_CONFIG_CHNL,
+ MAX77693_HAPTIC_REG_CONFG_CYC1,
+ MAX77693_HAPTIC_REG_CONFG_CYC2,
+ MAX77693_HAPTIC_REG_CONFIG_PER1,
+ MAX77693_HAPTIC_REG_CONFIG_PER2,
+ MAX77693_HAPTIC_REG_CONFIG_PER3,
+ MAX77693_HAPTIC_REG_CONFIG_PER4,
+ MAX77693_HAPTIC_REG_CONFIG_DUTY1,
+ MAX77693_HAPTIC_REG_CONFIG_DUTY2,
+ MAX77693_HAPTIC_REG_CONFIG_PWM1,
+ MAX77693_HAPTIC_REG_CONFIG_PWM2,
+ MAX77693_HAPTIC_REG_CONFIG_PWM3,
+ MAX77693_HAPTIC_REG_CONFIG_PWM4,
+};
+
+
+static int max77693_freeze(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(max77693_dumpaddr_pmic); i++)
+ max77693_read_reg(i2c, max77693_dumpaddr_pmic[i],
+ &max77693->reg_pmic_dump[i]);
+
+ for (i = 0; i < ARRAY_SIZE(max77693_dumpaddr_muic); i++)
+ max77693_read_reg(i2c, max77693_dumpaddr_muic[i],
+ &max77693->reg_muic_dump[i]);
+
+ for (i = 0; i < ARRAY_SIZE(max77693_dumpaddr_haptic); i++)
+ max77693_read_reg(i2c, max77693_dumpaddr_haptic[i],
+ &max77693->reg_haptic_dump[i]);
+
+ disable_irq(max77693->irq);
+
+ return 0;
+}
+
+static int max77693_restore(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
+ int i;
+
+ enable_irq(max77693->irq);
+
+ for (i = 0; i < ARRAY_SIZE(max77693_dumpaddr_pmic); i++)
+ max77693_write_reg(i2c, max77693_dumpaddr_pmic[i],
+ max77693->reg_pmic_dump[i]);
+
+ for (i = 0; i < ARRAY_SIZE(max77693_dumpaddr_muic); i++)
+ max77693_write_reg(i2c, max77693_dumpaddr_muic[i],
+ max77693->reg_muic_dump[i]);
+
+ for (i = 0; i < ARRAY_SIZE(max77693_dumpaddr_haptic); i++)
+ max77693_write_reg(i2c, max77693_dumpaddr_haptic[i],
+ max77693->reg_haptic_dump[i]);
+
+
+ return 0;
+}
+#endif
+
+
+const struct dev_pm_ops max77693_pm = {
+ .suspend = max77693_suspend,
+ .resume = max77693_resume,
+#ifdef CONFIG_HIBERNATION
+ .freeze = max77693_freeze,
+ .thaw = max77693_restore,
+ .restore = max77693_restore,
+#endif
+};
+
+static struct i2c_driver max77693_i2c_driver = {
+ .driver = {
+ .name = "max77693",
+ .owner = THIS_MODULE,
+ .pm = &max77693_pm,
+ },
+ .probe = max77693_i2c_probe,
+ .remove = max77693_i2c_remove,
+ .id_table = max77693_i2c_id,
+};
+
+static int __init max77693_i2c_init(void)
+{
+ return i2c_add_driver(&max77693_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77693_i2c_init);
+
+static void __exit max77693_i2c_exit(void)
+{
+ i2c_del_driver(&max77693_i2c_driver);
+}
+module_exit(max77693_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver");
+MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max8698.c b/drivers/mfd/max8698.c
new file mode 100644
index 0000000..9dd6004
--- /dev/null
+++ b/drivers/mfd/max8698.c
@@ -0,0 +1,161 @@
+/*
+ * max8698.c - mfd core driver for the Maxim 8698
+ *
+ * Copyright (C) 2009-2010 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * 2010.10.26
+ * Modified by Taekki Kim <taekki.kim@samsung.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8698.h>
+#include <linux/mfd/max8698-private.h>
+
+static struct mfd_cell max8698_devs[] = {
+ {
+ .name = "max8698-pmic",
+ }
+};
+
+static int max8698_i2c_device_read(struct max8698_dev *max8698, u8 reg, u8 *dest)
+{
+ struct i2c_client *client = max8698->i2c_client;
+ int ret;
+
+ mutex_lock(&max8698->iolock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ mutex_unlock(&max8698->iolock);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+ *dest = ret;
+ return 0;
+}
+
+static int max8698_i2c_device_write(struct max8698_dev *max8698, u8 reg, u8 value)
+{
+ struct i2c_client *client = max8698->i2c_client;
+ int ret;
+
+ mutex_lock(&max8698->iolock);
+ ret = i2c_smbus_write_byte_data(client, reg, value);
+ mutex_unlock(&max8698->iolock);
+ return ret;
+}
+
+static int max8698_i2c_device_update(struct max8698_dev *max8698, u8 reg,
+ u8 val, u8 mask)
+{
+ struct i2c_client *client = max8698->i2c_client;
+ int ret;
+
+ mutex_lock(&max8698->iolock);
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+ u8 old_val = ret & 0xff;
+ u8 new_val = (val & mask) | (old_val & (~mask));
+ ret = i2c_smbus_write_byte_data(client, reg, new_val);
+ if (ret >= 0)
+ ret = 0;
+ }
+ mutex_unlock(&max8698->iolock);
+ return ret;
+}
+
+static int max8698_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max8698_dev *max8698;
+ int ret = 0;
+
+ max8698 = kzalloc(sizeof(struct max8698_dev), GFP_KERNEL);
+ if (max8698 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max8698);
+ max8698->dev = &i2c->dev;
+ max8698->i2c_client = i2c;
+ max8698->dev_read = max8698_i2c_device_read;
+ max8698->dev_write = max8698_i2c_device_write;
+ max8698->dev_update = max8698_i2c_device_update;
+ mutex_init(&max8698->iolock);
+
+ ret = mfd_add_devices(max8698->dev, -1,
+ max8698_devs, ARRAY_SIZE(max8698_devs),
+ NULL, 0);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ mfd_remove_devices(max8698->dev);
+ kfree(max8698);
+ return ret;
+}
+
+static int max8698_i2c_remove(struct i2c_client *i2c)
+{
+ struct max8698_dev *max8698 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max8698->dev);
+ kfree(max8698);
+
+ return 0;
+}
+
+static const struct i2c_device_id max8698_i2c_id[] = {
+ { "max8698", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max8698_i2c_id);
+
+static struct i2c_driver max8698_i2c_driver = {
+ .driver = {
+ .name = "max8698",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8698_i2c_probe,
+ .remove = max8698_i2c_remove,
+ .id_table = max8698_i2c_id,
+};
+
+static int __init max8698_i2c_init(void)
+{
+ return i2c_add_driver(&max8698_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max8698_i2c_init);
+
+static void __exit max8698_i2c_exit(void)
+{
+ i2c_del_driver(&max8698_i2c_driver);
+}
+module_exit(max8698_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 8698 multi-function core driver");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c
index 638bf7e..399ba92 100644
--- a/drivers/mfd/max8997-irq.c
+++ b/drivers/mfd/max8997-irq.c
@@ -1,118 +1,187 @@
/*
- * max8997-irq.c - Interrupt controller support for MAX8997
+ * Interrupt controller support for MAX8997
*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * MyungJoo Ham <myungjoo.ham@samsung.com>
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
*
- * 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.
+ * based on max8998-irq.c
*
- * This program is distributed in the hope that 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.
+ * <ms925.kim@samsung.com>
*
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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 driver is based on max8998-irq.c
*/
-#include <linux/err.h>
-#include <linux/irq.h>
+#include <linux/device.h>
#include <linux/interrupt.h>
-#include <linux/mfd/max8997.h>
+#include <linux/irq.h>
#include <linux/mfd/max8997-private.h>
+#include <linux/delay.h>
-static const u8 max8997_mask_reg[] = {
- [PMIC_INT1] = MAX8997_REG_INT1MSK,
- [PMIC_INT2] = MAX8997_REG_INT2MSK,
- [PMIC_INT3] = MAX8997_REG_INT3MSK,
- [PMIC_INT4] = MAX8997_REG_INT4MSK,
- [FUEL_GAUGE] = MAX8997_REG_INVALID,
- [MUIC_INT1] = MAX8997_MUIC_REG_INTMASK1,
- [MUIC_INT2] = MAX8997_MUIC_REG_INTMASK2,
- [MUIC_INT3] = MAX8997_MUIC_REG_INTMASK3,
- [GPIO_LOW] = MAX8997_REG_INVALID,
- [GPIO_HI] = MAX8997_REG_INVALID,
- [FLASH_STATUS] = MAX8997_REG_INVALID,
-};
+extern struct class *sec_class;
-static struct i2c_client *get_i2c(struct max8997_dev *max8997,
- enum max8997_irq_source src)
-{
- switch (src) {
- case PMIC_INT1 ... PMIC_INT4:
- return max8997->i2c;
- case FUEL_GAUGE:
- return NULL;
- case MUIC_INT1 ... MUIC_INT3:
- return max8997->muic;
- case GPIO_LOW ... GPIO_HI:
- return max8997->i2c;
- case FLASH_STATUS:
- return max8997->i2c;
- default:
- return ERR_PTR(-EINVAL);
- }
+#define FLED1_FLT_BIT (1<<0)
+#define FLED2_FLT_BIT (1<<1)
+#define FLED_FLT_MASK (FLED1_FLT_BIT | FLED2_FLT_BIT)
- return ERR_PTR(-EINVAL);
-}
+static u8 fled_status;
struct max8997_irq_data {
+ int reg;
int mask;
- enum max8997_irq_source group;
};
-#define DECLARE_IRQ(idx, _group, _mask) \
- [(idx)] = { .group = (_group), .mask = (_mask) }
-static const struct max8997_irq_data max8997_irqs[] = {
- DECLARE_IRQ(MAX8997_PMICIRQ_PWRONR, PMIC_INT1, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_PWRONF, PMIC_INT1, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_PWRON1SEC, PMIC_INT1, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGONR, PMIC_INT1, 1 << 4),
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGONF, PMIC_INT1, 1 << 5),
- DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT2, PMIC_INT1, 1 << 6),
- DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT1, PMIC_INT1, 1 << 7),
-
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGR, PMIC_INT2, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_JIGF, PMIC_INT2, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_MR, PMIC_INT2, 1 << 2),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS1OK, PMIC_INT2, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS2OK, PMIC_INT2, 1 << 4),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS3OK, PMIC_INT2, 1 << 5),
- DECLARE_IRQ(MAX8997_PMICIRQ_DVS4OK, PMIC_INT2, 1 << 6),
-
- DECLARE_IRQ(MAX8997_PMICIRQ_CHGINS, PMIC_INT3, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_CHGRM, PMIC_INT3, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_DCINOVP, PMIC_INT3, 1 << 2),
- DECLARE_IRQ(MAX8997_PMICIRQ_TOPOFFR, PMIC_INT3, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_CHGRSTF, PMIC_INT3, 1 << 5),
- DECLARE_IRQ(MAX8997_PMICIRQ_MBCHGTMEXPD, PMIC_INT3, 1 << 7),
-
- DECLARE_IRQ(MAX8997_PMICIRQ_RTC60S, PMIC_INT4, 1 << 0),
- DECLARE_IRQ(MAX8997_PMICIRQ_RTCA1, PMIC_INT4, 1 << 1),
- DECLARE_IRQ(MAX8997_PMICIRQ_RTCA2, PMIC_INT4, 1 << 2),
- DECLARE_IRQ(MAX8997_PMICIRQ_SMPL_INT, PMIC_INT4, 1 << 3),
- DECLARE_IRQ(MAX8997_PMICIRQ_RTC1S, PMIC_INT4, 1 << 4),
- DECLARE_IRQ(MAX8997_PMICIRQ_WTSR, PMIC_INT4, 1 << 5),
-
- DECLARE_IRQ(MAX8997_MUICIRQ_ADCError, MUIC_INT1, 1 << 2),
- DECLARE_IRQ(MAX8997_MUICIRQ_ADCLow, MUIC_INT1, 1 << 1),
- DECLARE_IRQ(MAX8997_MUICIRQ_ADC, MUIC_INT1, 1 << 0),
-
- DECLARE_IRQ(MAX8997_MUICIRQ_VBVolt, MUIC_INT2, 1 << 4),
- DECLARE_IRQ(MAX8997_MUICIRQ_DBChg, MUIC_INT2, 1 << 3),
- DECLARE_IRQ(MAX8997_MUICIRQ_DCDTmr, MUIC_INT2, 1 << 2),
- DECLARE_IRQ(MAX8997_MUICIRQ_ChgDetRun, MUIC_INT2, 1 << 1),
- DECLARE_IRQ(MAX8997_MUICIRQ_ChgTyp, MUIC_INT2, 1 << 0),
-
- DECLARE_IRQ(MAX8997_MUICIRQ_OVP, MUIC_INT3, 1 << 2),
+static struct max8997_irq_data max8997_irqs[] = {
+ [MAX8997_IRQ_PWRONR] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_PWRONR_MASK,
+ },
+ [MAX8997_IRQ_PWRONF] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_PWRONF_MASK,
+ },
+ [MAX8997_IRQ_PWRON1SEC] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_PWRON1SEC_MASK,
+ },
+ [MAX8997_IRQ_JIGONR] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_JIGONR_MASK,
+ },
+ [MAX8997_IRQ_JIGONF] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_JIGONF_MASK,
+ },
+ [MAX8997_IRQ_LOWBAT2] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_LOWBAT2_MASK,
+ },
+ [MAX8997_IRQ_LOWBAT1] = {
+ .reg = 1,
+ .mask = MAX8997_IRQ_LOWBAT1_MASK,
+ },
+ [MAX8997_IRQ_JIGR] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_JIGR_MASK,
+ },
+ [MAX8997_IRQ_JIGF] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_JIGF_MASK,
+ },
+ [MAX8997_IRQ_MR] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_MR_MASK,
+ },
+ [MAX8997_IRQ_DVS1OK] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_DVS1OK_MASK,
+ },
+ [MAX8997_IRQ_DVS2OK] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_DVS2OK_MASK,
+ },
+ [MAX8997_IRQ_DVS4OK] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_DVS4OK_MASK,
+ },
+ [MAX8997_IRQ_DVS5OK] = {
+ .reg = 2,
+ .mask = MAX8997_IRQ_DVS5OK_MASK,
+ },
+ [MAX8997_IRQ_CHGINS] = {
+ .reg = 3,
+ .mask = MAX8997_IRQ_CHGINS_MASK,
+ },
+ [MAX8997_IRQ_CHGRM] = {
+ .reg = 3,
+ .mask = MAX8997_IRQ_CHGRM_MASK,
+ },
+ [MAX8997_IRQ_DCINOVP] = {
+ .reg = 3,
+ .mask = MAX8997_IRQ_DCINOVP_MASK,
+ },
+ [MAX8997_IRQ_TOPOFF] = {
+ .reg = 3,
+ .mask = MAX8997_IRQ_TOPOFF_MASK,
+ },
+ [MAX8997_IRQ_CHGRSTF] = {
+ .reg = 3,
+ .mask = MAX8997_IRQ_CHGRSTF_MASK,
+ },
+ [MAX8997_IRQ_MBCHGTMEXPD] = {
+ .reg = 3,
+ .mask = MAX8997_IRQ_MBCHGTMEXPD_MASK,
+ },
+ [MAX8997_IRQ_RTC60S] = {
+ .reg = 4,
+ .mask = MAX8997_IRQ_RTC60S_MASK,
+ },
+ [MAX8997_IRQ_RTCA1] = {
+ .reg = 4,
+ .mask = MAX8997_IRQ_RTCA1_MASK,
+ },
+ [MAX8997_IRQ_RTCA2] = {
+ .reg = 4,
+ .mask = MAX8997_IRQ_RTCA2_MASK,
+ },
+ [MAX8997_IRQ_SMPL_INT] = {
+ .reg = 4,
+ .mask = MAX8997_IRQ_SMPL_INT_MASK,
+ },
+ [MAX8997_IRQ_RTC1S] = {
+ .reg = 4,
+ .mask = MAX8997_IRQ_RTC1S_MASK,
+ },
+ [MAX8997_IRQ_WTSR] = {
+ .reg = 4,
+ .mask = MAX8997_IRQ_WTSR_MASK,
+ },
+ [MAX8997_IRQ_ADC] = {
+ .reg = 5,
+ .mask = MAX8997_IRQ_ADC_MASK,
+ },
+ [MAX8997_IRQ_ADCLOW] = {
+ .reg = 5,
+ .mask = MAX8997_IRQ_ADCLOW_MASK,
+ },
+ [MAX8997_IRQ_ADCERR] = {
+ .reg = 5,
+ .mask = MAX8997_IRQ_ADCERR_MASK,
+ },
+ [MAX8997_IRQ_CHGTYP] = {
+ .reg = 6,
+ .mask = MAX8997_IRQ_CHGTYP_MASK,
+ },
+ [MAX8997_IRQ_CHGDETRUN] = {
+ .reg = 6,
+ .mask = MAX8997_IRQ_CHGDETRUN_MASK,
+ },
+ [MAX8997_IRQ_DCDTMR] = {
+ .reg = 6,
+ .mask = MAX8997_IRQ_DCDTMR_MASK,
+ },
+ [MAX8997_IRQ_DBCHG] = {
+ .reg = 6,
+ .mask = MAX8997_IRQ_DCDTMR_MASK,
+ },
+ [MAX8997_IRQ_VBVOLT] = {
+ .reg = 6,
+ .mask = MAX8997_IRQ_VBVOLT_MASK,
+ },
+ [MAX8997_IRQ_OVP] = {
+ .reg = 7,
+ .mask = MAX8997_IRQ_OVP_MASK,
+ },
};
+static inline struct max8997_irq_data *
+irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
+{
+ return &max8997_irqs[irq - max8997->irq_base];
+}
+
static void max8997_irq_lock(struct irq_data *data)
{
struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
@@ -125,216 +194,221 @@ static void max8997_irq_sync_unlock(struct irq_data *data)
struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
int i;
- for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
- u8 mask_reg = max8997_mask_reg[i];
- struct i2c_client *i2c = get_i2c(max8997, i);
-
- if (mask_reg == MAX8997_REG_INVALID ||
- IS_ERR_OR_NULL(i2c))
- continue;
- max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
-
- max8997_write_reg(i2c, max8997_mask_reg[i],
- max8997->irq_masks_cur[i]);
+ for (i = 0; i < ARRAY_SIZE(max8997->irq_masks_cur); i++) {
+ /*
+ * If there's been a change in the mask write it back
+ * to the hardware.
+ */
+ if (max8997->irq_masks_cur[i] != max8997->irq_masks_cache[i]) {
+ max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i];
+ if (i < MAX8997_NUM_IRQ_PMIC_REGS)
+ max8997_write_reg(max8997->i2c,
+ MAX8997_REG_IRQM1 + i,
+ max8997->irq_masks_cur[i]);
+ else
+ max8997_write_reg(max8997->muic,
+ MAX8997_MUIC_REG_INTMASK1 + i
+ - MAX8997_NUM_IRQ_PMIC_REGS,
+ ~max8997->irq_masks_cur[i]);
+ }
}
mutex_unlock(&max8997->irqlock);
}
-static const inline struct max8997_irq_data *
-irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
-{
- return &max8997_irqs[irq - max8997->irq_base];
-}
-
-static void max8997_irq_mask(struct irq_data *data)
+static void max8997_irq_unmask(struct irq_data *data)
{
struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
- const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
- data->irq);
+ struct max8997_irq_data *irq_data
+ = irq_to_max8997_irq(max8997, data->irq);
- max8997->irq_masks_cur[irq_data->group] |= irq_data->mask;
+ max8997->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
}
-static void max8997_irq_unmask(struct irq_data *data)
+static void max8997_irq_mask(struct irq_data *data)
{
struct max8997_dev *max8997 = irq_get_chip_data(data->irq);
- const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997,
- data->irq);
+ struct max8997_irq_data *irq_data
+ = irq_to_max8997_irq(max8997, data->irq);
- max8997->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+ max8997->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
}
static struct irq_chip max8997_irq_chip = {
- .name = "max8997",
- .irq_bus_lock = max8997_irq_lock,
- .irq_bus_sync_unlock = max8997_irq_sync_unlock,
- .irq_mask = max8997_irq_mask,
- .irq_unmask = max8997_irq_unmask,
+ .name = "max8997",
+ .irq_bus_lock = max8997_irq_lock,
+ .irq_bus_sync_unlock = max8997_irq_sync_unlock,
+ .irq_mask = max8997_irq_mask,
+ .irq_unmask = max8997_irq_unmask,
};
-#define MAX8997_IRQSRC_PMIC (1 << 1)
-#define MAX8997_IRQSRC_FUELGAUGE (1 << 2)
-#define MAX8997_IRQSRC_MUIC (1 << 3)
-#define MAX8997_IRQSRC_GPIO (1 << 4)
-#define MAX8997_IRQSRC_FLASH (1 << 5)
static irqreturn_t max8997_irq_thread(int irq, void *data)
{
struct max8997_dev *max8997 = data;
- u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
u8 irq_src;
+ u8 irq_flash;
+ u8 irq_reg[MAX8997_NUM_IRQ_REGS];
int ret;
int i;
- ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
+ ret = max8997_read_reg(max8997->i2c, MAX8997_REG_IRQ_SOURCE, &irq_src);
+
if (ret < 0) {
- dev_err(max8997->dev, "Failed to read interrupt source: %d\n",
- ret);
+ dev_err(max8997->dev, "Failed to read interrupt src"
+ " register: %d\n", ret);
return IRQ_NONE;
}
- if (irq_src & MAX8997_IRQSRC_PMIC) {
- /* PMIC INT1 ~ INT4 */
- max8997_bulk_read(max8997->i2c, MAX8997_REG_INT1, 4,
- &irq_reg[PMIC_INT1]);
- }
- if (irq_src & MAX8997_IRQSRC_FUELGAUGE) {
- /*
- * TODO: FUEL GAUGE
- *
- * This is to be supported by Max17042 driver. When
- * an interrupt incurs here, it should be relayed to a
- * Max17042 device that is connected (probably by
- * platform-data). However, we do not have interrupt
- * handling in Max17042 driver currently. The Max17042 IRQ
- * driver should be ready to be used as a stand-alone device and
- * a Max8997-dependent device. Because it is not ready in
- * Max17042-side and it is not too critical in operating
- * Max8997, we do not implement this in initial releases.
- */
- irq_reg[FUEL_GAUGE] = 0;
- }
- if (irq_src & MAX8997_IRQSRC_MUIC) {
- /* MUIC INT1 ~ INT3 */
- max8997_bulk_read(max8997->muic, MAX8997_MUIC_REG_INT1, 3,
- &irq_reg[MUIC_INT1]);
- }
- if (irq_src & MAX8997_IRQSRC_GPIO) {
- /* GPIO Interrupt */
- u8 gpio_info[MAX8997_NUM_GPIO];
-
- irq_reg[GPIO_LOW] = 0;
- irq_reg[GPIO_HI] = 0;
-
- max8997_bulk_read(max8997->i2c, MAX8997_REG_GPIOCNTL1,
- MAX8997_NUM_GPIO, gpio_info);
- for (i = 0; i < MAX8997_NUM_GPIO; i++) {
- bool interrupt = false;
-
- switch (gpio_info[i] & MAX8997_GPIO_INT_MASK) {
- case MAX8997_GPIO_INT_BOTH:
- if (max8997->gpio_status[i] != gpio_info[i])
- interrupt = true;
- break;
- case MAX8997_GPIO_INT_RISE:
- if ((max8997->gpio_status[i] != gpio_info[i]) &&
- (gpio_info[i] & MAX8997_GPIO_DATA_MASK))
- interrupt = true;
- break;
- case MAX8997_GPIO_INT_FALL:
- if ((max8997->gpio_status[i] != gpio_info[i]) &&
- !(gpio_info[i] & MAX8997_GPIO_DATA_MASK))
- interrupt = true;
- break;
- default:
- break;
- }
-
- if (interrupt) {
- if (i < 8)
- irq_reg[GPIO_LOW] |= (1 << i);
- else
- irq_reg[GPIO_HI] |= (1 << (i - 8));
- }
-
+ dev_info(max8997->dev, "%s: irq:%d, irq_src:0x%x\n", __func__,
+ irq, irq_src);
+
+
+ for (i = 0; i < MAX8997_NUM_IRQ_REGS; i++)
+ irq_reg[i] = 0x0;
+
+ if (irq_src & MAX8997_INTR_PMIC_MASK)
+ ret = max8997_bulk_read(max8997->i2c, MAX8997_REG_IRQ1,
+ MAX8997_NUM_IRQ_PMIC_REGS, irq_reg);
+ else if (irq_src & MAX8997_INTR_MUIC_MASK)
+ ret = max8997_bulk_read(max8997->muic, MAX8997_MUIC_REG_INT1,
+ MAX8997_NUM_IRQ_MUIC_REGS,
+ &irq_reg[MAX8997_NUM_IRQ_PMIC_REGS]);
+ else if (irq_src & MAX8997_INTR_FLASH_MASK) {
+ ret = max8997_read_reg(max8997->i2c, MAX8997_REG_FLASH_STATUS,
+ &irq_flash);
+ if (ret < 0)
+ dev_err(max8997->dev, "%s: fail to read flash"
+ " status: %d\n", __func__, ret);
+ else {
+ dev_info(max8997->dev, "%s: FLASH Interrupt: 0x%02x\n",
+ __func__, irq_flash);
+ fled_status = irq_flash & FLED_FLT_MASK;
}
+ } else if (irq_src & MAX8997_INTR_FUELGAUGE_MASK) {
+ dev_warn(max8997->dev, "%s: Fuel Gauge interrupt\n", __func__);
+ msleep(20);
+ } else {
+ dev_warn(max8997->dev, "Unused interrupt source: 0x%x\n",
+ irq_src);
+ return IRQ_NONE;
}
- if (irq_src & MAX8997_IRQSRC_FLASH) {
- /* Flash Status Interrupt */
- ret = max8997_read_reg(max8997->i2c, MAX8997_REG_FLASHSTATUS,
- &irq_reg[FLASH_STATUS]);
+
+ if (ret < 0) {
+ dev_err(max8997->dev, "Failed to read interrupt register: %d\n",
+ ret);
+ return IRQ_NONE;
}
/* Apply masking */
- for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++)
+ for (i = 0; i < MAX8997_NUM_IRQ_REGS; i++)
irq_reg[i] &= ~max8997->irq_masks_cur[i];
/* Report */
for (i = 0; i < MAX8997_IRQ_NR; i++) {
- if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask)
+ if (irq_reg[max8997_irqs[i].reg - 1] & max8997_irqs[i].mask)
handle_nested_irq(max8997->irq_base + i);
}
return IRQ_HANDLED;
}
-int max8997_irq_resume(struct max8997_dev *max8997)
+static ssize_t max8997_show_fled_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- if (max8997->irq && max8997->irq_base)
- max8997_irq_thread(max8997->irq_base, max8997);
- return 0;
+ if (fled_status & FLED1_FLT_BIT)
+ sprintf(buf, "LED1 FAULT\n");
+ else if (fled_status & FLED2_FLT_BIT)
+ sprintf(buf, "LED2 FAULT\n");
+ else
+ sprintf(buf, "LED OK\n");
+
+ pr_info("%s: %s\n", __func__, buf);
+
+ return strlen(buf);
}
+static DEVICE_ATTR(fled_state, 0444, max8997_show_fled_state, NULL);
+
int max8997_irq_init(struct max8997_dev *max8997)
{
int i;
int cur_irq;
int ret;
- u8 val;
+ u8 pmic_intm[MAX8997_NUM_IRQ_PMIC_REGS];
+ u8 muic_intm[MAX8997_NUM_IRQ_MUIC_REGS];
+ u8 pmic_id;
+ struct device *fled_dev;
if (!max8997->irq) {
- dev_warn(max8997->dev, "No interrupt specified.\n");
+ dev_warn(max8997->dev,
+ "No interrupt specified, no interrupts\n");
max8997->irq_base = 0;
return 0;
}
if (!max8997->irq_base) {
- dev_err(max8997->dev, "No interrupt base specified.\n");
+ dev_err(max8997->dev,
+ "No interrupt base specified, no interrupts\n");
return 0;
}
- mutex_init(&max8997->irqlock);
+ for (i = 0; i < MAX8997_NUM_IRQ_PMIC_REGS; i++)
+ pmic_intm[i] = 0xff;
+
+ for (i = 0; i < MAX8997_NUM_IRQ_MUIC_REGS; i++)
+ muic_intm[i] = 0x00;
- /* Mask individual interrupt sources */
- for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) {
- struct i2c_client *i2c;
+ mutex_init(&max8997->irqlock);
+ ret = max8997_read_reg(max8997->i2c, MAX8997_REG_ID, &pmic_id);
+ if (ret)
+ dev_err(max8997->dev, "%s: fail to read PMIC ID(0x%X)\n",
+ __func__, ret);
+ else
+ dev_info(max8997->dev, "%s: PMIC ID(0x%X)\n",
+ __func__, pmic_id);
+
+ /* Mask the individual interrupt sources */
+ for (i = 0; i < MAX8997_NUM_IRQ_REGS; i++) {
max8997->irq_masks_cur[i] = 0xff;
max8997->irq_masks_cache[i] = 0xff;
- i2c = get_i2c(max8997, i);
+ }
- if (IS_ERR_OR_NULL(i2c))
- continue;
- if (max8997_mask_reg[i] == MAX8997_REG_INVALID)
- continue;
+ ret = max8997_bulk_write(max8997->i2c, MAX8997_REG_IRQM1,
+ MAX8997_NUM_IRQ_PMIC_REGS, pmic_intm);
+ if (ret)
+ dev_err(max8997->dev, "%s: fail to write PMIC IRQM(%d)\n",
+ __func__, ret);
- max8997_write_reg(i2c, max8997_mask_reg[i], 0xff);
- }
+ ret = max8997_bulk_write(max8997->muic, MAX8997_MUIC_REG_INTMASK1,
+ MAX8997_NUM_IRQ_MUIC_REGS, muic_intm);
+ if (ret)
+ dev_err(max8997->dev, "%s: fail to write MUIC IRQM(%d)\n",
+ __func__, ret);
+
+ /* Enable flash interrupts to debug flash over current issues
+ * refer MAX8997 I2C Registers: FLASH STATUS control register(0x6D)
+ * 0x01: FLED1_FLT : FLED1 status read back
+ * 0x02: FLED2_FLT : FLED2 status read back
+ */
+ ret = max8997_write_reg(max8997->i2c, MAX8997_REG_FLASH_STATUS_MASK,
+ 0xfc);
+ if (ret)
+ dev_err(max8997->dev, "%s: fail to write FLASH IRQM(%d)\n",
+ __func__, ret);
- for (i = 0; i < MAX8997_NUM_GPIO; i++) {
- max8997->gpio_status[i] = (max8997_read_reg(max8997->i2c,
- MAX8997_REG_GPIOCNTL1 + i,
- &val)
- & MAX8997_GPIO_DATA_MASK) ?
- true : false;
- }
+ fled_dev = device_create(sec_class, NULL, 0, NULL, "sec_fled");
+ ret = device_create_file(fled_dev, &dev_attr_fled_state);
+ if (ret)
+ dev_err(fled_dev, "failed to create device file(fled_state)\n");
- /* Register with genirq */
+ /* register with genirq */
for (i = 0; i < MAX8997_IRQ_NR; i++) {
cur_irq = i + max8997->irq_base;
irq_set_chip_data(cur_irq, max8997);
irq_set_chip_and_handler(cur_irq, &max8997_irq_chip,
- handle_edge_irq);
+ handle_level_irq);
irq_set_nested_thread(cur_irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(cur_irq, IRQF_VALID);
@@ -344,25 +418,27 @@ int max8997_irq_init(struct max8997_dev *max8997)
}
ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "max8997-irq", max8997);
-
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "max8997-irq", max8997);
if (ret) {
dev_err(max8997->dev, "Failed to request IRQ %d: %d\n",
- max8997->irq, ret);
+ max8997->irq, ret);
return ret;
}
if (!max8997->ono)
return 0;
+ dev_dbg(max8997->dev, "%s: ono irq request\n", __func__);
ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
- IRQF_ONESHOT, "max8997-ono", max8997);
-
- if (ret)
- dev_err(max8997->dev, "Failed to request ono-IRQ %d: %d\n",
- max8997->ono, ret);
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "max8997-ono", max8997);
+ if (ret) {
+ dev_err(max8997->dev, "Failed to request IRQ %d: %d\n",
+ max8997->ono, ret);
+ return ret;
+ }
+ enable_irq_wake(max8997->ono);
return 0;
}
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 5d1fca0..55d6e8d 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -1,8 +1,9 @@
/*
- * max8997.c - mfd core driver for the Maxim 8966 and 8997
+ * max8997.c - mfd core driver for the Maxim 8997
*
- * Copyright (C) 2011 Samsung Electronics
- * MyungJoo Ham <myungjoo.ham@smasung.com>
+ * Copyright (C) 2009-2010 Samsung Electronics
+ *
+ * based on max8998.c
*
* 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
@@ -17,31 +18,35 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * This driver is based on max8998.c
*/
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
-#define I2C_ADDR_PMIC (0xCC >> 1)
-#define I2C_ADDR_MUIC (0x4A >> 1)
-#define I2C_ADDR_BATTERY (0x6C >> 1)
-#define I2C_ADDR_RTC (0x0C >> 1)
-#define I2C_ADDR_HAPTIC (0x90 >> 1)
+#define RTC_I2C_ADDR (0x0c >> 1)
+#define MUIC_I2C_ADDR (0x4a >> 1)
+#define HMOTOR_I2C_ADDR (0x90 >> 1)
static struct mfd_cell max8997_devs[] = {
- { .name = "max8997-pmic", },
- { .name = "max8997-rtc", },
- { .name = "max8997-battery", },
- { .name = "max8997-haptic", },
- { .name = "max8997-muic", },
- { .name = "max8997-flash", },
+ {
+ .name = "max8997-pmic",
+ }, {
+ .name = "max8997-charger",
+ }, {
+ .name = "max8997-rtc",
+ }, {
+ .name = "max8997-muic",
+ }, {
+ .name = "max8997-hapticmotor",
+ },
};
int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
@@ -59,7 +64,7 @@ int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
*dest = ret;
return 0;
}
-EXPORT_SYMBOL_GPL(max8997_read_reg);
+EXPORT_SYMBOL(max8997_read_reg);
int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
@@ -74,7 +79,7 @@ int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
return 0;
}
-EXPORT_SYMBOL_GPL(max8997_bulk_read);
+EXPORT_SYMBOL(max8997_bulk_read);
int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
{
@@ -86,7 +91,7 @@ int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value)
mutex_unlock(&max8997->iolock);
return ret;
}
-EXPORT_SYMBOL_GPL(max8997_write_reg);
+EXPORT_SYMBOL(max8997_write_reg);
int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
{
@@ -101,7 +106,7 @@ int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
return 0;
}
-EXPORT_SYMBOL_GPL(max8997_bulk_write);
+EXPORT_SYMBOL(max8997_bulk_write);
int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
{
@@ -118,13 +123,13 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
mutex_unlock(&max8997->iolock);
return ret;
}
-EXPORT_SYMBOL_GPL(max8997_update_reg);
+EXPORT_SYMBOL(max8997_update_reg);
static int max8997_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct max8997_dev *max8997;
struct max8997_platform_data *pdata = i2c->dev.platform_data;
+ struct max8997_dev *max8997;
int ret = 0;
max8997 = kzalloc(sizeof(struct max8997_dev), GFP_KERNEL);
@@ -134,44 +139,39 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max8997);
max8997->dev = &i2c->dev;
max8997->i2c = i2c;
+ max8997->irq = i2c->irq;
max8997->type = id->driver_data;
-
- if (!pdata)
- goto err;
-
- max8997->wakeup = pdata->wakeup;
-
+ if (pdata) {
+ max8997->ono = pdata->ono;
+ max8997->irq_base = pdata->irq_base;
+ max8997->wakeup = pdata->wakeup;
+ }
mutex_init(&max8997->iolock);
- max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
+ max8997->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
i2c_set_clientdata(max8997->rtc, max8997);
- max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
- i2c_set_clientdata(max8997->haptic, max8997);
- max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
- i2c_set_clientdata(max8997->muic, max8997);
- pm_runtime_set_active(max8997->dev);
+ max8997->muic = i2c_new_dummy(i2c->adapter, MUIC_I2C_ADDR);
+ i2c_set_clientdata(max8997->muic, max8997);
- mfd_add_devices(max8997->dev, -1, max8997_devs,
- ARRAY_SIZE(max8997_devs),
- NULL, 0);
+ max8997->hmotor = i2c_new_dummy(i2c->adapter, HMOTOR_I2C_ADDR);
+ i2c_set_clientdata(max8997->hmotor, max8997);
- /*
- * TODO: enable others (flash, muic, rtc, battery, ...) and
- * check the return value
- */
+ max8997_irq_init(max8997);
+ ret = mfd_add_devices(max8997->dev, -1,
+ max8997_devs, ARRAY_SIZE(max8997_devs),
+ NULL, 0);
if (ret < 0)
- goto err_mfd;
+ goto err;
+
return ret;
-err_mfd:
+err:
mfd_remove_devices(max8997->dev);
- i2c_unregister_device(max8997->muic);
- i2c_unregister_device(max8997->haptic);
+ max8997_irq_exit(max8997);
i2c_unregister_device(max8997->rtc);
-err:
kfree(max8997);
return ret;
}
@@ -181,8 +181,7 @@ static int max8997_i2c_remove(struct i2c_client *i2c)
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
mfd_remove_devices(max8997->dev);
- i2c_unregister_device(max8997->muic);
- i2c_unregister_device(max8997->haptic);
+ max8997_irq_exit(max8997);
i2c_unregister_device(max8997->rtc);
kfree(max8997);
@@ -191,211 +190,44 @@ static int max8997_i2c_remove(struct i2c_client *i2c)
static const struct i2c_device_id max8997_i2c_id[] = {
{ "max8997", TYPE_MAX8997 },
- { "max8966", TYPE_MAX8966 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
-
-u8 max8997_dumpaddr_pmic[] = {
- MAX8997_REG_INT1MSK,
- MAX8997_REG_INT2MSK,
- MAX8997_REG_INT3MSK,
- MAX8997_REG_INT4MSK,
- MAX8997_REG_MAINCON1,
- MAX8997_REG_MAINCON2,
- MAX8997_REG_BUCKRAMP,
- MAX8997_REG_BUCK1CTRL,
- MAX8997_REG_BUCK1DVS1,
- MAX8997_REG_BUCK1DVS2,
- MAX8997_REG_BUCK1DVS3,
- MAX8997_REG_BUCK1DVS4,
- MAX8997_REG_BUCK1DVS5,
- MAX8997_REG_BUCK1DVS6,
- MAX8997_REG_BUCK1DVS7,
- MAX8997_REG_BUCK1DVS8,
- MAX8997_REG_BUCK2CTRL,
- MAX8997_REG_BUCK2DVS1,
- MAX8997_REG_BUCK2DVS2,
- MAX8997_REG_BUCK2DVS3,
- MAX8997_REG_BUCK2DVS4,
- MAX8997_REG_BUCK2DVS5,
- MAX8997_REG_BUCK2DVS6,
- MAX8997_REG_BUCK2DVS7,
- MAX8997_REG_BUCK2DVS8,
- MAX8997_REG_BUCK3CTRL,
- MAX8997_REG_BUCK3DVS,
- MAX8997_REG_BUCK4CTRL,
- MAX8997_REG_BUCK4DVS,
- MAX8997_REG_BUCK5CTRL,
- MAX8997_REG_BUCK5DVS1,
- MAX8997_REG_BUCK5DVS2,
- MAX8997_REG_BUCK5DVS3,
- MAX8997_REG_BUCK5DVS4,
- MAX8997_REG_BUCK5DVS5,
- MAX8997_REG_BUCK5DVS6,
- MAX8997_REG_BUCK5DVS7,
- MAX8997_REG_BUCK5DVS8,
- MAX8997_REG_BUCK6CTRL,
- MAX8997_REG_BUCK6BPSKIPCTRL,
- MAX8997_REG_BUCK7CTRL,
- MAX8997_REG_BUCK7DVS,
- MAX8997_REG_LDO1CTRL,
- MAX8997_REG_LDO2CTRL,
- MAX8997_REG_LDO3CTRL,
- MAX8997_REG_LDO4CTRL,
- MAX8997_REG_LDO5CTRL,
- MAX8997_REG_LDO6CTRL,
- MAX8997_REG_LDO7CTRL,
- MAX8997_REG_LDO8CTRL,
- MAX8997_REG_LDO9CTRL,
- MAX8997_REG_LDO10CTRL,
- MAX8997_REG_LDO11CTRL,
- MAX8997_REG_LDO12CTRL,
- MAX8997_REG_LDO13CTRL,
- MAX8997_REG_LDO14CTRL,
- MAX8997_REG_LDO15CTRL,
- MAX8997_REG_LDO16CTRL,
- MAX8997_REG_LDO17CTRL,
- MAX8997_REG_LDO18CTRL,
- MAX8997_REG_LDO21CTRL,
- MAX8997_REG_MBCCTRL1,
- MAX8997_REG_MBCCTRL2,
- MAX8997_REG_MBCCTRL3,
- MAX8997_REG_MBCCTRL4,
- MAX8997_REG_MBCCTRL5,
- MAX8997_REG_MBCCTRL6,
- MAX8997_REG_OTPCGHCVS,
- MAX8997_REG_SAFEOUTCTRL,
- MAX8997_REG_LBCNFG1,
- MAX8997_REG_LBCNFG2,
- MAX8997_REG_BBCCTRL,
-
- MAX8997_REG_FLASH1_CUR,
- MAX8997_REG_FLASH2_CUR,
- MAX8997_REG_MOVIE_CUR,
- MAX8997_REG_GSMB_CUR,
- MAX8997_REG_BOOST_CNTL,
- MAX8997_REG_LEN_CNTL,
- MAX8997_REG_FLASH_CNTL,
- MAX8997_REG_WDT_CNTL,
- MAX8997_REG_MAXFLASH1,
- MAX8997_REG_MAXFLASH2,
- MAX8997_REG_FLASHSTATUSMASK,
-
- MAX8997_REG_GPIOCNTL1,
- MAX8997_REG_GPIOCNTL2,
- MAX8997_REG_GPIOCNTL3,
- MAX8997_REG_GPIOCNTL4,
- MAX8997_REG_GPIOCNTL5,
- MAX8997_REG_GPIOCNTL6,
- MAX8997_REG_GPIOCNTL7,
- MAX8997_REG_GPIOCNTL8,
- MAX8997_REG_GPIOCNTL9,
- MAX8997_REG_GPIOCNTL10,
- MAX8997_REG_GPIOCNTL11,
- MAX8997_REG_GPIOCNTL12,
-
- MAX8997_REG_LDO1CONFIG,
- MAX8997_REG_LDO2CONFIG,
- MAX8997_REG_LDO3CONFIG,
- MAX8997_REG_LDO4CONFIG,
- MAX8997_REG_LDO5CONFIG,
- MAX8997_REG_LDO6CONFIG,
- MAX8997_REG_LDO7CONFIG,
- MAX8997_REG_LDO8CONFIG,
- MAX8997_REG_LDO9CONFIG,
- MAX8997_REG_LDO10CONFIG,
- MAX8997_REG_LDO11CONFIG,
- MAX8997_REG_LDO12CONFIG,
- MAX8997_REG_LDO13CONFIG,
- MAX8997_REG_LDO14CONFIG,
- MAX8997_REG_LDO15CONFIG,
- MAX8997_REG_LDO16CONFIG,
- MAX8997_REG_LDO17CONFIG,
- MAX8997_REG_LDO18CONFIG,
- MAX8997_REG_LDO21CONFIG,
-
- MAX8997_REG_DVSOKTIMER1,
- MAX8997_REG_DVSOKTIMER2,
- MAX8997_REG_DVSOKTIMER4,
- MAX8997_REG_DVSOKTIMER5,
-};
+MODULE_DEVICE_TABLE(i2c, max8997_i2c_id);
-u8 max8997_dumpaddr_muic[] = {
- MAX8997_MUIC_REG_INTMASK1,
- MAX8997_MUIC_REG_INTMASK2,
- MAX8997_MUIC_REG_INTMASK3,
- MAX8997_MUIC_REG_CDETCTRL,
- MAX8997_MUIC_REG_CONTROL1,
- MAX8997_MUIC_REG_CONTROL2,
- MAX8997_MUIC_REG_CONTROL3,
-};
-
-u8 max8997_dumpaddr_haptic[] = {
- MAX8997_HAPTIC_REG_CONF1,
- MAX8997_HAPTIC_REG_CONF2,
- MAX8997_HAPTIC_REG_DRVCONF,
- MAX8997_HAPTIC_REG_CYCLECONF1,
- MAX8997_HAPTIC_REG_CYCLECONF2,
- MAX8997_HAPTIC_REG_SIGCONF1,
- MAX8997_HAPTIC_REG_SIGCONF2,
- MAX8997_HAPTIC_REG_SIGCONF3,
- MAX8997_HAPTIC_REG_SIGCONF4,
- MAX8997_HAPTIC_REG_SIGDC1,
- MAX8997_HAPTIC_REG_SIGDC2,
- MAX8997_HAPTIC_REG_SIGPWMDC1,
- MAX8997_HAPTIC_REG_SIGPWMDC2,
- MAX8997_HAPTIC_REG_SIGPWMDC3,
- MAX8997_HAPTIC_REG_SIGPWMDC4,
-};
-
-static int max8997_freeze(struct device *dev)
+#ifdef CONFIG_PM
+static int max8997_suspend(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int i;
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
- max8997_read_reg(i2c, max8997_dumpaddr_pmic[i],
- &max8997->reg_dump[i]);
+ if (max8997->wakeup)
+ enable_irq_wake(max8997->irq);
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
- max8997_read_reg(i2c, max8997_dumpaddr_muic[i],
- &max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
-
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
- max8997_read_reg(i2c, max8997_dumpaddr_haptic[i],
- &max8997->reg_dump[i + MAX8997_REG_PMIC_END +
- MAX8997_MUIC_REG_END]);
+ disable_irq(max8997->irq);
return 0;
}
-static int max8997_restore(struct device *dev)
+static int max8997_resume(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++)
- max8997_write_reg(i2c, max8997_dumpaddr_pmic[i],
- max8997->reg_dump[i]);
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++)
- max8997_write_reg(i2c, max8997_dumpaddr_muic[i],
- max8997->reg_dump[i + MAX8997_REG_PMIC_END]);
+ if (max8997->wakeup)
+ disable_irq_wake(max8997->irq);
- for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++)
- max8997_write_reg(i2c, max8997_dumpaddr_haptic[i],
- max8997->reg_dump[i + MAX8997_REG_PMIC_END +
- MAX8997_MUIC_REG_END]);
+ enable_irq(max8997->irq);
return 0;
}
+#else
+#define max8997_suspend NULL
+#define max8997_resume NULL
+#endif /* CONFIG_PM */
const struct dev_pm_ops max8997_pm = {
- .freeze = max8997_freeze,
- .restore = max8997_restore,
+ .suspend = max8997_suspend,
+ .resume = max8997_resume,
};
static struct i2c_driver max8997_i2c_driver = {
@@ -423,5 +255,5 @@ static void __exit max8997_i2c_exit(void)
module_exit(max8997_i2c_exit);
MODULE_DESCRIPTION("MAXIM 8997 multi-function core driver");
-MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_AUTHOR("<ms925.kim@samsung.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c
new file mode 100644
index 0000000..fa947ab
--- /dev/null
+++ b/drivers/mfd/s5m-core.c
@@ -0,0 +1,249 @@
+/*
+ * s5m87xx.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#include <linux/mfd/s5m87xx/s5m-rtc.h>
+
+static struct mfd_cell s5m87xx_devs[] = {
+ {
+ .name = "s5m8767-pmic",
+ }, {
+ .name = "s5m8763-pmic",
+ }, {
+ .name = "s5m-rtc",
+ },
+};
+
+int s5m_reg_read(struct i2c_client *i2c, u8 reg, u8 *dest)
+{
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&s5m87xx->iolock);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ mutex_unlock(&s5m87xx->iolock);
+ if (ret < 0)
+ return ret;
+
+ ret &= 0xff;
+ *dest = ret;
+ return 0;
+}
+EXPORT_SYMBOL(s5m_reg_read);
+
+int s5m_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&s5m87xx->iolock);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&s5m87xx->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(s5m_bulk_read);
+
+int s5m_reg_write(struct i2c_client *i2c, u8 reg, u8 value)
+{
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&s5m87xx->iolock);
+ ret = i2c_smbus_write_byte_data(i2c, reg, value);
+ mutex_unlock(&s5m87xx->iolock);
+ return ret;
+}
+EXPORT_SYMBOL(s5m_reg_write);
+
+int s5m_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf)
+{
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&s5m87xx->iolock);
+ ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf);
+ mutex_unlock(&s5m87xx->iolock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(s5m_bulk_write);
+
+int s5m_reg_update(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
+{
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+ int ret;
+
+ mutex_lock(&s5m87xx->iolock);
+ ret = i2c_smbus_read_byte_data(i2c, reg);
+ if (ret >= 0) {
+ u8 old_val = ret & 0xff;
+ u8 new_val = (val & mask) | (old_val & (~mask));
+ ret = i2c_smbus_write_byte_data(i2c, reg, new_val);
+ }
+ mutex_unlock(&s5m87xx->iolock);
+ return ret;
+}
+EXPORT_SYMBOL(s5m_reg_update);
+
+static int s5m87xx_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct s5m_platform_data *pdata = i2c->dev.platform_data;
+ struct s5m87xx_dev *s5m87xx;
+ int ret = 0;
+
+ s5m87xx = kzalloc(sizeof(struct s5m87xx_dev), GFP_KERNEL);
+ if (s5m87xx == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, s5m87xx);
+ s5m87xx->dev = &i2c->dev;
+ s5m87xx->i2c = i2c;
+ s5m87xx->irq = gpio_to_irq(pdata->irq_gpio);
+ s5m87xx->type = id->driver_data;
+
+ if (pdata) {
+ s5m87xx->device_type = pdata->device_type;
+ s5m87xx->ono = pdata->ono;
+ s5m87xx->irq_base = pdata->irq_base;
+ s5m87xx->wakeup = pdata->wakeup;
+ s5m87xx->wtsr_smpl = pdata->wtsr_smpl;
+ }
+
+ mutex_init(&s5m87xx->iolock);
+
+ s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+ i2c_set_clientdata(s5m87xx->rtc, s5m87xx);
+
+ if (pdata && pdata->cfg_pmic_irq)
+ pdata->cfg_pmic_irq();
+
+ s5m_irq_init(s5m87xx);
+
+ pm_runtime_set_active(s5m87xx->dev);
+
+ ret = mfd_add_devices(s5m87xx->dev, -1,
+ s5m87xx_devs, ARRAY_SIZE(s5m87xx_devs),
+ NULL, 0);
+
+ if (ret < 0)
+ goto err;
+
+ dev_info(s5m87xx->dev ,"S5M87xx MFD probe done!!! \n");
+ return ret;
+
+err:
+ mfd_remove_devices(s5m87xx->dev);
+ s5m_irq_exit(s5m87xx);
+ i2c_unregister_device(s5m87xx->rtc);
+ kfree(s5m87xx);
+ return ret;
+}
+
+static int s5m87xx_i2c_remove(struct i2c_client *i2c)
+{
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(s5m87xx->dev);
+ s5m_irq_exit(s5m87xx);
+ i2c_unregister_device(s5m87xx->rtc);
+ kfree(s5m87xx);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5m87xx_i2c_id[] = {
+ { "s5m87xx", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
+
+#ifdef CONFIG_PM
+static int s5m_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+
+ if (s5m87xx->wakeup)
+ enable_irq_wake(s5m87xx->irq);
+
+ disable_irq(s5m87xx->irq);
+
+ return 0;
+}
+
+static int s5m_resume(struct device *dev)
+{
+ struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+
+ if (s5m87xx->wakeup)
+ disable_irq_wake(s5m87xx->irq);
+
+ enable_irq(s5m87xx->irq);
+
+ return 0;
+}
+#else
+#define s5m_suspend NULL
+#define s5m_resume NULL
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops s5m87xx_apm = {
+ .suspend = s5m_suspend,
+ .resume = s5m_resume,
+};
+
+static struct i2c_driver s5m87xx_i2c_driver = {
+ .driver = {
+ .name = "s5m87xx",
+ .owner = THIS_MODULE,
+ .pm = &s5m87xx_apm,
+ },
+ .probe = s5m87xx_i2c_probe,
+ .remove = s5m87xx_i2c_remove,
+ .id_table = s5m87xx_i2c_id,
+};
+
+static int __init s5m87xx_i2c_init(void)
+{
+ return i2c_add_driver(&s5m87xx_i2c_driver);
+}
+
+subsys_initcall(s5m87xx_i2c_init);
+
+static void __exit s5m87xx_i2c_exit(void)
+{
+ i2c_del_driver(&s5m87xx_i2c_driver);
+}
+module_exit(s5m87xx_i2c_exit);
+
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("Core support for the S5M87XX MFD");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c
new file mode 100644
index 0000000..dbf1b7a
--- /dev/null
+++ b/drivers/mfd/s5m-irq.c
@@ -0,0 +1,487 @@
+/*
+ * s5m87xx-irq.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+
+struct s5m_irq_data {
+ int reg;
+ int mask;
+};
+
+static struct s5m_irq_data s5m8767_irqs[] = {
+ [S5M8767_IRQ_PWRR] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_PWRR_MASK,
+ },
+ [S5M8767_IRQ_PWRF] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_PWRF_MASK,
+ },
+ [S5M8767_IRQ_PWR1S] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_PWR1S_MASK,
+ },
+ [S5M8767_IRQ_JIGR] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_JIGR_MASK,
+ },
+ [S5M8767_IRQ_JIGF] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_JIGF_MASK,
+ },
+ [S5M8767_IRQ_LOWBAT2] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_LOWBAT2_MASK,
+ },
+ [S5M8767_IRQ_LOWBAT1] = {
+ .reg = 1,
+ .mask = S5M8767_IRQ_LOWBAT1_MASK,
+ },
+ [S5M8767_IRQ_MRB] = {
+ .reg = 2,
+ .mask = S5M8767_IRQ_MRB_MASK,
+ },
+ [S5M8767_IRQ_DVSOK2] = {
+ .reg = 2,
+ .mask = S5M8767_IRQ_DVSOK2_MASK,
+ },
+ [S5M8767_IRQ_DVSOK3] = {
+ .reg = 2,
+ .mask = S5M8767_IRQ_DVSOK3_MASK,
+ },
+ [S5M8767_IRQ_DVSOK4] = {
+ .reg = 2,
+ .mask = S5M8767_IRQ_DVSOK4_MASK,
+ },
+ [S5M8767_IRQ_RTC60S] = {
+ .reg = 3,
+ .mask = S5M8767_IRQ_RTC60S_MASK,
+ },
+ [S5M8767_IRQ_RTCA1] = {
+ .reg = 3,
+ .mask = S5M8767_IRQ_RTCA1_MASK,
+ },
+ [S5M8767_IRQ_RTCA2] = {
+ .reg = 3,
+ .mask = S5M8767_IRQ_RTCA2_MASK,
+ },
+ [S5M8767_IRQ_SMPL] = {
+ .reg = 3,
+ .mask = S5M8767_IRQ_SMPL_MASK,
+ },
+ [S5M8767_IRQ_RTC1S] = {
+ .reg = 3,
+ .mask = S5M8767_IRQ_RTC1S_MASK,
+ },
+ [S5M8767_IRQ_WTSR] = {
+ .reg = 3,
+ .mask = S5M8767_IRQ_WTSR_MASK,
+ },
+};
+
+static struct s5m_irq_data s5m8763_irqs[] = {
+ [S5M8763_IRQ_DCINF] = {
+ .reg = 1,
+ .mask = S5M8763_IRQ_DCINF_MASK,
+ },
+ [S5M8763_IRQ_DCINR] = {
+ .reg = 1,
+ .mask = S5M8763_IRQ_DCINR_MASK,
+ },
+ [S5M8763_IRQ_JIGF] = {
+ .reg = 1,
+ .mask = S5M8763_IRQ_JIGF_MASK,
+ },
+ [S5M8763_IRQ_JIGR] = {
+ .reg = 1,
+ .mask = S5M8763_IRQ_JIGR_MASK,
+ },
+ [S5M8763_IRQ_PWRONF] = {
+ .reg = 1,
+ .mask = S5M8763_IRQ_PWRONF_MASK,
+ },
+ [S5M8763_IRQ_PWRONR] = {
+ .reg = 1,
+ .mask = S5M8763_IRQ_PWRONR_MASK,
+ },
+ [S5M8763_IRQ_WTSREVNT] = {
+ .reg = 2,
+ .mask = S5M8763_IRQ_WTSREVNT_MASK,
+ },
+ [S5M8763_IRQ_SMPLEVNT] = {
+ .reg = 2,
+ .mask = S5M8763_IRQ_SMPLEVNT_MASK,
+ },
+ [S5M8763_IRQ_ALARM1] = {
+ .reg = 2,
+ .mask = S5M8763_IRQ_ALARM1_MASK,
+ },
+ [S5M8763_IRQ_ALARM0] = {
+ .reg = 2,
+ .mask = S5M8763_IRQ_ALARM0_MASK,
+ },
+ [S5M8763_IRQ_ONKEY1S] = {
+ .reg = 3,
+ .mask = S5M8763_IRQ_ONKEY1S_MASK,
+ },
+ [S5M8763_IRQ_TOPOFFR] = {
+ .reg = 3,
+ .mask = S5M8763_IRQ_TOPOFFR_MASK,
+ },
+ [S5M8763_IRQ_DCINOVPR] = {
+ .reg = 3,
+ .mask = S5M8763_IRQ_DCINOVPR_MASK,
+ },
+ [S5M8763_IRQ_CHGRSTF] = {
+ .reg = 3,
+ .mask = S5M8763_IRQ_CHGRSTF_MASK,
+ },
+ [S5M8763_IRQ_DONER] = {
+ .reg = 3,
+ .mask = S5M8763_IRQ_DONER_MASK,
+ },
+ [S5M8763_IRQ_CHGFAULT] = {
+ .reg = 3,
+ .mask = S5M8763_IRQ_CHGFAULT_MASK,
+ },
+ [S5M8763_IRQ_LOBAT1] = {
+ .reg = 4,
+ .mask = S5M8763_IRQ_LOBAT1_MASK,
+ },
+ [S5M8763_IRQ_LOBAT2] = {
+ .reg = 4,
+ .mask = S5M8763_IRQ_LOBAT2_MASK,
+ },
+};
+
+static inline struct s5m_irq_data *
+irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq)
+{
+ return &s5m8767_irqs[irq - s5m87xx->irq_base];
+}
+
+static void s5m8767_irq_lock(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&s5m87xx->irqlock);
+}
+
+static void s5m8767_irq_sync_unlock(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
+ if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
+ s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
+ s5m_reg_write(s5m87xx->i2c, S5M8767_REG_INT1M + i,
+ s5m87xx->irq_masks_cur[i]);
+ }
+ }
+
+ mutex_unlock(&s5m87xx->irqlock);
+}
+
+static void s5m8767_irq_unmask(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+ struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
+ data->irq);
+
+ s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void s5m8767_irq_mask(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+ struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
+ data->irq);
+
+ s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip s5m8767_irq_chip = {
+ .name = "s5m8767",
+ .irq_bus_lock = s5m8767_irq_lock,
+ .irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
+ .irq_mask = s5m8767_irq_mask,
+ .irq_unmask = s5m8767_irq_unmask,
+};
+
+static inline struct s5m_irq_data *
+irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq)
+{
+ return &s5m8763_irqs[irq - s5m87xx->irq_base];
+}
+
+static void s5m8763_irq_lock(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&s5m87xx->irqlock);
+}
+
+static void s5m8763_irq_sync_unlock(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
+ if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
+ s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
+ s5m_reg_write(s5m87xx->i2c, S5M8763_REG_IRQM1 + i,
+ s5m87xx->irq_masks_cur[i]);
+ }
+ }
+
+ mutex_unlock(&s5m87xx->irqlock);
+}
+
+static void s5m8763_irq_unmask(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+ struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
+ data->irq);
+
+ s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void s5m8763_irq_mask(struct irq_data *data)
+{
+ struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+ struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
+ data->irq);
+
+ s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip s5m8763_irq_chip = {
+ .name = "s5m8763",
+ .irq_bus_lock = s5m8763_irq_lock,
+ .irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
+ .irq_mask = s5m8763_irq_mask,
+ .irq_unmask = s5m8763_irq_unmask,
+};
+
+
+static irqreturn_t s5m8767_irq_thread(int irq, void *data)
+{
+ struct s5m87xx_dev *s5m87xx = data;
+ u8 irq_reg[NUM_IRQ_REGS-1];
+ int ret;
+ int i;
+
+
+ ret = s5m_bulk_read(s5m87xx->i2c, S5M8767_REG_INT1,
+ NUM_IRQ_REGS - 1, irq_reg);
+ if (ret < 0) {
+ dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < NUM_IRQ_REGS - 1; i++)
+ irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
+
+ for (i = 0; i < S5M8767_IRQ_NR; i++) {
+ if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
+ handle_nested_irq(s5m87xx->irq_base + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s5m8763_irq_thread(int irq, void *data)
+{
+ struct s5m87xx_dev *s5m87xx = data;
+ u8 irq_reg[NUM_IRQ_REGS];
+ int ret;
+ int i;
+
+ ret = s5m_bulk_read(s5m87xx->i2c, S5M8763_REG_IRQ1,
+ NUM_IRQ_REGS, irq_reg);
+ if (ret < 0) {
+ dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < NUM_IRQ_REGS; i++)
+ irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
+
+ for (i = 0; i < S5M8763_IRQ_NR; i++) {
+ if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
+ handle_nested_irq(s5m87xx->irq_base + i);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
+{
+ if (s5m87xx->irq && s5m87xx->irq_base){
+ switch (s5m87xx->device_type) {
+ case S5M8763X:
+ s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx);
+ break;
+ case S5M8767X:
+ s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
+ break;
+ default:
+ break;
+
+ }
+ }
+ return 0;
+}
+
+int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
+{
+ int i;
+ int cur_irq;
+ int ret = 0;
+ int type = s5m87xx->device_type;
+
+ if (!s5m87xx->irq) {
+ dev_warn(s5m87xx->dev,
+ "No interrupt specified, no interrupts\n");
+ s5m87xx->irq_base = 0;
+ return 0;
+ }
+
+ if (!s5m87xx->irq_base) {
+ dev_err(s5m87xx->dev,
+ "No interrupt base specified, no interrupts\n");
+ return 0;
+ }
+
+ mutex_init(&s5m87xx->irqlock);
+
+ switch (type) {
+ case S5M8763X:
+ for (i = 0; i < NUM_IRQ_REGS; i++) {
+ s5m87xx->irq_masks_cur[i] = 0xff;
+ s5m87xx->irq_masks_cache[i] = 0xff;
+ s5m_reg_write(s5m87xx->i2c, S5M8763_REG_IRQM1 + i,
+ 0xff);
+ }
+
+ s5m_reg_write(s5m87xx->i2c, S5M8763_REG_STATUSM1, 0xff);
+ s5m_reg_write(s5m87xx->i2c, S5M8763_REG_STATUSM2, 0xff);
+
+ for (i = 0; i < S5M8763_IRQ_NR; i++) {
+ cur_irq = i + s5m87xx->irq_base;
+ irq_set_chip_data(cur_irq, s5m87xx);
+ irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(s5m87xx->irq, NULL,
+ s5m8763_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "s5m87xx-irq", s5m87xx);
+ if (ret) {
+ dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+ s5m87xx->irq, ret);
+ return ret;
+ }
+ break;
+ case S5M8767X:
+ for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
+ s5m87xx->irq_masks_cur[i] = 0xff;
+ s5m87xx->irq_masks_cache[i] = 0xff;
+ s5m_reg_write(s5m87xx->i2c, S5M8767_REG_INT1M + i,
+ 0xff);
+ }
+ for (i = 0; i < S5M8767_IRQ_NR; i++) {
+ cur_irq = i + s5m87xx->irq_base;
+ ret = irq_set_chip_data(cur_irq, s5m87xx);
+ if (ret) {
+ dev_err(s5m87xx->dev,
+ "Failed to irq_set_chip_data %d: %d\n",
+ s5m87xx->irq, ret);
+ return ret;
+ }
+
+ irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(cur_irq, IRQF_VALID);
+#else
+ irq_set_noprobe(cur_irq);
+#endif
+ }
+
+ ret = request_threaded_irq(s5m87xx->irq, NULL,
+ s5m8767_irq_thread,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "s5m87xx-irq", s5m87xx);
+ if (ret) {
+ dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+ s5m87xx->irq, ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!s5m87xx->ono)
+ return 0;
+
+ switch (type) {
+ case S5M8763X:
+ ret = request_threaded_irq(s5m87xx->ono, NULL,
+ s5m8763_irq_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "s5m87xx-ono",
+ s5m87xx);
+ break;
+ case S5M8767X:
+ ret = request_threaded_irq(s5m87xx->ono, NULL,
+ s5m8767_irq_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
+ break;
+ default:
+ break;
+ }
+
+ if (ret)
+ dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+ s5m87xx->ono, ret);
+
+ return 0;
+}
+
+void s5m_irq_exit(struct s5m87xx_dev *s5m87xx)
+{
+ if (s5m87xx->ono)
+ free_irq(s5m87xx->ono, s5m87xx);
+
+ if (s5m87xx->irq)
+ free_irq(s5m87xx->irq, s5m87xx);
+}
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index e198d40..95f9c0e 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -243,6 +243,18 @@ static struct mfd_cell wm8994_devs[] = {
* and should be handled via the standard regulator API supply
* management.
*/
+static const char *wm1811_main_supplies[] = {
+ "DBVDD1",
+ "DBVDD2",
+ "DBVDD3",
+ "DCVDD",
+ "AVDD1",
+ "AVDD2",
+ "CPVDD",
+ "SPKVDD1",
+ "SPKVDD2",
+};
+
static const char *wm8994_main_supplies[] = {
"DBVDD",
"DCVDD",
@@ -281,6 +293,62 @@ static int wm8994_suspend(struct device *dev)
return 0;
}
+ ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA |
+ WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA |
+ WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+
+ ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA |
+ WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA |
+ WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+
+ switch (wm8994->type) {
+ case WM8958:
+ case WM1811:
+ ret = wm8994_reg_read(wm8994, WM8958_MIC_DETECT_1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read power status: %d\n", ret);
+ } else if (ret & WM8958_MICD_ENA) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (wm8994->type) {
+ case WM1811:
+ ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read jackdet: %d\n", ret);
+ } else if (ret & WM1811_JACKDET_MODE_MASK) {
+ dev_dbg(dev, "CODEC still active, ignoring suspend\n");
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Disable LDO pulldowns while the device is suspended if we
+ * don't know that something will be driving them. */
+ if (!wm8994->ldo_ena_always_driven)
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD);
+
/* GPIO configuration state is saved here since we may be configuring
* the GPIO alternate functions even if we're not using the gpiolib
* driver for them.
@@ -316,7 +384,7 @@ static int wm8994_suspend(struct device *dev)
static int wm8994_resume(struct device *dev)
{
struct wm8994 *wm8994 = dev_get_drvdata(dev);
- int ret;
+ int ret, i;
/* We may have lied to the PM core about suspending */
if (!wm8994->suspended)
@@ -329,10 +397,16 @@ static int wm8994_resume(struct device *dev)
return ret;
}
- ret = wm8994_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK,
- WM8994_NUM_IRQ_REGS * 2, &wm8994->irq_masks_cur);
- if (ret < 0)
- dev_err(dev, "Failed to restore interrupt masks: %d\n", ret);
+ /* Write register at a time as we use the cache on the CPU so store
+ * it in native endian.
+ */
+ for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) {
+ ret = wm8994_reg_write(wm8994, WM8994_INTERRUPT_STATUS_1_MASK
+ + i, wm8994->irq_masks_cur[i]);
+ if (ret < 0)
+ dev_err(dev, "Failed to restore interrupt masks: %d\n",
+ ret);
+ }
ret = wm8994_write(wm8994, WM8994_LDO_1, WM8994_NUM_LDO_REGS * 2,
&wm8994->ldo_regs);
@@ -344,6 +418,11 @@ static int wm8994_resume(struct device *dev)
if (ret < 0)
dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
+ /* Disable LDO pulldowns while the device is active */
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
+ 0);
+
wm8994->suspended = false;
return 0;
@@ -380,6 +459,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
struct wm8994_pdata *pdata = wm8994->dev->platform_data;
const char *devname;
int ret, i;
+ int pulls = 0;
mutex_init(&wm8994->io_lock);
dev_set_drvdata(wm8994->dev, wm8994);
@@ -395,6 +475,9 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
}
switch (wm8994->type) {
+ case WM1811:
+ wm8994->num_supplies = ARRAY_SIZE(wm1811_main_supplies);
+ break;
case WM8994:
wm8994->num_supplies = ARRAY_SIZE(wm8994_main_supplies);
break;
@@ -403,7 +486,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
- return -EINVAL;
+ goto err;
}
wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
@@ -415,6 +498,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
}
switch (wm8994->type) {
+ case WM1811:
+ for (i = 0; i < ARRAY_SIZE(wm1811_main_supplies); i++)
+ wm8994->supplies[i].supply = wm1811_main_supplies[i];
+ break;
case WM8994:
for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++)
wm8994->supplies[i].supply = wm8994_main_supplies[i];
@@ -425,9 +512,9 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
- return -EINVAL;
+ goto err;
}
-
+
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
wm8994->supplies);
if (ret != 0) {
@@ -446,8 +533,29 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
if (ret < 0) {
dev_err(wm8994->dev, "Failed to read ID register\n");
goto err_enable;
- }
+ } else
+ dev_info(wm8994->dev, "Succeeded to read ID register\n");
+
switch (ret) {
+ case 0x1811:
+ devname = "WM1811";
+ if (wm8994->type != WM1811)
+ dev_warn(wm8994->dev, "Device registered as type %d\n",
+ wm8994->type);
+ wm8994->type = WM1811;
+
+ /* Samsung-specific customization of MICBIAS levels */
+ wm8994_reg_write(wm8994, 0x102, 0x3);
+ wm8994_reg_write(wm8994, 0xcb, 0x5151);
+ wm8994_reg_write(wm8994, 0xd3, 0x3f3f);
+ wm8994_reg_write(wm8994, 0xd4, 0x3f3f);
+ wm8994_reg_write(wm8994, 0xd5, 0x3f3f);
+ wm8994_reg_write(wm8994, 0xd6, 0x3226);
+ wm8994_reg_write(wm8994, 0x102, 0x0);
+ wm8994_reg_write(wm8994, 0xd1, 0x87);
+ wm8994_reg_write(wm8994, 0x3b, 0x9);
+ wm8994_reg_write(wm8994, 0x3c, 0x2);
+ break;
case 0x8994:
devname = "WM8994";
if (wm8994->type != WM8994)
@@ -476,19 +584,28 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err_enable;
}
- switch (ret) {
- case 0:
- case 1:
- if (wm8994->type == WM8994)
+ wm8994->revision = ret & WM8994_CHIP_REV_MASK;
+ wm8994->cust_id = (ret & WM8994_CUST_ID_MASK) >> WM8994_CUST_ID_SHIFT;
+
+ switch (wm8994->type) {
+ case WM8994:
+ switch (wm8994->revision) {
+ case 0:
+ case 1:
dev_warn(wm8994->dev,
"revision %c not fully supported\n",
- 'A' + ret);
+ 'A' + wm8994->revision);
+ break;
+ default:
+ break;
+ }
break;
default:
break;
}
- dev_info(wm8994->dev, "%s revision %c\n", devname, 'A' + ret);
+ dev_info(wm8994->dev, "%s revision %c CUST_ID %02x\n", devname,
+ 'A' + wm8994->revision, wm8994->cust_id);
if (pdata) {
wm8994->irq_base = pdata->irq_base;
@@ -502,8 +619,19 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
pdata->gpio_defaults[i]);
}
}
+
+ wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
+
+ if (pdata->spkmode_pu)
+ pulls |= WM8994_SPKMODE_PU;
}
+ /* Disable unneeded pulls */
+ wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
+ WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD |
+ WM8994_SPKMODE_PU | WM8994_CSNADDR_PD,
+ pulls);
+
/* In some system designs where the regulators are not in use,
* we can achieve a small reduction in leakage currents by
* floating LDO outputs. This bit makes no difference if the
@@ -529,8 +657,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
goto err_irq;
}
+#if 0 /* To do */
pm_runtime_enable(wm8994->dev);
pm_runtime_resume(wm8994->dev);
+#endif
return 0;
@@ -545,7 +675,6 @@ err_supplies:
kfree(wm8994->supplies);
err:
mfd_remove_devices(wm8994->dev);
- kfree(wm8994);
return ret;
}
@@ -582,13 +711,24 @@ static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg,
return 0;
}
-static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
+static int wm8994_i2c_gather_write_device(struct wm8994 *wm8994, unsigned short reg,
int bytes, const void *src)
{
struct i2c_client *i2c = wm8994->control_data;
struct i2c_msg xfer[2];
int ret;
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_PROTOCOL_MANGLING)) {
+ dev_vdbg(wm8994->dev,
+ "%s: I2C Controller does _NOT_ support block/gather\n",
+ __func__);
+ return -ENOTSUPP;
+ }
+
+ dev_vdbg(wm8994->dev,
+ "%s: gather write - Reg = 0x%04x, bytes = 0x%x\n",
+ __func__, reg, bytes);
+
reg = cpu_to_be16(reg);
xfer[0].addr = i2c->addr;
@@ -610,6 +750,66 @@ static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
return 0;
}
+static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg,
+ int bytes, const void *src)
+{
+ struct i2c_client *i2c = wm8994->control_data;
+ int ret;
+
+ unsigned char msg[2 + 2];
+ void *buf;
+
+ /* If we're doing a single register write we can probably just
+ * send the work_buf directly, otherwise try to do a gather
+ * write.
+ */
+ if (bytes == 2) {
+ dev_vdbg(wm8994->dev,
+ "%s: Single register write - Reg = 0x%04x, bytes = 0x%x\n",
+ __func__, reg, bytes);
+
+ reg = cpu_to_be16(reg);
+ memcpy(&msg[0], &reg, 2);
+ memcpy(&msg[2], src, bytes);
+
+ ret = i2c_master_send(i2c, msg, bytes + 2);
+ if (ret < 0)
+ return ret;
+ if (ret < bytes + 2)
+ return -EIO;
+
+ return 0;
+ } else {
+ ret = wm8994_i2c_gather_write_device(wm8994, reg, bytes, src);
+ }
+
+ if (ret == -ENOTSUPP) {
+ dev_vdbg(wm8994->dev,
+ "%s: Manual group write - Reg = 0x%04x, bytes = 0x%x\n",
+ __func__, reg, bytes);
+
+ buf = kmalloc(2 + bytes, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ reg = cpu_to_be16(reg);
+ memcpy(buf, &reg, 2);
+ memcpy(buf + 2, src, bytes);
+
+ ret = i2c_master_send(i2c, buf, bytes + 2);
+
+ kfree(buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < bytes + 2)
+ return -EIO;
+
+ return 0;
+ }
+ return ret;
+}
+
static int wm8994_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -627,7 +827,12 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
wm8994->irq = i2c->irq;
wm8994->type = id->driver_data;
- return wm8994_device_init(wm8994, i2c->irq);
+ if (wm8994_device_init(wm8994, i2c->irq) < 0) {
+ kfree(wm8994);
+ return -ENODEV;
+ }
+
+ return 0;
}
static int wm8994_i2c_remove(struct i2c_client *i2c)
@@ -640,6 +845,7 @@ static int wm8994_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8994_i2c_id[] = {
+ { "wm1811", WM1811 },
{ "wm8994", WM8994 },
{ "wm8958", WM8958 },
{ }
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 71c6e8f..d682f7b 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -231,12 +231,6 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
status[i] &= ~wm8994->irq_masks_cur[i];
}
- /* Report */
- for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
- if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
- handle_nested_irq(wm8994->irq_base + i);
- }
-
/* Ack any unmasked IRQs */
for (i = 0; i < ARRAY_SIZE(status); i++) {
if (status[i])
@@ -244,6 +238,12 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data)
status[i]);
}
+ /* Report */
+ for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) {
+ if (status[wm8994_irqs[i].reg - 1] & wm8994_irqs[i].mask)
+ handle_nested_irq(wm8994->irq_base + i);
+ }
+
return IRQ_HANDLED;
}