All of lore.kernel.org
 help / color / mirror / Atom feed
From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
To: Lee Jones <lee.jones@linaro.org>
Cc: Samuel Ortiz <sameo@linux.intel.com>,
	Mark Brown <broonie@kernel.org>,
	Mike Turquette <mturquette@linaro.org>,
	Liam Girdwood <lgirdwood@gmail.com>,
	Alessandro Zummo <a.zummo@towertech.it>,
	Kukjin Kim <kgene.kim@samsung.com>,
	Doug Anderson <dianders@chromium.org>,
	Olof Johansson <olof@lixom.net>,
	Sjoerd Simons <sjoerd.simons@collabora.co.uk>,
	Daniel Stone <daniels@collabora.com>,
	Tomeu Vizoso <tomeu.vizoso@collabora.com>,
	linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org,
	Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Subject: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
Date: Mon,  9 Jun 2014 11:37:46 +0200	[thread overview]
Message-ID: <1402306670-17041-2-git-send-email-javier.martinez@collabora.co.uk> (raw)
In-Reply-To: <1402306670-17041-1-git-send-email-javier.martinez@collabora.co.uk>

Maxim MAX77802 is a power management chip that contains 10 high
efficiency Buck regulators, 32 Low-dropout (LDO) regulators used
to power up application processors and peripherals, a 2-channel
32kHz clock outputs, a Real-Time-Clock (RTC) and a I2C interface
to program the individual regulators, clocks outputs and the RTC.

This patch adds the core support for MAX77802 PMIC and is based
on a driver added by Simon Glass to the Chrome OS kernel 3.8 tree.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 Documentation/devicetree/bindings/mfd/max77802.txt |  88 ++++++
 drivers/mfd/Kconfig                                |  13 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/max77802-irq.c                         | 332 +++++++++++++++++++++
 drivers/mfd/max77802.c                             | 282 +++++++++++++++++
 include/linux/mfd/max77802-private.h               | 291 ++++++++++++++++++
 include/linux/mfd/max77802.h                       | 124 ++++++++
 7 files changed, 1131 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/max77802.txt
 create mode 100644 drivers/mfd/max77802-irq.c
 create mode 100644 drivers/mfd/max77802.c
 create mode 100644 include/linux/mfd/max77802-private.h
 create mode 100644 include/linux/mfd/max77802.h

diff --git a/Documentation/devicetree/bindings/mfd/max77802.txt b/Documentation/devicetree/bindings/mfd/max77802.txt
new file mode 100644
index 0000000..addf02e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max77802.txt
@@ -0,0 +1,88 @@
+Maxim MAX77802 multi-function device
+
+MAX77802 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
+interfaced to host controller using i2c interface. PMIC, Charger and RTC
+submodules are addressed using same i2c slave address
+
+This document describes the binding for mfd device and PMIC submodule.
+
+Binding for the built-in 32k clock generator block is defined separately
+in bindings/clk/maxim,max77802.txt file.
+
+Required properties:
+- compatible : Must be "maxim,max77802";
+- reg : Specifies the i2c slave address of PMIC block.
+- interrupts : This i2c device has an IRQ line connected to the main SoC.
+- interrupt-parent : The parent interrupt controller.
+
+Optional properties:
+- max77802,pmic-buck-default-dvs-idx: We'll always write this DVS index in the
+  PMIC for BUCKs with DVS (Bucks 1-4, 6).
+  NOTE: at the moment these bindings don't include enough details for actual
+  GPIO-DVS--this just lets you choose which single slot to use.
+
+- max77802,pmic-buck-dvs-gpios: The DVS GPIOs. We'll try to set these GPIOs
+  to match pmic-buck-default-dvs-idx at probe time if they are defined. If
+  some or all of these GPIOs are not defined it's assumed that the board has
+  any missing GPIOs hardwired to match pmic-buck-default-dvs-idx.
+
+- max77802,pmic-buck-selb-gpios: GPIOs to enable DVS-GPIO for BUCKs.
+  Should be five values: 1, 2, 3, 4, 6.  It is strongly suggested to include
+  these GPIOs if there's any chance that changing DVS GPIOs one line at a
+  time might glitch your DVS values.
+
+Optional node:
+- voltage-regulators : The regulators of max77802 have to be instantiated
+  under subnode named "voltage-regulators" using the following format.
+
+	regulator_name {
+		regulator-compatible = LDOn/BUCKn
+		standard regulator constraints....
+	};
+	refer Documentation/devicetree/bindings/regulator/regulator.txt
+
+  The regulator-compatible property of regulator should initialized with string
+to get matched with their hardware counterparts as follow:
+
+	-LDOn 	:	for LDOs, where n can lie in range 1 to 35.
+			example: LDO1, LDO2, LDO35.
+	-BUCKn 	:	for BUCKs, where n can lie in range 1 to 10.
+			example: BUCK1, BUCK5, BUCK10.
+Example:
+
+	max77802@09 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&wakeup_eint>;
+		interrupts = <26 0>;
+		reg = <0x09>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		max77802,pmic-buck-default-dvs-idx = <1>;
+		max77802,pmic-buck-dvs-gpios = <&gpy7 6 0>,
+					       <&gpj4 2 0>,
+					       <&gpj4 3 0>;
+		max77802,pmic-buck-selb-gpios = <&gph0 2 0>,
+						<&gph0 3 0>,
+						<&gph0 4 0>,
+						<&gph0 5 0>,
+						<&gph0 6 0>;
+
+		voltage-regulators {
+			ldo11_reg {
+				regulator-compatible = "LDO11";
+				regulator-name = "vdd_ldo11";
+				regulator-min-microvolt = <1900000>;
+				regulator-max-microvolt = <1900000>;
+				regulator-always-on;
+			};
+
+			buck1_reg {
+				regulator-compatible = "BUCK1";
+				regulator-name = "vdd_mif";
+				regulator-min-microvolt = <950000>;
+				regulator-max-microvolt = <1300000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+	};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ee8204c..b8df9e4 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -392,6 +392,19 @@ config MFD_MAX77693
 	  additional drivers must be enabled in order to use the functionality
 	  of the device.
 
+config MFD_MAX77802
+	bool "Maxim Integrated MAX77802 PMIC Support"
+	depends on I2C=y
+	select MFD_CORE
+	select REGMAP_I2C
+	select IRQ_DOMAIN
+	help
+	  Say yes here to support for Maxim Integrated MAX77802.
+	  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_MAX8907
 	tristate "Maxim Semiconductor MAX8907 PMIC Support"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8afedba..1a2a7ae 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -117,6 +117,7 @@ obj-$(CONFIG_MFD_DA9063)	+= da9063.o
 obj-$(CONFIG_MFD_MAX14577)	+= max14577.o
 obj-$(CONFIG_MFD_MAX77686)	+= max77686.o max77686-irq.o
 obj-$(CONFIG_MFD_MAX77693)	+= max77693.o max77693-irq.o
+obj-$(CONFIG_MFD_MAX77802)	+= max77802.o max77802-irq.o
 obj-$(CONFIG_MFD_MAX8907)	+= max8907.o
 max8925-objs			:= max8925-core.o max8925-i2c.o
 obj-$(CONFIG_MFD_MAX8925)	+= max8925.o
diff --git a/drivers/mfd/max77802-irq.c b/drivers/mfd/max77802-irq.c
new file mode 100644
index 0000000..38a8ce7
--- /dev/null
+++ b/drivers/mfd/max77802-irq.c
@@ -0,0 +1,332 @@
+/*
+ * max77802-irq.c - Interrupt controller support for MAX77802
+ *
+ * Copyright (C) 2013-2014 Google, Inc
+ *
+ * Copyright (C) 2012 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.
+ *
+ * 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/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+enum {
+	MAX77802_DEBUG_IRQ_INFO = 1 << 0,
+	MAX77802_DEBUG_IRQ_MASK = 1 << 1,
+	MAX77802_DEBUG_IRQ_INT = 1 << 2,
+};
+
+static int debug_mask = 0;
+module_param(debug_mask, int, 0);
+MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO  0x2=IRQ_MASK 0x4=IRQ_INI)");
+
+static const u8 max77802_mask_reg[] = {
+	[PMIC_INT1] = MAX77802_REG_INT1MSK,
+	[PMIC_INT2] = MAX77802_REG_INT2MSK,
+	[RTC_INT] = MAX77802_RTC_INTM,
+};
+
+struct max77802_irq_data {
+	int mask;
+	enum max77802_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask)		\
+	[(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77802_irq_data max77802_irqs[] = {
+	DECLARE_IRQ(MAX77802_PMICIRQ_PWRONF,	PMIC_INT1, 1 << 0),
+	DECLARE_IRQ(MAX77802_PMICIRQ_PWRONR,	PMIC_INT1, 1 << 1),
+	DECLARE_IRQ(MAX77802_PMICIRQ_JIGONBF,	PMIC_INT1, 1 << 2),
+	DECLARE_IRQ(MAX77802_PMICIRQ_JIGONBR,	PMIC_INT1, 1 << 3),
+	DECLARE_IRQ(MAX77802_PMICIRQ_ACOKBF,	PMIC_INT1, 1 << 4),
+	DECLARE_IRQ(MAX77802_PMICIRQ_ACOKBR,	PMIC_INT1, 1 << 5),
+	DECLARE_IRQ(MAX77802_PMICIRQ_ONKEY1S,	PMIC_INT1, 1 << 6),
+	DECLARE_IRQ(MAX77802_PMICIRQ_MRSTB,		PMIC_INT1, 1 << 7),
+	DECLARE_IRQ(MAX77802_PMICIRQ_140C,		PMIC_INT2, 1 << 0),
+	DECLARE_IRQ(MAX77802_PMICIRQ_120C,		PMIC_INT2, 1 << 1),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTC60S,		RTC_INT, 1 << 0),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTCA1,		RTC_INT, 1 << 1),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTCA2,		RTC_INT, 1 << 2),
+	DECLARE_IRQ(MAX77802_RTCIRQ_SMPL,		RTC_INT, 1 << 3),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTC1S,		RTC_INT, 1 << 4),
+	DECLARE_IRQ(MAX77802_RTCIRQ_WTSR,		RTC_INT, 1 << 5),
+};
+
+static void max77802_irq_lock(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+		pr_info("%s\n", __func__);
+
+	mutex_lock(&max77802->irqlock);
+}
+
+static void max77802_irq_sync_unlock(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+	int i;
+
+	for (i = 0; i < MAX77802_IRQ_GROUP_NR; i++) {
+		u8 mask_reg = max77802_mask_reg[i];
+
+		if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+			pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+			__func__, i, mask_reg, max77802->irq_masks_cur[i]);
+
+		if (mask_reg == MAX77802_REG_INVALID ||
+				IS_ERR_OR_NULL(max77802->regmap))
+			continue;
+
+		max77802->irq_masks_cache[i] = max77802->irq_masks_cur[i];
+
+		regmap_write(max77802->regmap, mask_reg,
+			     max77802->irq_masks_cur[i]);
+	}
+
+	mutex_unlock(&max77802->irqlock);
+}
+
+static const inline struct max77802_irq_data *to_max77802_irq(int irq)
+{
+	struct irq_data *data = irq_get_irq_data(irq);
+	return &max77802_irqs[data->hwirq];
+}
+
+static void max77802_irq_mask(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+	const struct max77802_irq_data *irq_data = to_max77802_irq(data->irq);
+
+	max77802->irq_masks_cur[irq_data->group] |= irq_data->mask;
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+		pr_info("%s: group=%d, cur=0x%x\n",
+			__func__, irq_data->group,
+			max77802->irq_masks_cur[irq_data->group]);
+}
+
+static void max77802_irq_unmask(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+	const struct max77802_irq_data *irq_data = to_max77802_irq(data->irq);
+
+	max77802->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+		pr_info("%s: group=%d, cur=0x%x\n",
+			__func__, irq_data->group,
+			max77802->irq_masks_cur[irq_data->group]);
+}
+
+static int max77802_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+
+	if (device_may_wakeup(max77802->dev)) {
+		if (on)
+			return enable_irq_wake(max77802->irq);
+		else
+			return disable_irq_wake(max77802->irq);
+	} else if (on) {
+		dev_warn(max77802->dev,
+			 "Child requested wakeup but wakeup disabled\n");
+		return -ENXIO;
+	}
+	return 0;
+}
+
+static struct irq_chip max77802_irq_chip = {
+	.name			= "max77802",
+	.irq_bus_lock		= max77802_irq_lock,
+	.irq_bus_sync_unlock	= max77802_irq_sync_unlock,
+	.irq_mask		= max77802_irq_mask,
+	.irq_unmask		= max77802_irq_unmask,
+	.irq_set_wake		= max77802_irq_set_wake,
+};
+
+static irqreturn_t max77802_irq_thread(int irq, void *data)
+{
+	struct max77802_dev *max77802 = data;
+	unsigned int irq_reg[MAX77802_IRQ_GROUP_NR] = {};
+	unsigned int irq_src;
+	int ret;
+	int i, cur_irq;
+
+	ret = regmap_read(max77802->regmap,  MAX77802_REG_INTSRC, &irq_src);
+	if (ret < 0) {
+		dev_err(max77802->dev, "Failed to read interrupt source: %d\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_INT)
+		pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+	if (irq_src == MAX77802_IRQSRC_PMIC) {
+		ret = regmap_bulk_read(max77802->regmap,
+					 MAX77802_REG_INT1, irq_reg, 2);
+		if (ret < 0) {
+			dev_err(max77802->dev, "Failed to read interrupt source: %d\n",
+					ret);
+			return IRQ_NONE;
+		}
+
+		if (debug_mask & MAX77802_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 & MAX77802_IRQSRC_RTC) {
+		ret = regmap_read(max77802->regmap,
+				  MAX77802_RTC_INT, &irq_reg[RTC_INT]);
+		if (ret < 0) {
+			dev_err(max77802->dev, "Failed to read interrupt source: %d\n",
+					ret);
+			return IRQ_NONE;
+		}
+
+		if (debug_mask & MAX77802_DEBUG_IRQ_INT)
+			pr_info("%s: rtc int=0x%x\n", __func__,
+				irq_reg[RTC_INT]);
+
+	}
+
+	for (i = 0; i < MAX77802_IRQ_GROUP_NR; i++)
+		irq_reg[i] &= ~max77802->irq_masks_cur[i];
+
+	for (i = 0; i < MAX77802_IRQ_NR; i++) {
+		if (irq_reg[max77802_irqs[i].group] & max77802_irqs[i].mask) {
+			cur_irq = irq_find_mapping(max77802->irq_domain, i);
+			if (cur_irq)
+				handle_nested_irq(cur_irq);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int max77802_irq_domain_map(struct irq_domain *d, unsigned int irq,
+				   irq_hw_number_t hw)
+{
+	struct max77802_dev *max77802 = d->host_data;
+
+	irq_set_chip_data(irq, max77802);
+	irq_set_chip_and_handler(irq, &max77802_irq_chip, handle_simple_irq);
+	irq_set_nested_thread(irq, 1);
+	irq_set_parent(irq, max77802->irq);
+#ifdef CONFIG_ARM
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	irq_set_noprobe(irq);
+#endif
+	return 0;
+}
+
+static struct irq_domain_ops max77802_irq_domain_ops = {
+	.map = max77802_irq_domain_map,
+};
+
+int max77802_irq_init(struct max77802_dev *max77802)
+{
+	struct irq_domain *domain;
+	int i;
+	int ret;
+	int val;
+
+	mutex_init(&max77802->irqlock);
+
+	if (max77802->irq_gpio && !max77802->irq) {
+		max77802->irq = gpio_to_irq(max77802->irq_gpio);
+
+		if (debug_mask & MAX77802_DEBUG_IRQ_INT) {
+			ret = gpio_request(max77802->irq_gpio, "pmic_irq");
+			if (ret < 0) {
+				dev_err(max77802->dev,
+					"Failed to request gpio %d with ret:"
+					"%d\n",	max77802->irq_gpio, ret);
+				return IRQ_NONE;
+			}
+
+			gpio_direction_input(max77802->irq_gpio);
+			val = gpio_get_value(max77802->irq_gpio);
+			gpio_free(max77802->irq_gpio);
+			pr_info("%s: gpio_irq=%x\n", __func__, val);
+		}
+	}
+
+	if (!max77802->irq) {
+		dev_err(max77802->dev, "irq is not specified\n");
+		return -ENODEV;
+	}
+
+	/* Mask individual interrupt sources */
+	for (i = 0; i < MAX77802_IRQ_GROUP_NR; i++) {
+		max77802->irq_masks_cur[i] = 0xff;
+		max77802->irq_masks_cache[i] = 0xff;
+
+		if (IS_ERR_OR_NULL(max77802->regmap))
+			continue;
+		if (max77802_mask_reg[i] == MAX77802_REG_INVALID)
+			continue;
+
+		regmap_write(max77802->regmap, max77802_mask_reg[i], 0xff);
+	}
+	domain = irq_domain_add_linear(NULL, MAX77802_IRQ_NR,
+					&max77802_irq_domain_ops, max77802);
+	if (!domain) {
+		dev_err(max77802->dev, "could not create irq domain\n");
+		return -ENODEV;
+	}
+	max77802->irq_domain = domain;
+
+	ret = request_threaded_irq(max77802->irq, NULL, max77802_irq_thread,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   "max77802-irq", max77802);
+
+	if (ret)
+		dev_err(max77802->dev, "Failed to request IRQ %d: %d\n",
+			max77802->irq, ret);
+
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_INFO)
+		pr_info("%s-\n", __func__);
+
+	return 0;
+}
+
+void max77802_irq_exit(struct max77802_dev *max77802)
+{
+	if (max77802->irq)
+		free_irq(max77802->irq, max77802);
+}
+
+int max77802_irq_resume(struct max77802_dev *max77802)
+{
+	/*
+	 * The IRQ that woke us up may still need to be ACK'ed on resume.
+	 * If it isn't ever ACK'ed, future IRQs may not be delivered.
+	 */
+	if (max77802->irq)
+		max77802_irq_thread(0, max77802);
+
+	return 0;
+}
diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
new file mode 100644
index 0000000..59696dd
--- /dev/null
+++ b/drivers/mfd/max77802.c
@@ -0,0 +1,282 @@
+/*
+ * max77802.c - mfd core driver for the Maxim 77802
+ *
+ * Copyright (C) 2013-2014 Google, Inc
+ * Simon Glass <sjg@chromium.org>
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.com>
+ * Jonghwa Lee <jonghwa3.lee@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.
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+
+static const struct mfd_cell max77802_devs[] = {
+	{ .name = "max77802-pmic", },
+};
+
+static bool max77802_pmic_is_accessible_reg(struct device *dev,
+					    unsigned int reg)
+{
+	return (reg >= MAX77802_REG_DEVICE_ID && reg < MAX77802_REG_PMIC_END);
+}
+
+static bool max77802_rtc_is_accessible_reg(struct device *dev,
+					   unsigned int reg)
+{
+	return (reg >= MAX77802_RTC_INT && reg < MAX77802_RTC_END);
+}
+
+static bool max77802_is_accessible_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_pmic_is_accessible_reg(dev, reg) ||
+		max77802_rtc_is_accessible_reg(dev, reg));
+}
+
+static bool max77802_pmic_is_precious_reg(struct device *dev, unsigned int reg)
+{
+	return (reg == MAX77802_REG_INTSRC || reg == MAX77802_REG_INT1 ||
+		reg == MAX77802_REG_INT2);
+}
+
+static bool max77802_rtc_is_precious_reg(struct device *dev, unsigned int reg)
+{
+	return (reg == MAX77802_RTC_INT ||
+		reg == MAX77802_RTC_UPDATE0 ||
+		reg == MAX77802_RTC_UPDATE1);
+}
+
+static bool max77802_is_precious_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_pmic_is_precious_reg(dev, reg) ||
+		max77802_rtc_is_precious_reg(dev, reg));
+}
+
+static bool max77802_pmic_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_is_precious_reg(dev, reg) ||
+		reg == MAX77802_REG_STATUS1 || reg == MAX77802_REG_STATUS2 ||
+		reg == MAX77802_REG_PWRON);
+}
+
+static bool max77802_rtc_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_rtc_is_precious_reg(dev, reg) ||
+		reg == MAX77802_RTC_SEC ||
+		reg == MAX77802_RTC_MIN ||
+		reg == MAX77802_RTC_HOUR ||
+		reg == MAX77802_RTC_WEEKDAY ||
+		reg == MAX77802_RTC_MONTH ||
+		reg == MAX77802_RTC_YEAR ||
+		reg == MAX77802_RTC_DATE);
+}
+
+static bool max77802_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_pmic_is_volatile_reg(dev, reg) ||
+		max77802_rtc_is_volatile_reg(dev, reg));
+}
+
+static struct regmap_config max77802_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = max77802_is_accessible_reg,
+	.readable_reg = max77802_is_accessible_reg,
+	.precious_reg = max77802_is_precious_reg,
+	.volatile_reg = max77802_is_volatile_reg,
+	.name = "max77802-pmic",
+	.cache_type = REGCACHE_RBTREE,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id max77802_pmic_dt_match[] = {
+	{.compatible = "maxim,max77802", .data = NULL},
+	{},
+};
+
+static void max77802_dt_parse_dvs_gpio(struct device *dev,
+				       struct max77802_platform_data *pd,
+				       struct device_node *np)
+{
+	int i;
+
+	/*
+	 * NOTE: we don't consider GPIO errors fatal; board may have some lines
+	 * directly pulled high or low and thus doesn't specify them.
+	 */
+	for (i = 0; i < ARRAY_SIZE(pd->buck_gpio_dvs); i++)
+		pd->buck_gpio_dvs[i] =
+			of_get_named_gpio(np,
+					  "max77802,pmic-buck-dvs-gpios", i);
+
+	for (i = 0; i < ARRAY_SIZE(pd->buck_gpio_selb); i++)
+		pd->buck_gpio_selb[i] =
+			of_get_named_gpio(np,
+					  "max77802,pmic-buck-selb-gpios", i);
+}
+
+static struct max77802_platform_data *max77802_i2c_parse_dt_pdata(struct device
+								  *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct max77802_platform_data *pd;
+
+	pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd) {
+		dev_err(dev, "could not allocate memory for pdata\n");
+		return NULL;
+	}
+
+	/* Read default index and ignore errors, since default is 0 */
+	of_property_read_u32(np, "max77802,pmic-buck-default-dvs-idx",
+			     &pd->buck_default_idx);
+
+	max77802_dt_parse_dvs_gpio(dev, pd, np);
+
+	dev->platform_data = pd;
+	return pd;
+}
+#else
+static struct max77802_platform_data *max77802_i2c_parse_dt_pdata(struct device
+								  *dev)
+{
+	return 0;
+}
+#endif
+
+static int max77802_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct max77802_dev *max77802 = NULL;
+	struct max77802_platform_data *pdata = dev_get_platdata(&i2c->dev);
+	unsigned int data;
+	int ret = 0;
+
+	if (i2c->dev.of_node)
+		pdata = max77802_i2c_parse_dt_pdata(&i2c->dev);
+
+	if (!pdata) {
+		dev_err(&i2c->dev, "No platform data found.\n");
+		return -EIO;
+	}
+
+	max77802 = devm_kzalloc(&i2c->dev, sizeof(struct max77802_dev),
+				GFP_KERNEL);
+	if (max77802 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, max77802);
+	max77802->dev = &i2c->dev;
+	max77802->i2c = i2c;
+	max77802->type = id->driver_data;
+
+	max77802->wakeup = pdata->wakeup;
+	max77802->irq_gpio = pdata->irq_gpio;
+	max77802->irq = i2c->irq;
+
+	max77802->regmap = devm_regmap_init_i2c(i2c, &max77802_regmap_config);
+	if (IS_ERR(max77802->regmap)) {
+		ret = PTR_ERR(max77802->regmap);
+		dev_err(max77802->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (regmap_read(max77802->regmap,
+			 MAX77802_REG_DEVICE_ID, &data) < 0) {
+		dev_err(max77802->dev,
+			"device not found on this channel (this is not an error)\n");
+		return -ENODEV;
+	} else {
+		dev_info(max77802->dev, "device found\n");
+	}
+
+	max77802_irq_init(max77802);
+
+	ret = mfd_add_devices(max77802->dev, -1, max77802_devs,
+			      ARRAY_SIZE(max77802_devs), NULL, 0, NULL);
+	if (ret < 0)
+		mfd_remove_devices(max77802->dev);
+
+	return ret;
+}
+
+static int max77802_i2c_remove(struct i2c_client *i2c)
+{
+	struct max77802_dev *max77802 = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(max77802->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id max77802_i2c_id[] = {
+	{ "max77802", TYPE_MAX77802 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max77802_i2c_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int max77802_resume(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max77802_dev *max77802 = i2c_get_clientdata(i2c);
+
+	max77802_irq_resume(max77802);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77802_pm_ops, NULL, max77802_resume);
+
+static struct i2c_driver max77802_i2c_driver = {
+	.driver = {
+		   .name = "max77802",
+		   .owner = THIS_MODULE,
+		   .pm = &max77802_pm_ops,
+		   .of_match_table = of_match_ptr(max77802_pmic_dt_match),
+	},
+	.probe = max77802_i2c_probe,
+	.remove = max77802_i2c_remove,
+	.id_table = max77802_i2c_id,
+};
+
+static int __init max77802_i2c_init(void)
+{
+	return i2c_add_driver(&max77802_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77802_i2c_init);
+
+static void __exit max77802_i2c_exit(void)
+{
+	i2c_del_driver(&max77802_i2c_driver);
+}
+module_exit(max77802_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77802 multi-function core driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/max77802-private.h b/include/linux/mfd/max77802-private.h
new file mode 100644
index 0000000..7db5225
--- /dev/null
+++ b/include/linux/mfd/max77802-private.h
@@ -0,0 +1,291 @@
+/*
+ * max77802-private.h - Voltage regulator driver for the Maxim 77802
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_MAX77802_PRIV_H
+#define __LINUX_MFD_MAX77802_PRIV_H
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+
+#define MAX77802_REG_INVALID		(0xff)
+
+enum max77802_pmic_reg {
+	MAX77802_REG_DEVICE_ID		= 0x00,
+	MAX77802_REG_INTSRC		= 0x01,
+	MAX77802_REG_INT1		= 0x02,
+	MAX77802_REG_INT2		= 0x03,
+
+	MAX77802_REG_INT1MSK		= 0x04,
+	MAX77802_REG_INT2MSK		= 0x05,
+
+	MAX77802_REG_STATUS1		= 0x06,
+	MAX77802_REG_STATUS2		= 0x07,
+
+	MAX77802_REG_PWRON		= 0x08,
+	/* Reserved: 0x09 */
+	MAX77802_REG_MRSTB		= 0x0A,
+	MAX77802_REG_EPWRHOLD		= 0x0B,
+	/* Reserved: 0x0C-0x0D */
+	MAX77802_REG_BOOSTCTRL		= 0x0E,
+	MAX77802_REG_BOOSTOUT		= 0x0F,
+
+	MAX77802_REG_BUCK1CTRL		= 0x10,
+	MAX77802_REG_BUCK1DVS1		= 0x11,
+	MAX77802_REG_BUCK1DVS2		= 0x12,
+	MAX77802_REG_BUCK1DVS3		= 0x13,
+	MAX77802_REG_BUCK1DVS4		= 0x14,
+	MAX77802_REG_BUCK1DVS5		= 0x15,
+	MAX77802_REG_BUCK1DVS6		= 0x16,
+	MAX77802_REG_BUCK1DVS7		= 0x17,
+	MAX77802_REG_BUCK1DVS8		= 0x18,
+	/* Reserved: 0x19 */
+	MAX77802_REG_BUCK2CTRL1		= 0x1A,
+	MAX77802_REG_BUCK2CTRL2		= 0x1B,
+	MAX77802_REG_BUCK2PHTRAN	= 0x1C,
+	MAX77802_REG_BUCK2DVS1		= 0x1D,
+	MAX77802_REG_BUCK2DVS2		= 0x1E,
+	MAX77802_REG_BUCK2DVS3		= 0x1F,
+	MAX77802_REG_BUCK2DVS4		= 0x20,
+	MAX77802_REG_BUCK2DVS5		= 0x21,
+	MAX77802_REG_BUCK2DVS6		= 0x22,
+	MAX77802_REG_BUCK2DVS7		= 0x23,
+	MAX77802_REG_BUCK2DVS8		= 0x24,
+	/* Reserved: 0x25-0x26 */
+	MAX77802_REG_BUCK3CTRL1		= 0x27,
+	MAX77802_REG_BUCK3DVS1		= 0x28,
+	MAX77802_REG_BUCK3DVS2		= 0x29,
+	MAX77802_REG_BUCK3DVS3		= 0x2A,
+	MAX77802_REG_BUCK3DVS4		= 0x2B,
+	MAX77802_REG_BUCK3DVS5		= 0x2C,
+	MAX77802_REG_BUCK3DVS6		= 0x2D,
+	MAX77802_REG_BUCK3DVS7		= 0x2E,
+	MAX77802_REG_BUCK3DVS8		= 0x2F,
+	/* Reserved: 0x30-0x36 */
+	MAX77802_REG_BUCK4CTRL1		= 0x37,
+	MAX77802_REG_BUCK4DVS1		= 0x38,
+	MAX77802_REG_BUCK4DVS2		= 0x39,
+	MAX77802_REG_BUCK4DVS3		= 0x3A,
+	MAX77802_REG_BUCK4DVS4		= 0x3B,
+	MAX77802_REG_BUCK4DVS5		= 0x3C,
+	MAX77802_REG_BUCK4DVS6		= 0x3D,
+	MAX77802_REG_BUCK4DVS7		= 0x3E,
+	MAX77802_REG_BUCK4DVS8		= 0x3F,
+	/* Reserved: 0x40 */
+	MAX77802_REG_BUCK5CTRL		= 0x41,
+	MAX77802_REG_BUCK5OUT		= 0x42,
+	/* Reserved: 0x43 */
+	MAX77802_REG_BUCK6CTRL		= 0x44,
+	MAX77802_REG_BUCK6DVS1		= 0x45,
+	MAX77802_REG_BUCK6DVS2		= 0x46,
+	MAX77802_REG_BUCK6DVS3		= 0x47,
+	MAX77802_REG_BUCK6DVS4		= 0x48,
+	MAX77802_REG_BUCK6DVS5		= 0x49,
+	MAX77802_REG_BUCK6DVS6		= 0x4A,
+	MAX77802_REG_BUCK6DVS7		= 0x4B,
+	MAX77802_REG_BUCK6DVS8		= 0x4C,
+	/* Reserved: 0x4D */
+	MAX77802_REG_BUCK7CTRL		= 0x4E,
+	MAX77802_REG_BUCK7OUT		= 0x4F,
+	/* Reserved: 0x50 */
+	MAX77802_REG_BUCK8CTRL		= 0x51,
+	MAX77802_REG_BUCK8OUT		= 0x52,
+	/* Reserved: 0x53 */
+	MAX77802_REG_BUCK9CTRL		= 0x54,
+	MAX77802_REG_BUCK9OUT		= 0x55,
+	/* Reserved: 0x56 */
+	MAX77802_REG_BUCK10CTRL		= 0x57,
+	MAX77802_REG_BUCK10OUT		= 0x58,
+
+	/* Reserved: 0x59-0x5F */
+
+	MAX77802_REG_LDO1CTRL1		= 0x60,
+	MAX77802_REG_LDO2CTRL1		= 0x61,
+	MAX77802_REG_LDO3CTRL1		= 0x62,
+	MAX77802_REG_LDO4CTRL1		= 0x63,
+	MAX77802_REG_LDO5CTRL1		= 0x64,
+	MAX77802_REG_LDO6CTRL1		= 0x65,
+	MAX77802_REG_LDO7CTRL1		= 0x66,
+	MAX77802_REG_LDO8CTRL1		= 0x67,
+	MAX77802_REG_LDO9CTRL1		= 0x68,
+	MAX77802_REG_LDO10CTRL1		= 0x69,
+	MAX77802_REG_LDO11CTRL1		= 0x6A,
+	MAX77802_REG_LDO12CTRL1		= 0x6B,
+	MAX77802_REG_LDO13CTRL1		= 0x6C,
+	MAX77802_REG_LDO14CTRL1		= 0x6D,
+	MAX77802_REG_LDO15CTRL1		= 0x6E,
+	/* Reserved: 0x6F */
+	MAX77802_REG_LDO17CTRL1		= 0x70,
+	MAX77802_REG_LDO18CTRL1		= 0x71,
+	MAX77802_REG_LDO19CTRL1		= 0x72,
+	MAX77802_REG_LDO20CTRL1		= 0x73,
+	MAX77802_REG_LDO21CTRL1		= 0x74,
+	MAX77802_REG_LDO22CTRL1		= 0x75,
+	MAX77802_REG_LDO23CTRL1		= 0x76,
+	MAX77802_REG_LDO24CTRL1		= 0x77,
+	MAX77802_REG_LDO25CTRL1		= 0x78,
+	MAX77802_REG_LDO26CTRL1		= 0x79,
+	MAX77802_REG_LDO27CTRL1		= 0x7A,
+	MAX77802_REG_LDO28CTRL1		= 0x7B,
+	MAX77802_REG_LDO29CTRL1		= 0x7C,
+	MAX77802_REG_LDO30CTRL1		= 0x7D,
+	/* Reserved: 0x7E */
+	MAX77802_REG_LDO32CTRL1		= 0x7F,
+	MAX77802_REG_LDO33CTRL1		= 0x80,
+	MAX77802_REG_LDO34CTRL1		= 0x81,
+	MAX77802_REG_LDO35CTRL1		= 0x82,
+	/* Reserved: 0x83-0x8F */
+	MAX77802_REG_LDO1CTRL2		= 0x90,
+	MAX77802_REG_LDO2CTRL2		= 0x91,
+	MAX77802_REG_LDO3CTRL2		= 0x92,
+	MAX77802_REG_LDO4CTRL2		= 0x93,
+	MAX77802_REG_LDO5CTRL2		= 0x94,
+	MAX77802_REG_LDO6CTRL2		= 0x95,
+	MAX77802_REG_LDO7CTRL2		= 0x96,
+	MAX77802_REG_LDO8CTRL2		= 0x97,
+	MAX77802_REG_LDO9CTRL2		= 0x98,
+	MAX77802_REG_LDO10CTRL2		= 0x99,
+	MAX77802_REG_LDO11CTRL2		= 0x9A,
+	MAX77802_REG_LDO12CTRL2		= 0x9B,
+	MAX77802_REG_LDO13CTRL2		= 0x9C,
+	MAX77802_REG_LDO14CTRL2		= 0x9D,
+	MAX77802_REG_LDO15CTRL2		= 0x9E,
+	/* Reserved: 0x9F */
+	MAX77802_REG_LDO17CTRL2		= 0xA0,
+	MAX77802_REG_LDO18CTRL2		= 0xA1,
+	MAX77802_REG_LDO19CTRL2		= 0xA2,
+	MAX77802_REG_LDO20CTRL2		= 0xA3,
+	MAX77802_REG_LDO21CTRL2		= 0xA4,
+	MAX77802_REG_LDO22CTRL2		= 0xA5,
+	MAX77802_REG_LDO23CTRL2		= 0xA6,
+	MAX77802_REG_LDO24CTRL2		= 0xA7,
+	MAX77802_REG_LDO25CTRL2		= 0xA8,
+	MAX77802_REG_LDO26CTRL2		= 0xA9,
+	MAX77802_REG_LDO27CTRL2		= 0xAA,
+	MAX77802_REG_LDO28CTRL2		= 0xAB,
+	MAX77802_REG_LDO29CTRL2		= 0xAC,
+	MAX77802_REG_LDO30CTRL2		= 0xAD,
+	/* Reserved: 0xAE */
+	MAX77802_REG_LDO32CTRL2		= 0xAF,
+	MAX77802_REG_LDO33CTRL2		= 0xB0,
+	MAX77802_REG_LDO34CTRL2		= 0xB1,
+	MAX77802_REG_LDO35CTRL2		= 0xB2,
+	/* Reserved: 0xB3 */
+
+	MAX77802_REG_BBAT_CHG		= 0xB4,
+	MAX77802_REG_32KHZ		= 0xB5,
+
+	MAX77802_REG_PMIC_END		= 0xB6,
+};
+
+enum max77802_rtc_reg {
+	MAX77802_RTC_INT		= 0xC0,
+	MAX77802_RTC_INTM		= 0xC1,
+	MAX77802_RTC_CONTROLM		= 0xC2,
+	MAX77802_RTC_CONTROL		= 0xC3,
+	MAX77802_RTC_UPDATE0		= 0xC4,
+	MAX77802_RTC_UPDATE1		= 0xC5,
+	MAX77802_WTSR_SMPL_CNTL		= 0xC6,
+	MAX77802_RTC_SEC		= 0xC7,
+	MAX77802_RTC_MIN		= 0xC8,
+	MAX77802_RTC_HOUR		= 0xC9,
+	MAX77802_RTC_WEEKDAY		= 0xCA,
+	MAX77802_RTC_MONTH		= 0xCB,
+	MAX77802_RTC_YEAR		= 0xCC,
+	MAX77802_RTC_DATE		= 0xCD,
+	MAX77802_RTC_AE1		= 0xCE,
+	MAX77802_ALARM1_SEC		= 0xCF,
+	MAX77802_ALARM1_MIN		= 0xD0,
+	MAX77802_ALARM1_HOUR		= 0xD1,
+	MAX77802_ALARM1_WEEKDAY		= 0xD2,
+	MAX77802_ALARM1_MONTH		= 0xD3,
+	MAX77802_ALARM1_YEAR		= 0xD4,
+	MAX77802_ALARM1_DATE		= 0xD5,
+	MAX77802_RTC_AE2		= 0xD6,
+	MAX77802_ALARM2_SEC		= 0xD7,
+	MAX77802_ALARM2_MIN		= 0xD8,
+	MAX77802_ALARM2_HOUR		= 0xD9,
+	MAX77802_ALARM2_WEEKDAY		= 0xDA,
+	MAX77802_ALARM2_MONTH		= 0xDB,
+	MAX77802_ALARM2_YEAR		= 0xDC,
+	MAX77802_ALARM2_DATE		= 0xDD,
+
+	MAX77802_RTC_END		= 0xDF,
+};
+
+#define MAX77802_IRQSRC_PMIC            (0)
+#define MAX77802_IRQSRC_RTC	        (1 << 0)
+
+enum max77802_irq_source {
+	PMIC_INT1 = 0,
+	PMIC_INT2,
+	RTC_INT,
+
+	MAX77802_IRQ_GROUP_NR,
+};
+
+enum max77802_irq {
+	MAX77802_PMICIRQ_PWRONF,
+	MAX77802_PMICIRQ_PWRONR,
+	MAX77802_PMICIRQ_JIGONBF,
+	MAX77802_PMICIRQ_JIGONBR,
+	MAX77802_PMICIRQ_ACOKBF,
+	MAX77802_PMICIRQ_ACOKBR,
+	MAX77802_PMICIRQ_ONKEY1S,
+	MAX77802_PMICIRQ_MRSTB,
+
+	MAX77802_PMICIRQ_140C,
+	MAX77802_PMICIRQ_120C,
+
+	MAX77802_RTCIRQ_RTC60S,
+	MAX77802_RTCIRQ_RTCA1,
+	MAX77802_RTCIRQ_RTCA2,
+	MAX77802_RTCIRQ_SMPL,
+	MAX77802_RTCIRQ_RTC1S,
+	MAX77802_RTCIRQ_WTSR,
+
+	MAX77802_IRQ_NR,
+};
+
+struct max77802_dev {
+	struct device *dev;
+	struct i2c_client *i2c; /* 0x09 / PMIC, Battery Control and RTC */
+
+	int type;
+
+	struct regmap *regmap;		/* regmap for mfd and rtc devices */
+
+	struct irq_domain *irq_domain;
+
+	int irq;
+	int irq_gpio;
+	bool wakeup;
+	struct mutex irqlock;
+	int irq_masks_cur[MAX77802_IRQ_GROUP_NR];
+	int irq_masks_cache[MAX77802_IRQ_GROUP_NR];
+};
+
+enum max77802_types {
+	TYPE_MAX77802,
+};
+
+extern int max77802_irq_init(struct max77802_dev *max77802);
+extern void max77802_irq_exit(struct max77802_dev *max77802);
+extern int max77802_irq_resume(struct max77802_dev *max77802);
+
+#endif /*  __LINUX_MFD_MAX77802_PRIV_H */
diff --git a/include/linux/mfd/max77802.h b/include/linux/mfd/max77802.h
new file mode 100644
index 0000000..3c08b2a
--- /dev/null
+++ b/include/linux/mfd/max77802.h
@@ -0,0 +1,124 @@
+/*
+ * max77802.h - Driver for the Maxim 77802
+ *
+ * Copyright (c) 2013-2014 Google, Inc
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  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.
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77802 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77802_H
+#define __LINUX_MFD_MAX77802_H
+
+#include <linux/regulator/consumer.h>
+
+/* MAX77802 regulator IDs - LDOS must come before BUCKs */
+enum max77802_regulators {
+	MAX77802_LDO1 = 0,
+	MAX77802_LDO2,
+	MAX77802_LDO3,
+	MAX77802_LDO4,
+	MAX77802_LDO5,
+	MAX77802_LDO6,
+	MAX77802_LDO7,
+	MAX77802_LDO8,
+	MAX77802_LDO9,
+	MAX77802_LDO10,
+	MAX77802_LDO11,
+	MAX77802_LDO12,
+	MAX77802_LDO13,
+	MAX77802_LDO14,
+	MAX77802_LDO15,
+	MAX77802_LDO16,
+	MAX77802_LDO17,
+	MAX77802_LDO18,
+	MAX77802_LDO19,
+	MAX77802_LDO20,
+	MAX77802_LDO21,
+	MAX77802_LDO22,
+	MAX77802_LDO23,
+	MAX77802_LDO24,
+	MAX77802_LDO25,
+	MAX77802_LDO26,
+	MAX77802_LDO27,
+	MAX77802_LDO28,
+	MAX77802_LDO29,
+	MAX77802_LDO30,
+	MAX77802_LDO31,
+	MAX77802_LDO32,
+	MAX77802_LDO33,
+	MAX77802_LDO34,
+	MAX77802_LDO35,
+	MAX77802_BUCK1,
+	MAX77802_BUCK2,
+	MAX77802_BUCK3,
+	MAX77802_BUCK4,
+	MAX77802_BUCK5,
+	MAX77802_BUCK6,
+	MAX77802_BUCK7,
+	MAX77802_BUCK8,
+	MAX77802_BUCK9,
+	MAX77802_BUCK10,
+
+	MAX77802_REG_MAX,
+};
+
+struct max77802_regulator_data {
+	int id;
+	int opmode;
+	struct regulator_init_data *initdata;
+	struct device_node *of_node;
+};
+
+enum max77802_opmode {
+	MAX77802_OPMODE_OFF,
+	MAX77802_OPMODE_STANDBY,
+	MAX77802_OPMODE_LP,
+	MAX77802_OPMODE_NORMAL,
+};
+
+struct max77802_opmode_data {
+	int id;
+	int mode;
+};
+
+struct max77802_platform_data {
+	/* IRQ */
+	int irq_gpio;
+	int ono;
+	int wakeup;
+
+	/* ---- PMIC ---- */
+	struct max77802_regulator_data *regulators;
+	int num_regulators;
+
+	struct max77802_opmode_data *opmode_data;
+
+	/*
+	 * GPIO-DVS feature is not fully enabled with the current version of
+	 * MAX77802 driver, but the driver does support using a DVS index other
+	 * than the default of 0.
+	 */
+	int buck_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
+	int buck_default_idx; /* Default value of DVS1, 2, 3 */
+
+	int buck_gpio_selb[5]; /* 77802: 1, 2, 3, 4, 6 */
+};
+
+#endif /* __LINUX_MFD_MAX77802_H */
-- 
2.0.0.rc2


WARNING: multiple messages have this Message-ID
From: javier.martinez@collabora.co.uk (Javier Martinez Canillas)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
Date: Mon,  9 Jun 2014 11:37:46 +0200	[thread overview]
Message-ID: <1402306670-17041-2-git-send-email-javier.martinez@collabora.co.uk> (raw)
In-Reply-To: <1402306670-17041-1-git-send-email-javier.martinez@collabora.co.uk>

Maxim MAX77802 is a power management chip that contains 10 high
efficiency Buck regulators, 32 Low-dropout (LDO) regulators used
to power up application processors and peripherals, a 2-channel
32kHz clock outputs, a Real-Time-Clock (RTC) and a I2C interface
to program the individual regulators, clocks outputs and the RTC.

This patch adds the core support for MAX77802 PMIC and is based
on a driver added by Simon Glass to the Chrome OS kernel 3.8 tree.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 Documentation/devicetree/bindings/mfd/max77802.txt |  88 ++++++
 drivers/mfd/Kconfig                                |  13 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/max77802-irq.c                         | 332 +++++++++++++++++++++
 drivers/mfd/max77802.c                             | 282 +++++++++++++++++
 include/linux/mfd/max77802-private.h               | 291 ++++++++++++++++++
 include/linux/mfd/max77802.h                       | 124 ++++++++
 7 files changed, 1131 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/max77802.txt
 create mode 100644 drivers/mfd/max77802-irq.c
 create mode 100644 drivers/mfd/max77802.c
 create mode 100644 include/linux/mfd/max77802-private.h
 create mode 100644 include/linux/mfd/max77802.h

diff --git a/Documentation/devicetree/bindings/mfd/max77802.txt b/Documentation/devicetree/bindings/mfd/max77802.txt
new file mode 100644
index 0000000..addf02e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max77802.txt
@@ -0,0 +1,88 @@
+Maxim MAX77802 multi-function device
+
+MAX77802 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
+interfaced to host controller using i2c interface. PMIC, Charger and RTC
+submodules are addressed using same i2c slave address
+
+This document describes the binding for mfd device and PMIC submodule.
+
+Binding for the built-in 32k clock generator block is defined separately
+in bindings/clk/maxim,max77802.txt file.
+
+Required properties:
+- compatible : Must be "maxim,max77802";
+- reg : Specifies the i2c slave address of PMIC block.
+- interrupts : This i2c device has an IRQ line connected to the main SoC.
+- interrupt-parent : The parent interrupt controller.
+
+Optional properties:
+- max77802,pmic-buck-default-dvs-idx: We'll always write this DVS index in the
+  PMIC for BUCKs with DVS (Bucks 1-4, 6).
+  NOTE: at the moment these bindings don't include enough details for actual
+  GPIO-DVS--this just lets you choose which single slot to use.
+
+- max77802,pmic-buck-dvs-gpios: The DVS GPIOs. We'll try to set these GPIOs
+  to match pmic-buck-default-dvs-idx at probe time if they are defined. If
+  some or all of these GPIOs are not defined it's assumed that the board has
+  any missing GPIOs hardwired to match pmic-buck-default-dvs-idx.
+
+- max77802,pmic-buck-selb-gpios: GPIOs to enable DVS-GPIO for BUCKs.
+  Should be five values: 1, 2, 3, 4, 6.  It is strongly suggested to include
+  these GPIOs if there's any chance that changing DVS GPIOs one line at a
+  time might glitch your DVS values.
+
+Optional node:
+- voltage-regulators : The regulators of max77802 have to be instantiated
+  under subnode named "voltage-regulators" using the following format.
+
+	regulator_name {
+		regulator-compatible = LDOn/BUCKn
+		standard regulator constraints....
+	};
+	refer Documentation/devicetree/bindings/regulator/regulator.txt
+
+  The regulator-compatible property of regulator should initialized with string
+to get matched with their hardware counterparts as follow:
+
+	-LDOn 	:	for LDOs, where n can lie in range 1 to 35.
+			example: LDO1, LDO2, LDO35.
+	-BUCKn 	:	for BUCKs, where n can lie in range 1 to 10.
+			example: BUCK1, BUCK5, BUCK10.
+Example:
+
+	max77802 at 09 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&wakeup_eint>;
+		interrupts = <26 0>;
+		reg = <0x09>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		max77802,pmic-buck-default-dvs-idx = <1>;
+		max77802,pmic-buck-dvs-gpios = <&gpy7 6 0>,
+					       <&gpj4 2 0>,
+					       <&gpj4 3 0>;
+		max77802,pmic-buck-selb-gpios = <&gph0 2 0>,
+						<&gph0 3 0>,
+						<&gph0 4 0>,
+						<&gph0 5 0>,
+						<&gph0 6 0>;
+
+		voltage-regulators {
+			ldo11_reg {
+				regulator-compatible = "LDO11";
+				regulator-name = "vdd_ldo11";
+				regulator-min-microvolt = <1900000>;
+				regulator-max-microvolt = <1900000>;
+				regulator-always-on;
+			};
+
+			buck1_reg {
+				regulator-compatible = "BUCK1";
+				regulator-name = "vdd_mif";
+				regulator-min-microvolt = <950000>;
+				regulator-max-microvolt = <1300000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+	};
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ee8204c..b8df9e4 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -392,6 +392,19 @@ config MFD_MAX77693
 	  additional drivers must be enabled in order to use the functionality
 	  of the device.
 
+config MFD_MAX77802
+	bool "Maxim Integrated MAX77802 PMIC Support"
+	depends on I2C=y
+	select MFD_CORE
+	select REGMAP_I2C
+	select IRQ_DOMAIN
+	help
+	  Say yes here to support for Maxim Integrated MAX77802.
+	  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_MAX8907
 	tristate "Maxim Semiconductor MAX8907 PMIC Support"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8afedba..1a2a7ae 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -117,6 +117,7 @@ obj-$(CONFIG_MFD_DA9063)	+= da9063.o
 obj-$(CONFIG_MFD_MAX14577)	+= max14577.o
 obj-$(CONFIG_MFD_MAX77686)	+= max77686.o max77686-irq.o
 obj-$(CONFIG_MFD_MAX77693)	+= max77693.o max77693-irq.o
+obj-$(CONFIG_MFD_MAX77802)	+= max77802.o max77802-irq.o
 obj-$(CONFIG_MFD_MAX8907)	+= max8907.o
 max8925-objs			:= max8925-core.o max8925-i2c.o
 obj-$(CONFIG_MFD_MAX8925)	+= max8925.o
diff --git a/drivers/mfd/max77802-irq.c b/drivers/mfd/max77802-irq.c
new file mode 100644
index 0000000..38a8ce7
--- /dev/null
+++ b/drivers/mfd/max77802-irq.c
@@ -0,0 +1,332 @@
+/*
+ * max77802-irq.c - Interrupt controller support for MAX77802
+ *
+ * Copyright (C) 2013-2014 Google, Inc
+ *
+ * Copyright (C) 2012 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.
+ *
+ * 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/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+enum {
+	MAX77802_DEBUG_IRQ_INFO = 1 << 0,
+	MAX77802_DEBUG_IRQ_MASK = 1 << 1,
+	MAX77802_DEBUG_IRQ_INT = 1 << 2,
+};
+
+static int debug_mask = 0;
+module_param(debug_mask, int, 0);
+MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO  0x2=IRQ_MASK 0x4=IRQ_INI)");
+
+static const u8 max77802_mask_reg[] = {
+	[PMIC_INT1] = MAX77802_REG_INT1MSK,
+	[PMIC_INT2] = MAX77802_REG_INT2MSK,
+	[RTC_INT] = MAX77802_RTC_INTM,
+};
+
+struct max77802_irq_data {
+	int mask;
+	enum max77802_irq_source group;
+};
+
+#define DECLARE_IRQ(idx, _group, _mask)		\
+	[(idx)] = { .group = (_group), .mask = (_mask) }
+static const struct max77802_irq_data max77802_irqs[] = {
+	DECLARE_IRQ(MAX77802_PMICIRQ_PWRONF,	PMIC_INT1, 1 << 0),
+	DECLARE_IRQ(MAX77802_PMICIRQ_PWRONR,	PMIC_INT1, 1 << 1),
+	DECLARE_IRQ(MAX77802_PMICIRQ_JIGONBF,	PMIC_INT1, 1 << 2),
+	DECLARE_IRQ(MAX77802_PMICIRQ_JIGONBR,	PMIC_INT1, 1 << 3),
+	DECLARE_IRQ(MAX77802_PMICIRQ_ACOKBF,	PMIC_INT1, 1 << 4),
+	DECLARE_IRQ(MAX77802_PMICIRQ_ACOKBR,	PMIC_INT1, 1 << 5),
+	DECLARE_IRQ(MAX77802_PMICIRQ_ONKEY1S,	PMIC_INT1, 1 << 6),
+	DECLARE_IRQ(MAX77802_PMICIRQ_MRSTB,		PMIC_INT1, 1 << 7),
+	DECLARE_IRQ(MAX77802_PMICIRQ_140C,		PMIC_INT2, 1 << 0),
+	DECLARE_IRQ(MAX77802_PMICIRQ_120C,		PMIC_INT2, 1 << 1),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTC60S,		RTC_INT, 1 << 0),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTCA1,		RTC_INT, 1 << 1),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTCA2,		RTC_INT, 1 << 2),
+	DECLARE_IRQ(MAX77802_RTCIRQ_SMPL,		RTC_INT, 1 << 3),
+	DECLARE_IRQ(MAX77802_RTCIRQ_RTC1S,		RTC_INT, 1 << 4),
+	DECLARE_IRQ(MAX77802_RTCIRQ_WTSR,		RTC_INT, 1 << 5),
+};
+
+static void max77802_irq_lock(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+		pr_info("%s\n", __func__);
+
+	mutex_lock(&max77802->irqlock);
+}
+
+static void max77802_irq_sync_unlock(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+	int i;
+
+	for (i = 0; i < MAX77802_IRQ_GROUP_NR; i++) {
+		u8 mask_reg = max77802_mask_reg[i];
+
+		if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+			pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
+			__func__, i, mask_reg, max77802->irq_masks_cur[i]);
+
+		if (mask_reg == MAX77802_REG_INVALID ||
+				IS_ERR_OR_NULL(max77802->regmap))
+			continue;
+
+		max77802->irq_masks_cache[i] = max77802->irq_masks_cur[i];
+
+		regmap_write(max77802->regmap, mask_reg,
+			     max77802->irq_masks_cur[i]);
+	}
+
+	mutex_unlock(&max77802->irqlock);
+}
+
+static const inline struct max77802_irq_data *to_max77802_irq(int irq)
+{
+	struct irq_data *data = irq_get_irq_data(irq);
+	return &max77802_irqs[data->hwirq];
+}
+
+static void max77802_irq_mask(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+	const struct max77802_irq_data *irq_data = to_max77802_irq(data->irq);
+
+	max77802->irq_masks_cur[irq_data->group] |= irq_data->mask;
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+		pr_info("%s: group=%d, cur=0x%x\n",
+			__func__, irq_data->group,
+			max77802->irq_masks_cur[irq_data->group]);
+}
+
+static void max77802_irq_unmask(struct irq_data *data)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+	const struct max77802_irq_data *irq_data = to_max77802_irq(data->irq);
+
+	max77802->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_MASK)
+		pr_info("%s: group=%d, cur=0x%x\n",
+			__func__, irq_data->group,
+			max77802->irq_masks_cur[irq_data->group]);
+}
+
+static int max77802_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+	struct max77802_dev *max77802 = irq_get_chip_data(data->irq);
+
+	if (device_may_wakeup(max77802->dev)) {
+		if (on)
+			return enable_irq_wake(max77802->irq);
+		else
+			return disable_irq_wake(max77802->irq);
+	} else if (on) {
+		dev_warn(max77802->dev,
+			 "Child requested wakeup but wakeup disabled\n");
+		return -ENXIO;
+	}
+	return 0;
+}
+
+static struct irq_chip max77802_irq_chip = {
+	.name			= "max77802",
+	.irq_bus_lock		= max77802_irq_lock,
+	.irq_bus_sync_unlock	= max77802_irq_sync_unlock,
+	.irq_mask		= max77802_irq_mask,
+	.irq_unmask		= max77802_irq_unmask,
+	.irq_set_wake		= max77802_irq_set_wake,
+};
+
+static irqreturn_t max77802_irq_thread(int irq, void *data)
+{
+	struct max77802_dev *max77802 = data;
+	unsigned int irq_reg[MAX77802_IRQ_GROUP_NR] = {};
+	unsigned int irq_src;
+	int ret;
+	int i, cur_irq;
+
+	ret = regmap_read(max77802->regmap,  MAX77802_REG_INTSRC, &irq_src);
+	if (ret < 0) {
+		dev_err(max77802->dev, "Failed to read interrupt source: %d\n",
+				ret);
+		return IRQ_NONE;
+	}
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_INT)
+		pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
+
+	if (irq_src == MAX77802_IRQSRC_PMIC) {
+		ret = regmap_bulk_read(max77802->regmap,
+					 MAX77802_REG_INT1, irq_reg, 2);
+		if (ret < 0) {
+			dev_err(max77802->dev, "Failed to read interrupt source: %d\n",
+					ret);
+			return IRQ_NONE;
+		}
+
+		if (debug_mask & MAX77802_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 & MAX77802_IRQSRC_RTC) {
+		ret = regmap_read(max77802->regmap,
+				  MAX77802_RTC_INT, &irq_reg[RTC_INT]);
+		if (ret < 0) {
+			dev_err(max77802->dev, "Failed to read interrupt source: %d\n",
+					ret);
+			return IRQ_NONE;
+		}
+
+		if (debug_mask & MAX77802_DEBUG_IRQ_INT)
+			pr_info("%s: rtc int=0x%x\n", __func__,
+				irq_reg[RTC_INT]);
+
+	}
+
+	for (i = 0; i < MAX77802_IRQ_GROUP_NR; i++)
+		irq_reg[i] &= ~max77802->irq_masks_cur[i];
+
+	for (i = 0; i < MAX77802_IRQ_NR; i++) {
+		if (irq_reg[max77802_irqs[i].group] & max77802_irqs[i].mask) {
+			cur_irq = irq_find_mapping(max77802->irq_domain, i);
+			if (cur_irq)
+				handle_nested_irq(cur_irq);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int max77802_irq_domain_map(struct irq_domain *d, unsigned int irq,
+				   irq_hw_number_t hw)
+{
+	struct max77802_dev *max77802 = d->host_data;
+
+	irq_set_chip_data(irq, max77802);
+	irq_set_chip_and_handler(irq, &max77802_irq_chip, handle_simple_irq);
+	irq_set_nested_thread(irq, 1);
+	irq_set_parent(irq, max77802->irq);
+#ifdef CONFIG_ARM
+	set_irq_flags(irq, IRQF_VALID);
+#else
+	irq_set_noprobe(irq);
+#endif
+	return 0;
+}
+
+static struct irq_domain_ops max77802_irq_domain_ops = {
+	.map = max77802_irq_domain_map,
+};
+
+int max77802_irq_init(struct max77802_dev *max77802)
+{
+	struct irq_domain *domain;
+	int i;
+	int ret;
+	int val;
+
+	mutex_init(&max77802->irqlock);
+
+	if (max77802->irq_gpio && !max77802->irq) {
+		max77802->irq = gpio_to_irq(max77802->irq_gpio);
+
+		if (debug_mask & MAX77802_DEBUG_IRQ_INT) {
+			ret = gpio_request(max77802->irq_gpio, "pmic_irq");
+			if (ret < 0) {
+				dev_err(max77802->dev,
+					"Failed to request gpio %d with ret:"
+					"%d\n",	max77802->irq_gpio, ret);
+				return IRQ_NONE;
+			}
+
+			gpio_direction_input(max77802->irq_gpio);
+			val = gpio_get_value(max77802->irq_gpio);
+			gpio_free(max77802->irq_gpio);
+			pr_info("%s: gpio_irq=%x\n", __func__, val);
+		}
+	}
+
+	if (!max77802->irq) {
+		dev_err(max77802->dev, "irq is not specified\n");
+		return -ENODEV;
+	}
+
+	/* Mask individual interrupt sources */
+	for (i = 0; i < MAX77802_IRQ_GROUP_NR; i++) {
+		max77802->irq_masks_cur[i] = 0xff;
+		max77802->irq_masks_cache[i] = 0xff;
+
+		if (IS_ERR_OR_NULL(max77802->regmap))
+			continue;
+		if (max77802_mask_reg[i] == MAX77802_REG_INVALID)
+			continue;
+
+		regmap_write(max77802->regmap, max77802_mask_reg[i], 0xff);
+	}
+	domain = irq_domain_add_linear(NULL, MAX77802_IRQ_NR,
+					&max77802_irq_domain_ops, max77802);
+	if (!domain) {
+		dev_err(max77802->dev, "could not create irq domain\n");
+		return -ENODEV;
+	}
+	max77802->irq_domain = domain;
+
+	ret = request_threaded_irq(max77802->irq, NULL, max77802_irq_thread,
+				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				   "max77802-irq", max77802);
+
+	if (ret)
+		dev_err(max77802->dev, "Failed to request IRQ %d: %d\n",
+			max77802->irq, ret);
+
+
+	if (debug_mask & MAX77802_DEBUG_IRQ_INFO)
+		pr_info("%s-\n", __func__);
+
+	return 0;
+}
+
+void max77802_irq_exit(struct max77802_dev *max77802)
+{
+	if (max77802->irq)
+		free_irq(max77802->irq, max77802);
+}
+
+int max77802_irq_resume(struct max77802_dev *max77802)
+{
+	/*
+	 * The IRQ that woke us up may still need to be ACK'ed on resume.
+	 * If it isn't ever ACK'ed, future IRQs may not be delivered.
+	 */
+	if (max77802->irq)
+		max77802_irq_thread(0, max77802);
+
+	return 0;
+}
diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
new file mode 100644
index 0000000..59696dd
--- /dev/null
+++ b/drivers/mfd/max77802.c
@@ -0,0 +1,282 @@
+/*
+ * max77802.c - mfd core driver for the Maxim 77802
+ *
+ * Copyright (C) 2013-2014 Google, Inc
+ * Simon Glass <sjg@chromium.org>
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Chiwoong Byun <woong.byun@smasung.com>
+ * Jonghwa Lee <jonghwa3.lee@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.
+ *
+ * This driver is based on max8997.c
+ */
+
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+
+static const struct mfd_cell max77802_devs[] = {
+	{ .name = "max77802-pmic", },
+};
+
+static bool max77802_pmic_is_accessible_reg(struct device *dev,
+					    unsigned int reg)
+{
+	return (reg >= MAX77802_REG_DEVICE_ID && reg < MAX77802_REG_PMIC_END);
+}
+
+static bool max77802_rtc_is_accessible_reg(struct device *dev,
+					   unsigned int reg)
+{
+	return (reg >= MAX77802_RTC_INT && reg < MAX77802_RTC_END);
+}
+
+static bool max77802_is_accessible_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_pmic_is_accessible_reg(dev, reg) ||
+		max77802_rtc_is_accessible_reg(dev, reg));
+}
+
+static bool max77802_pmic_is_precious_reg(struct device *dev, unsigned int reg)
+{
+	return (reg == MAX77802_REG_INTSRC || reg == MAX77802_REG_INT1 ||
+		reg == MAX77802_REG_INT2);
+}
+
+static bool max77802_rtc_is_precious_reg(struct device *dev, unsigned int reg)
+{
+	return (reg == MAX77802_RTC_INT ||
+		reg == MAX77802_RTC_UPDATE0 ||
+		reg == MAX77802_RTC_UPDATE1);
+}
+
+static bool max77802_is_precious_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_pmic_is_precious_reg(dev, reg) ||
+		max77802_rtc_is_precious_reg(dev, reg));
+}
+
+static bool max77802_pmic_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_is_precious_reg(dev, reg) ||
+		reg == MAX77802_REG_STATUS1 || reg == MAX77802_REG_STATUS2 ||
+		reg == MAX77802_REG_PWRON);
+}
+
+static bool max77802_rtc_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_rtc_is_precious_reg(dev, reg) ||
+		reg == MAX77802_RTC_SEC ||
+		reg == MAX77802_RTC_MIN ||
+		reg == MAX77802_RTC_HOUR ||
+		reg == MAX77802_RTC_WEEKDAY ||
+		reg == MAX77802_RTC_MONTH ||
+		reg == MAX77802_RTC_YEAR ||
+		reg == MAX77802_RTC_DATE);
+}
+
+static bool max77802_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return (max77802_pmic_is_volatile_reg(dev, reg) ||
+		max77802_rtc_is_volatile_reg(dev, reg));
+}
+
+static struct regmap_config max77802_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = max77802_is_accessible_reg,
+	.readable_reg = max77802_is_accessible_reg,
+	.precious_reg = max77802_is_precious_reg,
+	.volatile_reg = max77802_is_volatile_reg,
+	.name = "max77802-pmic",
+	.cache_type = REGCACHE_RBTREE,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id max77802_pmic_dt_match[] = {
+	{.compatible = "maxim,max77802", .data = NULL},
+	{},
+};
+
+static void max77802_dt_parse_dvs_gpio(struct device *dev,
+				       struct max77802_platform_data *pd,
+				       struct device_node *np)
+{
+	int i;
+
+	/*
+	 * NOTE: we don't consider GPIO errors fatal; board may have some lines
+	 * directly pulled high or low and thus doesn't specify them.
+	 */
+	for (i = 0; i < ARRAY_SIZE(pd->buck_gpio_dvs); i++)
+		pd->buck_gpio_dvs[i] =
+			of_get_named_gpio(np,
+					  "max77802,pmic-buck-dvs-gpios", i);
+
+	for (i = 0; i < ARRAY_SIZE(pd->buck_gpio_selb); i++)
+		pd->buck_gpio_selb[i] =
+			of_get_named_gpio(np,
+					  "max77802,pmic-buck-selb-gpios", i);
+}
+
+static struct max77802_platform_data *max77802_i2c_parse_dt_pdata(struct device
+								  *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct max77802_platform_data *pd;
+
+	pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd) {
+		dev_err(dev, "could not allocate memory for pdata\n");
+		return NULL;
+	}
+
+	/* Read default index and ignore errors, since default is 0 */
+	of_property_read_u32(np, "max77802,pmic-buck-default-dvs-idx",
+			     &pd->buck_default_idx);
+
+	max77802_dt_parse_dvs_gpio(dev, pd, np);
+
+	dev->platform_data = pd;
+	return pd;
+}
+#else
+static struct max77802_platform_data *max77802_i2c_parse_dt_pdata(struct device
+								  *dev)
+{
+	return 0;
+}
+#endif
+
+static int max77802_i2c_probe(struct i2c_client *i2c,
+			      const struct i2c_device_id *id)
+{
+	struct max77802_dev *max77802 = NULL;
+	struct max77802_platform_data *pdata = dev_get_platdata(&i2c->dev);
+	unsigned int data;
+	int ret = 0;
+
+	if (i2c->dev.of_node)
+		pdata = max77802_i2c_parse_dt_pdata(&i2c->dev);
+
+	if (!pdata) {
+		dev_err(&i2c->dev, "No platform data found.\n");
+		return -EIO;
+	}
+
+	max77802 = devm_kzalloc(&i2c->dev, sizeof(struct max77802_dev),
+				GFP_KERNEL);
+	if (max77802 == NULL)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, max77802);
+	max77802->dev = &i2c->dev;
+	max77802->i2c = i2c;
+	max77802->type = id->driver_data;
+
+	max77802->wakeup = pdata->wakeup;
+	max77802->irq_gpio = pdata->irq_gpio;
+	max77802->irq = i2c->irq;
+
+	max77802->regmap = devm_regmap_init_i2c(i2c, &max77802_regmap_config);
+	if (IS_ERR(max77802->regmap)) {
+		ret = PTR_ERR(max77802->regmap);
+		dev_err(max77802->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (regmap_read(max77802->regmap,
+			 MAX77802_REG_DEVICE_ID, &data) < 0) {
+		dev_err(max77802->dev,
+			"device not found on this channel (this is not an error)\n");
+		return -ENODEV;
+	} else {
+		dev_info(max77802->dev, "device found\n");
+	}
+
+	max77802_irq_init(max77802);
+
+	ret = mfd_add_devices(max77802->dev, -1, max77802_devs,
+			      ARRAY_SIZE(max77802_devs), NULL, 0, NULL);
+	if (ret < 0)
+		mfd_remove_devices(max77802->dev);
+
+	return ret;
+}
+
+static int max77802_i2c_remove(struct i2c_client *i2c)
+{
+	struct max77802_dev *max77802 = i2c_get_clientdata(i2c);
+
+	mfd_remove_devices(max77802->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id max77802_i2c_id[] = {
+	{ "max77802", TYPE_MAX77802 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max77802_i2c_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int max77802_resume(struct device *dev)
+{
+	struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+	struct max77802_dev *max77802 = i2c_get_clientdata(i2c);
+
+	max77802_irq_resume(max77802);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77802_pm_ops, NULL, max77802_resume);
+
+static struct i2c_driver max77802_i2c_driver = {
+	.driver = {
+		   .name = "max77802",
+		   .owner = THIS_MODULE,
+		   .pm = &max77802_pm_ops,
+		   .of_match_table = of_match_ptr(max77802_pmic_dt_match),
+	},
+	.probe = max77802_i2c_probe,
+	.remove = max77802_i2c_remove,
+	.id_table = max77802_i2c_id,
+};
+
+static int __init max77802_i2c_init(void)
+{
+	return i2c_add_driver(&max77802_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(max77802_i2c_init);
+
+static void __exit max77802_i2c_exit(void)
+{
+	i2c_del_driver(&max77802_i2c_driver);
+}
+module_exit(max77802_i2c_exit);
+
+MODULE_DESCRIPTION("MAXIM 77802 multi-function core driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/max77802-private.h b/include/linux/mfd/max77802-private.h
new file mode 100644
index 0000000..7db5225
--- /dev/null
+++ b/include/linux/mfd/max77802-private.h
@@ -0,0 +1,291 @@
+/*
+ * max77802-private.h - Voltage regulator driver for the Maxim 77802
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  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.
+ *
+ */
+
+#ifndef __LINUX_MFD_MAX77802_PRIV_H
+#define __LINUX_MFD_MAX77802_PRIV_H
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+
+#define MAX77802_REG_INVALID		(0xff)
+
+enum max77802_pmic_reg {
+	MAX77802_REG_DEVICE_ID		= 0x00,
+	MAX77802_REG_INTSRC		= 0x01,
+	MAX77802_REG_INT1		= 0x02,
+	MAX77802_REG_INT2		= 0x03,
+
+	MAX77802_REG_INT1MSK		= 0x04,
+	MAX77802_REG_INT2MSK		= 0x05,
+
+	MAX77802_REG_STATUS1		= 0x06,
+	MAX77802_REG_STATUS2		= 0x07,
+
+	MAX77802_REG_PWRON		= 0x08,
+	/* Reserved: 0x09 */
+	MAX77802_REG_MRSTB		= 0x0A,
+	MAX77802_REG_EPWRHOLD		= 0x0B,
+	/* Reserved: 0x0C-0x0D */
+	MAX77802_REG_BOOSTCTRL		= 0x0E,
+	MAX77802_REG_BOOSTOUT		= 0x0F,
+
+	MAX77802_REG_BUCK1CTRL		= 0x10,
+	MAX77802_REG_BUCK1DVS1		= 0x11,
+	MAX77802_REG_BUCK1DVS2		= 0x12,
+	MAX77802_REG_BUCK1DVS3		= 0x13,
+	MAX77802_REG_BUCK1DVS4		= 0x14,
+	MAX77802_REG_BUCK1DVS5		= 0x15,
+	MAX77802_REG_BUCK1DVS6		= 0x16,
+	MAX77802_REG_BUCK1DVS7		= 0x17,
+	MAX77802_REG_BUCK1DVS8		= 0x18,
+	/* Reserved: 0x19 */
+	MAX77802_REG_BUCK2CTRL1		= 0x1A,
+	MAX77802_REG_BUCK2CTRL2		= 0x1B,
+	MAX77802_REG_BUCK2PHTRAN	= 0x1C,
+	MAX77802_REG_BUCK2DVS1		= 0x1D,
+	MAX77802_REG_BUCK2DVS2		= 0x1E,
+	MAX77802_REG_BUCK2DVS3		= 0x1F,
+	MAX77802_REG_BUCK2DVS4		= 0x20,
+	MAX77802_REG_BUCK2DVS5		= 0x21,
+	MAX77802_REG_BUCK2DVS6		= 0x22,
+	MAX77802_REG_BUCK2DVS7		= 0x23,
+	MAX77802_REG_BUCK2DVS8		= 0x24,
+	/* Reserved: 0x25-0x26 */
+	MAX77802_REG_BUCK3CTRL1		= 0x27,
+	MAX77802_REG_BUCK3DVS1		= 0x28,
+	MAX77802_REG_BUCK3DVS2		= 0x29,
+	MAX77802_REG_BUCK3DVS3		= 0x2A,
+	MAX77802_REG_BUCK3DVS4		= 0x2B,
+	MAX77802_REG_BUCK3DVS5		= 0x2C,
+	MAX77802_REG_BUCK3DVS6		= 0x2D,
+	MAX77802_REG_BUCK3DVS7		= 0x2E,
+	MAX77802_REG_BUCK3DVS8		= 0x2F,
+	/* Reserved: 0x30-0x36 */
+	MAX77802_REG_BUCK4CTRL1		= 0x37,
+	MAX77802_REG_BUCK4DVS1		= 0x38,
+	MAX77802_REG_BUCK4DVS2		= 0x39,
+	MAX77802_REG_BUCK4DVS3		= 0x3A,
+	MAX77802_REG_BUCK4DVS4		= 0x3B,
+	MAX77802_REG_BUCK4DVS5		= 0x3C,
+	MAX77802_REG_BUCK4DVS6		= 0x3D,
+	MAX77802_REG_BUCK4DVS7		= 0x3E,
+	MAX77802_REG_BUCK4DVS8		= 0x3F,
+	/* Reserved: 0x40 */
+	MAX77802_REG_BUCK5CTRL		= 0x41,
+	MAX77802_REG_BUCK5OUT		= 0x42,
+	/* Reserved: 0x43 */
+	MAX77802_REG_BUCK6CTRL		= 0x44,
+	MAX77802_REG_BUCK6DVS1		= 0x45,
+	MAX77802_REG_BUCK6DVS2		= 0x46,
+	MAX77802_REG_BUCK6DVS3		= 0x47,
+	MAX77802_REG_BUCK6DVS4		= 0x48,
+	MAX77802_REG_BUCK6DVS5		= 0x49,
+	MAX77802_REG_BUCK6DVS6		= 0x4A,
+	MAX77802_REG_BUCK6DVS7		= 0x4B,
+	MAX77802_REG_BUCK6DVS8		= 0x4C,
+	/* Reserved: 0x4D */
+	MAX77802_REG_BUCK7CTRL		= 0x4E,
+	MAX77802_REG_BUCK7OUT		= 0x4F,
+	/* Reserved: 0x50 */
+	MAX77802_REG_BUCK8CTRL		= 0x51,
+	MAX77802_REG_BUCK8OUT		= 0x52,
+	/* Reserved: 0x53 */
+	MAX77802_REG_BUCK9CTRL		= 0x54,
+	MAX77802_REG_BUCK9OUT		= 0x55,
+	/* Reserved: 0x56 */
+	MAX77802_REG_BUCK10CTRL		= 0x57,
+	MAX77802_REG_BUCK10OUT		= 0x58,
+
+	/* Reserved: 0x59-0x5F */
+
+	MAX77802_REG_LDO1CTRL1		= 0x60,
+	MAX77802_REG_LDO2CTRL1		= 0x61,
+	MAX77802_REG_LDO3CTRL1		= 0x62,
+	MAX77802_REG_LDO4CTRL1		= 0x63,
+	MAX77802_REG_LDO5CTRL1		= 0x64,
+	MAX77802_REG_LDO6CTRL1		= 0x65,
+	MAX77802_REG_LDO7CTRL1		= 0x66,
+	MAX77802_REG_LDO8CTRL1		= 0x67,
+	MAX77802_REG_LDO9CTRL1		= 0x68,
+	MAX77802_REG_LDO10CTRL1		= 0x69,
+	MAX77802_REG_LDO11CTRL1		= 0x6A,
+	MAX77802_REG_LDO12CTRL1		= 0x6B,
+	MAX77802_REG_LDO13CTRL1		= 0x6C,
+	MAX77802_REG_LDO14CTRL1		= 0x6D,
+	MAX77802_REG_LDO15CTRL1		= 0x6E,
+	/* Reserved: 0x6F */
+	MAX77802_REG_LDO17CTRL1		= 0x70,
+	MAX77802_REG_LDO18CTRL1		= 0x71,
+	MAX77802_REG_LDO19CTRL1		= 0x72,
+	MAX77802_REG_LDO20CTRL1		= 0x73,
+	MAX77802_REG_LDO21CTRL1		= 0x74,
+	MAX77802_REG_LDO22CTRL1		= 0x75,
+	MAX77802_REG_LDO23CTRL1		= 0x76,
+	MAX77802_REG_LDO24CTRL1		= 0x77,
+	MAX77802_REG_LDO25CTRL1		= 0x78,
+	MAX77802_REG_LDO26CTRL1		= 0x79,
+	MAX77802_REG_LDO27CTRL1		= 0x7A,
+	MAX77802_REG_LDO28CTRL1		= 0x7B,
+	MAX77802_REG_LDO29CTRL1		= 0x7C,
+	MAX77802_REG_LDO30CTRL1		= 0x7D,
+	/* Reserved: 0x7E */
+	MAX77802_REG_LDO32CTRL1		= 0x7F,
+	MAX77802_REG_LDO33CTRL1		= 0x80,
+	MAX77802_REG_LDO34CTRL1		= 0x81,
+	MAX77802_REG_LDO35CTRL1		= 0x82,
+	/* Reserved: 0x83-0x8F */
+	MAX77802_REG_LDO1CTRL2		= 0x90,
+	MAX77802_REG_LDO2CTRL2		= 0x91,
+	MAX77802_REG_LDO3CTRL2		= 0x92,
+	MAX77802_REG_LDO4CTRL2		= 0x93,
+	MAX77802_REG_LDO5CTRL2		= 0x94,
+	MAX77802_REG_LDO6CTRL2		= 0x95,
+	MAX77802_REG_LDO7CTRL2		= 0x96,
+	MAX77802_REG_LDO8CTRL2		= 0x97,
+	MAX77802_REG_LDO9CTRL2		= 0x98,
+	MAX77802_REG_LDO10CTRL2		= 0x99,
+	MAX77802_REG_LDO11CTRL2		= 0x9A,
+	MAX77802_REG_LDO12CTRL2		= 0x9B,
+	MAX77802_REG_LDO13CTRL2		= 0x9C,
+	MAX77802_REG_LDO14CTRL2		= 0x9D,
+	MAX77802_REG_LDO15CTRL2		= 0x9E,
+	/* Reserved: 0x9F */
+	MAX77802_REG_LDO17CTRL2		= 0xA0,
+	MAX77802_REG_LDO18CTRL2		= 0xA1,
+	MAX77802_REG_LDO19CTRL2		= 0xA2,
+	MAX77802_REG_LDO20CTRL2		= 0xA3,
+	MAX77802_REG_LDO21CTRL2		= 0xA4,
+	MAX77802_REG_LDO22CTRL2		= 0xA5,
+	MAX77802_REG_LDO23CTRL2		= 0xA6,
+	MAX77802_REG_LDO24CTRL2		= 0xA7,
+	MAX77802_REG_LDO25CTRL2		= 0xA8,
+	MAX77802_REG_LDO26CTRL2		= 0xA9,
+	MAX77802_REG_LDO27CTRL2		= 0xAA,
+	MAX77802_REG_LDO28CTRL2		= 0xAB,
+	MAX77802_REG_LDO29CTRL2		= 0xAC,
+	MAX77802_REG_LDO30CTRL2		= 0xAD,
+	/* Reserved: 0xAE */
+	MAX77802_REG_LDO32CTRL2		= 0xAF,
+	MAX77802_REG_LDO33CTRL2		= 0xB0,
+	MAX77802_REG_LDO34CTRL2		= 0xB1,
+	MAX77802_REG_LDO35CTRL2		= 0xB2,
+	/* Reserved: 0xB3 */
+
+	MAX77802_REG_BBAT_CHG		= 0xB4,
+	MAX77802_REG_32KHZ		= 0xB5,
+
+	MAX77802_REG_PMIC_END		= 0xB6,
+};
+
+enum max77802_rtc_reg {
+	MAX77802_RTC_INT		= 0xC0,
+	MAX77802_RTC_INTM		= 0xC1,
+	MAX77802_RTC_CONTROLM		= 0xC2,
+	MAX77802_RTC_CONTROL		= 0xC3,
+	MAX77802_RTC_UPDATE0		= 0xC4,
+	MAX77802_RTC_UPDATE1		= 0xC5,
+	MAX77802_WTSR_SMPL_CNTL		= 0xC6,
+	MAX77802_RTC_SEC		= 0xC7,
+	MAX77802_RTC_MIN		= 0xC8,
+	MAX77802_RTC_HOUR		= 0xC9,
+	MAX77802_RTC_WEEKDAY		= 0xCA,
+	MAX77802_RTC_MONTH		= 0xCB,
+	MAX77802_RTC_YEAR		= 0xCC,
+	MAX77802_RTC_DATE		= 0xCD,
+	MAX77802_RTC_AE1		= 0xCE,
+	MAX77802_ALARM1_SEC		= 0xCF,
+	MAX77802_ALARM1_MIN		= 0xD0,
+	MAX77802_ALARM1_HOUR		= 0xD1,
+	MAX77802_ALARM1_WEEKDAY		= 0xD2,
+	MAX77802_ALARM1_MONTH		= 0xD3,
+	MAX77802_ALARM1_YEAR		= 0xD4,
+	MAX77802_ALARM1_DATE		= 0xD5,
+	MAX77802_RTC_AE2		= 0xD6,
+	MAX77802_ALARM2_SEC		= 0xD7,
+	MAX77802_ALARM2_MIN		= 0xD8,
+	MAX77802_ALARM2_HOUR		= 0xD9,
+	MAX77802_ALARM2_WEEKDAY		= 0xDA,
+	MAX77802_ALARM2_MONTH		= 0xDB,
+	MAX77802_ALARM2_YEAR		= 0xDC,
+	MAX77802_ALARM2_DATE		= 0xDD,
+
+	MAX77802_RTC_END		= 0xDF,
+};
+
+#define MAX77802_IRQSRC_PMIC            (0)
+#define MAX77802_IRQSRC_RTC	        (1 << 0)
+
+enum max77802_irq_source {
+	PMIC_INT1 = 0,
+	PMIC_INT2,
+	RTC_INT,
+
+	MAX77802_IRQ_GROUP_NR,
+};
+
+enum max77802_irq {
+	MAX77802_PMICIRQ_PWRONF,
+	MAX77802_PMICIRQ_PWRONR,
+	MAX77802_PMICIRQ_JIGONBF,
+	MAX77802_PMICIRQ_JIGONBR,
+	MAX77802_PMICIRQ_ACOKBF,
+	MAX77802_PMICIRQ_ACOKBR,
+	MAX77802_PMICIRQ_ONKEY1S,
+	MAX77802_PMICIRQ_MRSTB,
+
+	MAX77802_PMICIRQ_140C,
+	MAX77802_PMICIRQ_120C,
+
+	MAX77802_RTCIRQ_RTC60S,
+	MAX77802_RTCIRQ_RTCA1,
+	MAX77802_RTCIRQ_RTCA2,
+	MAX77802_RTCIRQ_SMPL,
+	MAX77802_RTCIRQ_RTC1S,
+	MAX77802_RTCIRQ_WTSR,
+
+	MAX77802_IRQ_NR,
+};
+
+struct max77802_dev {
+	struct device *dev;
+	struct i2c_client *i2c; /* 0x09 / PMIC, Battery Control and RTC */
+
+	int type;
+
+	struct regmap *regmap;		/* regmap for mfd and rtc devices */
+
+	struct irq_domain *irq_domain;
+
+	int irq;
+	int irq_gpio;
+	bool wakeup;
+	struct mutex irqlock;
+	int irq_masks_cur[MAX77802_IRQ_GROUP_NR];
+	int irq_masks_cache[MAX77802_IRQ_GROUP_NR];
+};
+
+enum max77802_types {
+	TYPE_MAX77802,
+};
+
+extern int max77802_irq_init(struct max77802_dev *max77802);
+extern void max77802_irq_exit(struct max77802_dev *max77802);
+extern int max77802_irq_resume(struct max77802_dev *max77802);
+
+#endif /*  __LINUX_MFD_MAX77802_PRIV_H */
diff --git a/include/linux/mfd/max77802.h b/include/linux/mfd/max77802.h
new file mode 100644
index 0000000..3c08b2a
--- /dev/null
+++ b/include/linux/mfd/max77802.h
@@ -0,0 +1,124 @@
+/*
+ * max77802.h - Driver for the Maxim 77802
+ *
+ * Copyright (c) 2013-2014 Google, Inc
+ *
+ *  Copyright (C) 2012 Samsung Electrnoics
+ *  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.
+ *
+ * This driver is based on max8997.h
+ *
+ * MAX77802 has PMIC, RTC devices.
+ * The devices share the same I2C bus and included in
+ * this mfd driver.
+ */
+
+#ifndef __LINUX_MFD_MAX77802_H
+#define __LINUX_MFD_MAX77802_H
+
+#include <linux/regulator/consumer.h>
+
+/* MAX77802 regulator IDs - LDOS must come before BUCKs */
+enum max77802_regulators {
+	MAX77802_LDO1 = 0,
+	MAX77802_LDO2,
+	MAX77802_LDO3,
+	MAX77802_LDO4,
+	MAX77802_LDO5,
+	MAX77802_LDO6,
+	MAX77802_LDO7,
+	MAX77802_LDO8,
+	MAX77802_LDO9,
+	MAX77802_LDO10,
+	MAX77802_LDO11,
+	MAX77802_LDO12,
+	MAX77802_LDO13,
+	MAX77802_LDO14,
+	MAX77802_LDO15,
+	MAX77802_LDO16,
+	MAX77802_LDO17,
+	MAX77802_LDO18,
+	MAX77802_LDO19,
+	MAX77802_LDO20,
+	MAX77802_LDO21,
+	MAX77802_LDO22,
+	MAX77802_LDO23,
+	MAX77802_LDO24,
+	MAX77802_LDO25,
+	MAX77802_LDO26,
+	MAX77802_LDO27,
+	MAX77802_LDO28,
+	MAX77802_LDO29,
+	MAX77802_LDO30,
+	MAX77802_LDO31,
+	MAX77802_LDO32,
+	MAX77802_LDO33,
+	MAX77802_LDO34,
+	MAX77802_LDO35,
+	MAX77802_BUCK1,
+	MAX77802_BUCK2,
+	MAX77802_BUCK3,
+	MAX77802_BUCK4,
+	MAX77802_BUCK5,
+	MAX77802_BUCK6,
+	MAX77802_BUCK7,
+	MAX77802_BUCK8,
+	MAX77802_BUCK9,
+	MAX77802_BUCK10,
+
+	MAX77802_REG_MAX,
+};
+
+struct max77802_regulator_data {
+	int id;
+	int opmode;
+	struct regulator_init_data *initdata;
+	struct device_node *of_node;
+};
+
+enum max77802_opmode {
+	MAX77802_OPMODE_OFF,
+	MAX77802_OPMODE_STANDBY,
+	MAX77802_OPMODE_LP,
+	MAX77802_OPMODE_NORMAL,
+};
+
+struct max77802_opmode_data {
+	int id;
+	int mode;
+};
+
+struct max77802_platform_data {
+	/* IRQ */
+	int irq_gpio;
+	int ono;
+	int wakeup;
+
+	/* ---- PMIC ---- */
+	struct max77802_regulator_data *regulators;
+	int num_regulators;
+
+	struct max77802_opmode_data *opmode_data;
+
+	/*
+	 * GPIO-DVS feature is not fully enabled with the current version of
+	 * MAX77802 driver, but the driver does support using a DVS index other
+	 * than the default of 0.
+	 */
+	int buck_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
+	int buck_default_idx; /* Default value of DVS1, 2, 3 */
+
+	int buck_gpio_selb[5]; /* 77802: 1, 2, 3, 4, 6 */
+};
+
+#endif /* __LINUX_MFD_MAX77802_H */
-- 
2.0.0.rc2

  reply	other threads:[~2014-06-09  9:38 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-06-09  9:37 [PATCH 0/5] Add Maxim 77802 PMIC support Javier Martinez Canillas
2014-06-09  9:37 ` Javier Martinez Canillas
2014-06-09  9:37 ` Javier Martinez Canillas [this message]
2014-06-09  9:37   ` [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC Javier Martinez Canillas
2014-06-09 10:22   ` Krzysztof Kozlowski
2014-06-09 10:22     ` Krzysztof Kozlowski
2014-06-09 11:56     ` Mark Brown
2014-06-09 11:56       ` Mark Brown
2014-06-09 23:07     ` Javier Martinez Canillas
2014-06-09 23:07       ` Javier Martinez Canillas
2014-06-09 19:47   ` Mark Brown
2014-06-09 19:47     ` Mark Brown
2014-06-09 23:40     ` Javier Martinez Canillas
2014-06-09 23:40       ` Javier Martinez Canillas
2014-06-09  9:37 ` [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators Javier Martinez Canillas
2014-06-09  9:37   ` Javier Martinez Canillas
2014-06-09 19:38   ` Mark Brown
2014-06-09 19:38     ` Mark Brown
2014-06-09 19:38     ` Mark Brown
2014-06-09 23:29     ` Javier Martinez Canillas
2014-06-09 23:29       ` Javier Martinez Canillas
2014-06-10 10:53       ` Mark Brown
2014-06-10 10:53         ` Mark Brown
2014-06-09  9:37 ` [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks Javier Martinez Canillas
2014-06-09  9:37   ` Javier Martinez Canillas
2014-06-09  9:37   ` Javier Martinez Canillas
2014-06-16  8:44   ` Lee Jones
2014-06-16  8:44     ` Lee Jones
2014-06-16  8:54     ` Javier Martinez Canillas
2014-06-16  8:54       ` Javier Martinez Canillas
2014-06-09  9:37 ` [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock Javier Martinez Canillas
2014-06-09  9:37   ` Javier Martinez Canillas
2014-06-09  9:37 ` [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit Javier Martinez Canillas
2014-06-09  9:37   ` Javier Martinez Canillas
2014-06-09 10:16 ` [PATCH 0/5] Add Maxim 77802 PMIC support Krzysztof Kozlowski
2014-06-09 10:16   ` Krzysztof Kozlowski
2014-06-09 16:04   ` Doug Anderson
2014-06-09 16:04     ` Doug Anderson
2014-06-09 16:04     ` Doug Anderson
2014-06-09 22:55     ` Javier Martinez Canillas
2014-06-09 22:55       ` Javier Martinez Canillas
2014-06-09 22:55       ` Javier Martinez Canillas
2014-06-09 23:57       ` Doug Anderson
2014-06-09 23:57         ` Doug Anderson
2014-06-09 23:57         ` Doug Anderson
2014-06-10  7:45       ` Krzysztof Kozlowski
2014-06-10  7:45         ` Krzysztof Kozlowski
2014-06-10  7:45         ` Krzysztof Kozlowski
2014-06-10  7:32     ` Krzysztof Kozlowski
2014-06-10  7:32       ` Krzysztof Kozlowski
2014-06-10  7:32       ` Krzysztof Kozlowski
2014-06-10  7:50       ` Javier Martinez Canillas
2014-06-10  7:50         ` Javier Martinez Canillas
2014-06-10  7:50         ` Javier Martinez Canillas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1402306670-17041-2-git-send-email-javier.martinez@collabora.co.uk \
    --to=javier.martinez@collabora.co.uk \
    --cc=a.zummo@towertech.it \
    --cc=broonie@kernel.org \
    --cc=daniels@collabora.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dianders@chromium.org \
    --cc=kgene.kim@samsung.com \
    --cc=lee.jones@linaro.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=mturquette@linaro.org \
    --cc=olof@lixom.net \
    --cc=sameo@linux.intel.com \
    --cc=sjoerd.simons@collabora.co.uk \
    --cc=tomeu.vizoso@collabora.com \
    --subject='Re: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.