All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09  9:37 ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel,
	Javier Martinez Canillas

MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
a Real-Time-Clock (RTC) and a I2C interface to program the individual
regulators, clocks and the RTC.

This series are based on drivers added by Simon Glass to the Chrome OS
kernel and adds support for the Maxim 77802 Power Management IC, their
regulators, clocks, RTC and I2C interface. It is composed of patches:

[PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
[PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
[PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
[PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
[PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit

Patches 1-4 add support for the different devices and Patch 5 enables
the MAX77802 PMIC on the Exynos5420 based Peach pit board.

Lee,

Patches 2-4 depend on Patch 1 so I think that it makes sense if you take
1-4 through your mfd tree once the relevant maintainers ack the drivers
added to the other subsystems (regulator, clk and rtc).

Patch 5 can go through Kukjin tree since is just DTS changes.

Thanks a lot and best regards,
Javier

-- 
2.0.0.rc2


^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09  9:37 ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
a Real-Time-Clock (RTC) and a I2C interface to program the individual
regulators, clocks and the RTC.

This series are based on drivers added by Simon Glass to the Chrome OS
kernel and adds support for the Maxim 77802 Power Management IC, their
regulators, clocks, RTC and I2C interface. It is composed of patches:

[PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
[PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
[PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
[PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
[PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit

Patches 1-4 add support for the different devices and Patch 5 enables
the MAX77802 PMIC on the Exynos5420 based Peach pit board.

Lee,

Patches 2-4 depend on Patch 1 so I think that it makes sense if you take
1-4 through your mfd tree once the relevant maintainers ack the drivers
added to the other subsystems (regulator, clk and rtc).

Patch 5 can go through Kukjin tree since is just DTS changes.

Thanks a lot and best regards,
Javier

-- 
2.0.0.rc2

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
  2014-06-09  9:37 ` Javier Martinez Canillas
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel,
	Javier Martinez Canillas

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


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

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

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
  2014-06-09  9:37 ` Javier Martinez Canillas
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel,
	Javier Martinez Canillas

The MAX77802 PMIC has 10 high-efficiency Buck and 32 Low-dropout
(LDO) regulators. This patch adds support for all these regulators
found on the 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>
---
 drivers/regulator/Kconfig    |   9 +
 drivers/regulator/Makefile   |   1 +
 drivers/regulator/max77802.c | 678 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 688 insertions(+)
 create mode 100644 drivers/regulator/max77802.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 789eb46..17873d3 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -377,6 +377,15 @@ config REGULATOR_MAX77693
 	  and one current regulator 'CHARGER'. This is suitable for
 	  Exynos-4x12 chips.
 
+config REGULATOR_MAX77802
+	tristate "Maxim 77802 regulator"
+	depends on MFD_MAX77802
+	help
+	  This driver controls a Maxim 77802 regulator
+	  via I2C bus. The provided regulator is suitable for
+	  Exynos-5 chips to control various voltages. It includes
+	  support for control of voltage and ramp speed.
+
 config REGULATOR_MC13XXX_CORE
 	tristate
 
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index d461110..2aea4b6 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
 obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
 obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o
+obj-$(CONFIG_REGULATOR_MAX77802) += max77802.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
diff --git a/drivers/regulator/max77802.c b/drivers/regulator/max77802.c
new file mode 100644
index 0000000..93e8f69
--- /dev/null
+++ b/drivers/regulator/max77802.c
@@ -0,0 +1,678 @@
+/*
+ * max77802.c - Regulator 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/kernel.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+
+/* Default ramp delay in case it is not manually set */
+#define MAX77802_RAMP_DELAY		100000		/* uV/us */
+
+#define MAX77802_OPMODE_SHIFT_LDO	6
+#define MAX77802_OPMODE_BUCK234_SHIFT	4
+#define MAX77802_OPMODE_MASK		0x3
+
+#define MAX77802_VSEL_MASK		0x3F
+#define MAX77802_DVS_VSEL_MASK		0xFF
+
+#define MAX77802_RAMP_RATE_MASK_2BIT	0xC0
+#define MAX77802_RAMP_RATE_SHIFT_2BIT	6
+#define MAX77802_RAMP_RATE_MASK_4BIT	0xF0
+#define MAX77802_RAMP_RATE_SHIFT_4BIT	4
+
+/* LDO16, LDO22 and LDO31 are not available on MAX77802 */
+#define MAX77802_MAX_REGULATORS		(MAX77802_REG_MAX - 3)
+
+/* MAX77802 has two register formats: 2-bit and 4-bit */
+static const unsigned int ramp_table_77802_2bit[] = {
+	12500,
+	25000,
+	50000,
+	100000,
+};
+
+static unsigned int ramp_table_77802_4bit[] = {
+	1000,	2000,	3030,	4000,
+	5000,	5880,	7140,	8330,
+	9090,	10000,	11110,	12500,
+	16670,	25000,	50000,	100000,
+};
+
+struct max77802_regulator_prv {
+	int num_regulators;
+	struct regulator_dev *rdev[MAX77802_MAX_REGULATORS];
+	unsigned int opmode[MAX77802_MAX_REGULATORS];
+};
+
+static int max77802_get_opmode_shift(int id)
+{
+	if (id >= MAX77802_LDO1 && id <= MAX77802_LDO35)
+		return MAX77802_OPMODE_SHIFT_LDO;
+	else if (id == MAX77802_BUCK1 || (id >= MAX77802_BUCK5 &&
+					  id <= MAX77802_BUCK10))
+		return 0;
+	else if (id >= MAX77802_BUCK2 && id <= MAX77802_BUCK4)
+		return MAX77802_OPMODE_BUCK234_SHIFT;
+	else
+		return -EINVAL;
+}
+
+/*
+ * Some BUCKS supports Normal[ON/OFF] mode during suspend
+ *
+ * BUCK 1, 6, 2-4, 5, 7-10 (all)
+ *
+ * The other mode (0x02) will make pwrreq switch between normal
+ * and low power.
+ */
+static int max77802_buck_set_suspend_disable(struct regulator_dev *rdev)
+{
+	unsigned int val = MAX77802_OPMODE_STANDBY;
+	struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+	int shift = max77802_get_opmode_shift(id);
+
+	max77802->opmode[id] = val;
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  rdev->desc->enable_mask, val << shift);
+}
+
+/*
+ * Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state
+ *
+ * LDOs 3-7, 9-14, 18-26, 28, 29, 32-34, 1, 2, 8, 15, 17, 27, 30, 35 (all)
+ *
+ * LDOs 1, 20, 21, and 3, mode 1 is not supported
+ */
+static int max77802_ldo_set_suspend_mode(struct regulator_dev *rdev,
+					 unsigned int mode)
+{
+	struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+	unsigned int val;
+	int shift = max77802_get_opmode_shift(id);
+
+	switch (mode) {
+	case REGULATOR_MODE_IDLE:			/* ON in LP Mode */
+		val = MAX77802_OPMODE_LP;
+		break;
+	case REGULATOR_MODE_NORMAL:			/* ON in Normal Mode */
+		val = MAX77802_OPMODE_NORMAL;
+		break;
+	case REGULATOR_MODE_STANDBY:			/* switch off */
+		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
+			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
+			val = MAX77802_OPMODE_STANDBY;
+			break;
+		}
+		/* no break */
+	default:
+		pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
+			rdev->desc->name, mode);
+		return -EINVAL;
+	}
+
+	max77802->opmode[rdev_get_id(rdev)] = val;
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  rdev->desc->enable_mask, val << shift);
+}
+
+static int max77802_enable(struct regulator_dev *rdev)
+{
+	struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+	int shift = max77802_get_opmode_shift(id);
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  rdev->desc->enable_mask,
+				  max77802->opmode[id] << shift);
+}
+
+static int max77802_find_ramp_value(struct regulator_dev *rdev,
+				    const unsigned int limits[], int size,
+				    unsigned int ramp_delay)
+{
+	int i;
+
+	for (i = 0; i < size; i++) {
+		if (ramp_delay <= limits[i])
+			return i;
+	}
+
+	/* Use maximum value for no ramp control */
+	pr_warn("%s: ramp_delay: %d not supported, setting 100000\n",
+		rdev->desc->name, ramp_delay);
+	return size - 1;
+}
+
+/* Used for BUCKs 2-4 */
+static int max77802_set_ramp_delay_2bit(struct regulator_dev *rdev,
+					int ramp_delay)
+{
+	int id = rdev_get_id(rdev);
+	unsigned int ramp_value;
+
+	if (id > MAX77802_BUCK4) {
+		pr_warn("%s: regulator_suspend_mode: ramp delay not supported\n",
+			rdev->desc->name);
+		return -EINVAL;
+	}
+	ramp_value = max77802_find_ramp_value(rdev, ramp_table_77802_2bit,
+				ARRAY_SIZE(ramp_table_77802_2bit), ramp_delay);
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  MAX77802_RAMP_RATE_MASK_2BIT,
+				  ramp_value << MAX77802_RAMP_RATE_SHIFT_2BIT);
+}
+
+/* For BUCK1, 6 */
+static int max77802_set_ramp_delay_4bit(struct regulator_dev *rdev,
+					    int ramp_delay)
+{
+	unsigned int ramp_value;
+
+	ramp_value = max77802_find_ramp_value(rdev, ramp_table_77802_4bit,
+				ARRAY_SIZE(ramp_table_77802_4bit), ramp_delay);
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  MAX77802_RAMP_RATE_MASK_4BIT,
+				  ramp_value << MAX77802_RAMP_RATE_SHIFT_4BIT);
+}
+
+/*
+ * LDOs 3-7, 9-14, 18-26, 28, 29, 32-34, 1, 2, 8, 15, 17, 27, 30, 35 (all)
+ */
+static struct regulator_ops max77802_ldo_ops = {
+	.list_voltage		= regulator_list_voltage_linear,
+	.map_voltage		= regulator_map_voltage_linear,
+	.is_enabled		= regulator_is_enabled_regmap,
+	.enable			= max77802_enable,
+	.disable		= regulator_disable_regmap,
+	.get_voltage_sel	= regulator_get_voltage_sel_regmap,
+	.set_voltage_sel	= regulator_set_voltage_sel_regmap,
+	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
+	.set_suspend_mode	= max77802_ldo_set_suspend_mode,
+};
+
+/* BUCKS 1, 6 */
+static struct regulator_ops max77802_buck_16_dvs_ops = {
+	.list_voltage		= regulator_list_voltage_linear,
+	.map_voltage		= regulator_map_voltage_linear,
+	.is_enabled		= regulator_is_enabled_regmap,
+	.enable			= max77802_enable,
+	.disable		= regulator_disable_regmap,
+	.get_voltage_sel	= regulator_get_voltage_sel_regmap,
+	.set_voltage_sel	= regulator_set_voltage_sel_regmap,
+	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
+	.set_ramp_delay		= max77802_set_ramp_delay_4bit,
+	.set_suspend_disable	= max77802_buck_set_suspend_disable,
+};
+
+/* BUCKs 2-4, 5, 7-10 */
+static struct regulator_ops max77802_buck_dvs_ops = {
+	.list_voltage		= regulator_list_voltage_linear,
+	.map_voltage		= regulator_map_voltage_linear,
+	.is_enabled		= regulator_is_enabled_regmap,
+	.enable			= max77802_enable,
+	.disable		= regulator_disable_regmap,
+	.get_voltage_sel	= regulator_get_voltage_sel_regmap,
+	.set_voltage_sel	= regulator_set_voltage_sel_regmap,
+	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
+	.set_ramp_delay		= max77802_set_ramp_delay_2bit,
+	.set_suspend_disable	= max77802_buck_set_suspend_disable,
+};
+
+/* LDOs 3-7, 9-14, 18-26, 28, 29, 32-34 */
+#define regulator_77802_desc_p_ldo(num)	{				\
+	.name		= "LDO"#num,					\
+	.id		= MAX77802_LDO##num,				\
+	.ops		= &max77802_ldo_ops,				\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 800000,					\
+	.uV_step	= 50000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.enable_mask	= MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+}
+
+/* LDOs 1, 2, 8, 15, 17, 27, 30, 35 */
+#define regulator_77802_desc_n_ldo(num)	{				\
+	.name		= "LDO"#num,					\
+	.id		= MAX77802_LDO##num,				\
+	.ops		= &max77802_ldo_ops,				\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 800000,					\
+	.uV_step	= 25000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.enable_mask	= MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+}
+
+/* BUCKs 1, 6 */
+#define regulator_77802_desc_16_buck(num)	{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_16_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 612500,					\
+	.uV_step	= 6250,						\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 8,					\
+	.vsel_reg	= MAX77802_REG_BUCK ## num ## DVS1,		\
+	.vsel_mask	= MAX77802_DVS_VSEL_MASK,			\
+	.enable_reg	= MAX77802_REG_BUCK ## num ## CTRL,		\
+	.enable_mask	= MAX77802_OPMODE_MASK,				\
+}
+
+/* BUCKS 2-4 */
+#define regulator_77802_desc_234_buck(num)	{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 600000,					\
+	.uV_step	= 6250,						\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 0x91,						\
+	.vsel_reg	= MAX77802_REG_BUCK ## num ## DVS1,		\
+	.vsel_mask	= MAX77802_DVS_VSEL_MASK,			\
+	.enable_reg	= MAX77802_REG_BUCK ## num ## CTRL1,		\
+	.enable_mask	= MAX77802_OPMODE_MASK <<			\
+				MAX77802_OPMODE_BUCK234_SHIFT,		\
+}
+
+/* BUCK 5 */
+#define regulator_77802_desc_buck5(num)		{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 750000,					\
+	.uV_step	= 50000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_BUCK5OUT,			\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_BUCK5CTRL,			\
+	.enable_mask	= MAX77802_OPMODE_MASK,				\
+}
+
+/* BUCKs 7-10 */
+#define regulator_77802_desc_buck7_10(num)	{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 750000,					\
+	.uV_step	= 50000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_BUCK7OUT + (num - 7) * 3,	\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_BUCK7CTRL + (num - 7) * 3,	\
+	.enable_mask	= MAX77802_OPMODE_MASK,				\
+}
+
+static struct regulator_desc regulators[] = {
+	regulator_77802_desc_n_ldo(1),
+	regulator_77802_desc_n_ldo(2),
+	regulator_77802_desc_p_ldo(3),
+	regulator_77802_desc_p_ldo(4),
+	regulator_77802_desc_p_ldo(5),
+	regulator_77802_desc_p_ldo(6),
+	regulator_77802_desc_p_ldo(7),
+	regulator_77802_desc_n_ldo(8),
+	regulator_77802_desc_p_ldo(9),
+	regulator_77802_desc_p_ldo(10),
+	regulator_77802_desc_p_ldo(11),
+	regulator_77802_desc_p_ldo(12),
+	regulator_77802_desc_p_ldo(13),
+	regulator_77802_desc_p_ldo(14),
+	regulator_77802_desc_n_ldo(15),
+	regulator_77802_desc_n_ldo(17),
+	regulator_77802_desc_p_ldo(18),
+	regulator_77802_desc_p_ldo(19),
+	regulator_77802_desc_p_ldo(20),
+	regulator_77802_desc_p_ldo(21),
+	regulator_77802_desc_p_ldo(23),
+	regulator_77802_desc_p_ldo(24),
+	regulator_77802_desc_p_ldo(25),
+	regulator_77802_desc_p_ldo(26),
+	regulator_77802_desc_n_ldo(27),
+	regulator_77802_desc_p_ldo(28),
+	regulator_77802_desc_p_ldo(29),
+	regulator_77802_desc_n_ldo(30),
+	regulator_77802_desc_p_ldo(32),
+	regulator_77802_desc_p_ldo(33),
+	regulator_77802_desc_p_ldo(34),
+	regulator_77802_desc_n_ldo(35),
+	regulator_77802_desc_16_buck(1),
+	regulator_77802_desc_234_buck(2),
+	regulator_77802_desc_234_buck(3),
+	regulator_77802_desc_234_buck(4),
+	regulator_77802_desc_buck5(5),
+	regulator_77802_desc_16_buck(6),
+	regulator_77802_desc_buck7_10(7),
+	regulator_77802_desc_buck7_10(8),
+	regulator_77802_desc_buck7_10(9),
+	regulator_77802_desc_buck7_10(10),
+};
+
+#ifdef CONFIG_OF
+static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
+					struct max77802_platform_data *pdata)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct device_node *pmic_np, *regulators_np;
+	struct max77802_regulator_data *rdata;
+	struct of_regulator_match rmatch;
+	unsigned int i;
+
+	pmic_np = iodev->dev->of_node;
+	regulators_np = of_get_child_by_name(pmic_np, "voltage-regulators");
+	if (!regulators_np) {
+		dev_err(&pdev->dev, "could not find regulators sub-node\n");
+		return -EINVAL;
+	}
+
+	pdata->num_regulators = ARRAY_SIZE(regulators);
+	rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) *
+			     pdata->num_regulators, GFP_KERNEL);
+	if (!rdata) {
+		of_node_put(regulators_np);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		rmatch.name = regulators[i].name;
+		rmatch.init_data = NULL;
+		rmatch.of_node = NULL;
+		if (of_regulator_match(&pdev->dev, regulators_np, &rmatch,
+				       1) != 1) {
+			dev_warn(&pdev->dev,
+				 "No matching regulator for '%s'\n", rmatch.name);
+			continue;
+		}
+		rdata[i].initdata = rmatch.init_data;
+		rdata[i].of_node = rmatch.of_node;
+		rdata[i].id = regulators[i].id;
+	}
+
+	pdata->regulators = rdata;
+	of_node_put(regulators_np);
+
+	return 0;
+}
+#else
+static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
+					struct max77802_platform_data *pdata)
+{
+	return 0;
+}
+#endif /* CONFIG_OF */
+
+/**
+ * max77802_setup_gpios - init DVS-related GPIOs
+ *
+ * This function claims / initalizations GPIOs related to DVS if they are
+ * defined. This may have the effect of switching voltages if the
+ * pdata->buck_default_idx does not match the boot time state of pins.
+ */
+static int max77802_setup_gpios(struct device *dev,
+				struct max77802_platform_data *pdata)
+{
+	int buck_default_idx = pdata->buck_default_idx;
+	int ret;
+	int i;
+
+	/* Set all SELB high to avoid glitching while DVS is changing */
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_selb); i++) {
+		int gpio = pdata->buck_gpio_selb[i];
+
+		/* OK if some GPIOs aren't defined */
+		if (!gpio_is_valid(gpio))
+			continue;
+
+		/* If a GPIO is valid, we'd better be able to claim it */
+		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
+					    "max77802 selb");
+		if (ret) {
+			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
+			return ret;
+		}
+	}
+
+	/* Set our initial setting */
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_dvs); i++) {
+		int gpio = pdata->buck_gpio_dvs[i];
+
+		/* OK if some GPIOs aren't defined */
+		if (!gpio_is_valid(gpio))
+			continue;
+
+		/* If a GPIO is valid, we'd better be able to claim it */
+		ret = devm_gpio_request(dev, gpio, "max77802 dvs");
+		if (ret) {
+			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
+			return ret;
+		}
+		gpio_direction_output(gpio, (buck_default_idx >> i) & 1);
+	}
+
+	/* Now set SELB low to take effect */
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_selb); i++) {
+		int gpio = pdata->buck_gpio_selb[i];
+
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, 0);
+	}
+
+	return 0;
+}
+
+/**
+ * max77802_read_gpios - read the current state of the dvs GPIOs
+ *
+ * We call this function at bootup to detect what slot the firmware was
+ * using for the DVS GPIOs.  That way we can properly preserve the firmware's
+ * voltage settings
+ */
+static int max77802_read_gpios(struct max77802_platform_data *pdata)
+{
+	int buck_default_idx = pdata->buck_default_idx;
+	int result = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_dvs); i++) {
+		int gpio = pdata->buck_gpio_dvs[i];
+
+		/* OK if some GPIOs aren't defined; we'll use default */
+		if (!gpio_is_valid(gpio)) {
+			result |= buck_default_idx & (1 << i);
+			continue;
+		}
+
+		if (gpio_get_value_cansleep(gpio))
+			result |= 1 << i;
+	}
+
+	return result;
+}
+
+static inline bool max77802_is_dvs_buck(int id)
+{
+	/* on 77802 bucks 1-4, 6 are */
+	return ((id >= MAX77802_BUCK1 && id <= MAX77802_BUCK4) ||
+		(id == MAX77802_BUCK6));
+}
+
+static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
+			      int from_reg, int to_reg)
+{
+	int val;
+	int ret;
+
+	if (from_reg == to_reg)
+		return;
+
+	ret = regmap_read(regmap, from_reg, &val);
+	if (!ret)
+		ret = regmap_write(regmap, to_reg, val);
+
+	if (ret)
+		dev_warn(dev, "Copy err %d => %d (%d)\n",
+			 from_reg, to_reg, ret);
+}
+
+static int max77802_pmic_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_platform_data *pdata = dev_get_platdata(iodev->dev);
+	struct max77802_regulator_prv *max77802;
+	int i, ret = 0;
+	struct regulator_config config = { };
+	unsigned int reg;
+	int buck_default_idx;
+	int buck_old_idx;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	/* This is allocated by the MFD driver */
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data found for regulator\n");
+		return -ENODEV;
+	}
+
+	max77802 = devm_kzalloc(&pdev->dev,
+				sizeof(struct max77802_regulator_prv),
+				GFP_KERNEL);
+	if (!max77802)
+		return -ENOMEM;
+
+	if (iodev->dev->of_node) {
+		ret = max77802_pmic_dt_parse_pdata(pdev, pdata);
+		if (ret)
+			return ret;
+	}
+
+	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
+		dev_err(&pdev->dev,
+			"Invalid initial data for regulator's initialiation: " \
+			"expected %d, pdata/dt provided %d\n",
+			MAX77802_MAX_REGULATORS,
+			pdata->num_regulators);
+		return -EINVAL;
+	}
+
+	config.dev = &pdev->dev;
+	config.regmap = iodev->regmap;
+	config.driver_data = max77802;
+	platform_set_drvdata(pdev, max77802);
+
+	buck_default_idx = pdata->buck_default_idx;
+	buck_old_idx = max77802_read_gpios(pdata);
+
+	for (i = 0; i < MAX77802_MAX_REGULATORS; i++) {
+		struct regulator_dev *rdev;
+		int id = pdata->regulators[i].id;
+
+		config.init_data = pdata->regulators[i].initdata;
+		config.of_node = pdata->regulators[i].of_node;
+
+		max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
+
+		if (max77802_is_dvs_buck(id)) {
+			/* Try to copy over data so we keep firmware settings */
+			reg = regulators[i].vsel_reg;
+			max77802_copy_reg(&pdev->dev, iodev->regmap,
+					  reg + buck_old_idx,
+					  reg + buck_default_idx);
+			regulators[i].vsel_reg += buck_default_idx;
+		}
+
+		rdev = devm_regulator_register(&pdev->dev,
+						&regulators[i], &config);
+		if (IS_ERR(rdev)) {
+			dev_err(&pdev->dev,
+				"regulator init failed for %d\n", i);
+			return PTR_ERR(rdev);
+		}
+	}
+
+	ret = max77802_setup_gpios(&pdev->dev, pdata);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct platform_device_id max77802_pmic_id[] = {
+	{"max77802-pmic", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77802_pmic_id);
+
+static struct platform_driver max77802_pmic_driver = {
+	.driver = {
+		.name = "max77802-pmic",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77802_pmic_probe,
+	.id_table = max77802_pmic_id,
+};
+
+static int __init max77802_pmic_init(void)
+{
+	return platform_driver_register(&max77802_pmic_driver);
+}
+subsys_initcall(max77802_pmic_init);
+
+static void __exit max77802_pmic_cleanup(void)
+{
+	platform_driver_unregister(&max77802_pmic_driver);
+}
+module_exit(max77802_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77802 Regulator Driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
-- 
2.0.0.rc2


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

The MAX77802 PMIC has 10 high-efficiency Buck and 32 Low-dropout
(LDO) regulators. This patch adds support for all these regulators
found on the 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>
---
 drivers/regulator/Kconfig    |   9 +
 drivers/regulator/Makefile   |   1 +
 drivers/regulator/max77802.c | 678 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 688 insertions(+)
 create mode 100644 drivers/regulator/max77802.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 789eb46..17873d3 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -377,6 +377,15 @@ config REGULATOR_MAX77693
 	  and one current regulator 'CHARGER'. This is suitable for
 	  Exynos-4x12 chips.
 
+config REGULATOR_MAX77802
+	tristate "Maxim 77802 regulator"
+	depends on MFD_MAX77802
+	help
+	  This driver controls a Maxim 77802 regulator
+	  via I2C bus. The provided regulator is suitable for
+	  Exynos-5 chips to control various voltages. It includes
+	  support for control of voltage and ramp speed.
+
 config REGULATOR_MC13XXX_CORE
 	tristate
 
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index d461110..2aea4b6 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
 obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
 obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o
+obj-$(CONFIG_REGULATOR_MAX77802) += max77802.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
diff --git a/drivers/regulator/max77802.c b/drivers/regulator/max77802.c
new file mode 100644
index 0000000..93e8f69
--- /dev/null
+++ b/drivers/regulator/max77802.c
@@ -0,0 +1,678 @@
+/*
+ * max77802.c - Regulator 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/kernel.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+
+/* Default ramp delay in case it is not manually set */
+#define MAX77802_RAMP_DELAY		100000		/* uV/us */
+
+#define MAX77802_OPMODE_SHIFT_LDO	6
+#define MAX77802_OPMODE_BUCK234_SHIFT	4
+#define MAX77802_OPMODE_MASK		0x3
+
+#define MAX77802_VSEL_MASK		0x3F
+#define MAX77802_DVS_VSEL_MASK		0xFF
+
+#define MAX77802_RAMP_RATE_MASK_2BIT	0xC0
+#define MAX77802_RAMP_RATE_SHIFT_2BIT	6
+#define MAX77802_RAMP_RATE_MASK_4BIT	0xF0
+#define MAX77802_RAMP_RATE_SHIFT_4BIT	4
+
+/* LDO16, LDO22 and LDO31 are not available on MAX77802 */
+#define MAX77802_MAX_REGULATORS		(MAX77802_REG_MAX - 3)
+
+/* MAX77802 has two register formats: 2-bit and 4-bit */
+static const unsigned int ramp_table_77802_2bit[] = {
+	12500,
+	25000,
+	50000,
+	100000,
+};
+
+static unsigned int ramp_table_77802_4bit[] = {
+	1000,	2000,	3030,	4000,
+	5000,	5880,	7140,	8330,
+	9090,	10000,	11110,	12500,
+	16670,	25000,	50000,	100000,
+};
+
+struct max77802_regulator_prv {
+	int num_regulators;
+	struct regulator_dev *rdev[MAX77802_MAX_REGULATORS];
+	unsigned int opmode[MAX77802_MAX_REGULATORS];
+};
+
+static int max77802_get_opmode_shift(int id)
+{
+	if (id >= MAX77802_LDO1 && id <= MAX77802_LDO35)
+		return MAX77802_OPMODE_SHIFT_LDO;
+	else if (id == MAX77802_BUCK1 || (id >= MAX77802_BUCK5 &&
+					  id <= MAX77802_BUCK10))
+		return 0;
+	else if (id >= MAX77802_BUCK2 && id <= MAX77802_BUCK4)
+		return MAX77802_OPMODE_BUCK234_SHIFT;
+	else
+		return -EINVAL;
+}
+
+/*
+ * Some BUCKS supports Normal[ON/OFF] mode during suspend
+ *
+ * BUCK 1, 6, 2-4, 5, 7-10 (all)
+ *
+ * The other mode (0x02) will make pwrreq switch between normal
+ * and low power.
+ */
+static int max77802_buck_set_suspend_disable(struct regulator_dev *rdev)
+{
+	unsigned int val = MAX77802_OPMODE_STANDBY;
+	struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+	int shift = max77802_get_opmode_shift(id);
+
+	max77802->opmode[id] = val;
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  rdev->desc->enable_mask, val << shift);
+}
+
+/*
+ * Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state
+ *
+ * LDOs 3-7, 9-14, 18-26, 28, 29, 32-34, 1, 2, 8, 15, 17, 27, 30, 35 (all)
+ *
+ * LDOs 1, 20, 21, and 3, mode 1 is not supported
+ */
+static int max77802_ldo_set_suspend_mode(struct regulator_dev *rdev,
+					 unsigned int mode)
+{
+	struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+	unsigned int val;
+	int shift = max77802_get_opmode_shift(id);
+
+	switch (mode) {
+	case REGULATOR_MODE_IDLE:			/* ON in LP Mode */
+		val = MAX77802_OPMODE_LP;
+		break;
+	case REGULATOR_MODE_NORMAL:			/* ON in Normal Mode */
+		val = MAX77802_OPMODE_NORMAL;
+		break;
+	case REGULATOR_MODE_STANDBY:			/* switch off */
+		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
+			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
+			val = MAX77802_OPMODE_STANDBY;
+			break;
+		}
+		/* no break */
+	default:
+		pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
+			rdev->desc->name, mode);
+		return -EINVAL;
+	}
+
+	max77802->opmode[rdev_get_id(rdev)] = val;
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  rdev->desc->enable_mask, val << shift);
+}
+
+static int max77802_enable(struct regulator_dev *rdev)
+{
+	struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+	int id = rdev_get_id(rdev);
+	int shift = max77802_get_opmode_shift(id);
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  rdev->desc->enable_mask,
+				  max77802->opmode[id] << shift);
+}
+
+static int max77802_find_ramp_value(struct regulator_dev *rdev,
+				    const unsigned int limits[], int size,
+				    unsigned int ramp_delay)
+{
+	int i;
+
+	for (i = 0; i < size; i++) {
+		if (ramp_delay <= limits[i])
+			return i;
+	}
+
+	/* Use maximum value for no ramp control */
+	pr_warn("%s: ramp_delay: %d not supported, setting 100000\n",
+		rdev->desc->name, ramp_delay);
+	return size - 1;
+}
+
+/* Used for BUCKs 2-4 */
+static int max77802_set_ramp_delay_2bit(struct regulator_dev *rdev,
+					int ramp_delay)
+{
+	int id = rdev_get_id(rdev);
+	unsigned int ramp_value;
+
+	if (id > MAX77802_BUCK4) {
+		pr_warn("%s: regulator_suspend_mode: ramp delay not supported\n",
+			rdev->desc->name);
+		return -EINVAL;
+	}
+	ramp_value = max77802_find_ramp_value(rdev, ramp_table_77802_2bit,
+				ARRAY_SIZE(ramp_table_77802_2bit), ramp_delay);
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  MAX77802_RAMP_RATE_MASK_2BIT,
+				  ramp_value << MAX77802_RAMP_RATE_SHIFT_2BIT);
+}
+
+/* For BUCK1, 6 */
+static int max77802_set_ramp_delay_4bit(struct regulator_dev *rdev,
+					    int ramp_delay)
+{
+	unsigned int ramp_value;
+
+	ramp_value = max77802_find_ramp_value(rdev, ramp_table_77802_4bit,
+				ARRAY_SIZE(ramp_table_77802_4bit), ramp_delay);
+
+	return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+				  MAX77802_RAMP_RATE_MASK_4BIT,
+				  ramp_value << MAX77802_RAMP_RATE_SHIFT_4BIT);
+}
+
+/*
+ * LDOs 3-7, 9-14, 18-26, 28, 29, 32-34, 1, 2, 8, 15, 17, 27, 30, 35 (all)
+ */
+static struct regulator_ops max77802_ldo_ops = {
+	.list_voltage		= regulator_list_voltage_linear,
+	.map_voltage		= regulator_map_voltage_linear,
+	.is_enabled		= regulator_is_enabled_regmap,
+	.enable			= max77802_enable,
+	.disable		= regulator_disable_regmap,
+	.get_voltage_sel	= regulator_get_voltage_sel_regmap,
+	.set_voltage_sel	= regulator_set_voltage_sel_regmap,
+	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
+	.set_suspend_mode	= max77802_ldo_set_suspend_mode,
+};
+
+/* BUCKS 1, 6 */
+static struct regulator_ops max77802_buck_16_dvs_ops = {
+	.list_voltage		= regulator_list_voltage_linear,
+	.map_voltage		= regulator_map_voltage_linear,
+	.is_enabled		= regulator_is_enabled_regmap,
+	.enable			= max77802_enable,
+	.disable		= regulator_disable_regmap,
+	.get_voltage_sel	= regulator_get_voltage_sel_regmap,
+	.set_voltage_sel	= regulator_set_voltage_sel_regmap,
+	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
+	.set_ramp_delay		= max77802_set_ramp_delay_4bit,
+	.set_suspend_disable	= max77802_buck_set_suspend_disable,
+};
+
+/* BUCKs 2-4, 5, 7-10 */
+static struct regulator_ops max77802_buck_dvs_ops = {
+	.list_voltage		= regulator_list_voltage_linear,
+	.map_voltage		= regulator_map_voltage_linear,
+	.is_enabled		= regulator_is_enabled_regmap,
+	.enable			= max77802_enable,
+	.disable		= regulator_disable_regmap,
+	.get_voltage_sel	= regulator_get_voltage_sel_regmap,
+	.set_voltage_sel	= regulator_set_voltage_sel_regmap,
+	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
+	.set_ramp_delay		= max77802_set_ramp_delay_2bit,
+	.set_suspend_disable	= max77802_buck_set_suspend_disable,
+};
+
+/* LDOs 3-7, 9-14, 18-26, 28, 29, 32-34 */
+#define regulator_77802_desc_p_ldo(num)	{				\
+	.name		= "LDO"#num,					\
+	.id		= MAX77802_LDO##num,				\
+	.ops		= &max77802_ldo_ops,				\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 800000,					\
+	.uV_step	= 50000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.enable_mask	= MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+}
+
+/* LDOs 1, 2, 8, 15, 17, 27, 30, 35 */
+#define regulator_77802_desc_n_ldo(num)	{				\
+	.name		= "LDO"#num,					\
+	.id		= MAX77802_LDO##num,				\
+	.ops		= &max77802_ldo_ops,				\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 800000,					\
+	.uV_step	= 25000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_LDO1CTRL1 + num - 1,		\
+	.enable_mask	= MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+}
+
+/* BUCKs 1, 6 */
+#define regulator_77802_desc_16_buck(num)	{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_16_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 612500,					\
+	.uV_step	= 6250,						\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 8,					\
+	.vsel_reg	= MAX77802_REG_BUCK ## num ## DVS1,		\
+	.vsel_mask	= MAX77802_DVS_VSEL_MASK,			\
+	.enable_reg	= MAX77802_REG_BUCK ## num ## CTRL,		\
+	.enable_mask	= MAX77802_OPMODE_MASK,				\
+}
+
+/* BUCKS 2-4 */
+#define regulator_77802_desc_234_buck(num)	{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 600000,					\
+	.uV_step	= 6250,						\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 0x91,						\
+	.vsel_reg	= MAX77802_REG_BUCK ## num ## DVS1,		\
+	.vsel_mask	= MAX77802_DVS_VSEL_MASK,			\
+	.enable_reg	= MAX77802_REG_BUCK ## num ## CTRL1,		\
+	.enable_mask	= MAX77802_OPMODE_MASK <<			\
+				MAX77802_OPMODE_BUCK234_SHIFT,		\
+}
+
+/* BUCK 5 */
+#define regulator_77802_desc_buck5(num)		{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 750000,					\
+	.uV_step	= 50000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_BUCK5OUT,			\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_BUCK5CTRL,			\
+	.enable_mask	= MAX77802_OPMODE_MASK,				\
+}
+
+/* BUCKs 7-10 */
+#define regulator_77802_desc_buck7_10(num)	{			\
+	.name		= "BUCK"#num,					\
+	.id		= MAX77802_BUCK##num,				\
+	.ops		= &max77802_buck_dvs_ops,			\
+	.type		= REGULATOR_VOLTAGE,				\
+	.owner		= THIS_MODULE,					\
+	.min_uV		= 750000,					\
+	.uV_step	= 50000,					\
+	.ramp_delay	= MAX77802_RAMP_DELAY,				\
+	.n_voltages	= 1 << 6,					\
+	.vsel_reg	= MAX77802_REG_BUCK7OUT + (num - 7) * 3,	\
+	.vsel_mask	= MAX77802_VSEL_MASK,				\
+	.enable_reg	= MAX77802_REG_BUCK7CTRL + (num - 7) * 3,	\
+	.enable_mask	= MAX77802_OPMODE_MASK,				\
+}
+
+static struct regulator_desc regulators[] = {
+	regulator_77802_desc_n_ldo(1),
+	regulator_77802_desc_n_ldo(2),
+	regulator_77802_desc_p_ldo(3),
+	regulator_77802_desc_p_ldo(4),
+	regulator_77802_desc_p_ldo(5),
+	regulator_77802_desc_p_ldo(6),
+	regulator_77802_desc_p_ldo(7),
+	regulator_77802_desc_n_ldo(8),
+	regulator_77802_desc_p_ldo(9),
+	regulator_77802_desc_p_ldo(10),
+	regulator_77802_desc_p_ldo(11),
+	regulator_77802_desc_p_ldo(12),
+	regulator_77802_desc_p_ldo(13),
+	regulator_77802_desc_p_ldo(14),
+	regulator_77802_desc_n_ldo(15),
+	regulator_77802_desc_n_ldo(17),
+	regulator_77802_desc_p_ldo(18),
+	regulator_77802_desc_p_ldo(19),
+	regulator_77802_desc_p_ldo(20),
+	regulator_77802_desc_p_ldo(21),
+	regulator_77802_desc_p_ldo(23),
+	regulator_77802_desc_p_ldo(24),
+	regulator_77802_desc_p_ldo(25),
+	regulator_77802_desc_p_ldo(26),
+	regulator_77802_desc_n_ldo(27),
+	regulator_77802_desc_p_ldo(28),
+	regulator_77802_desc_p_ldo(29),
+	regulator_77802_desc_n_ldo(30),
+	regulator_77802_desc_p_ldo(32),
+	regulator_77802_desc_p_ldo(33),
+	regulator_77802_desc_p_ldo(34),
+	regulator_77802_desc_n_ldo(35),
+	regulator_77802_desc_16_buck(1),
+	regulator_77802_desc_234_buck(2),
+	regulator_77802_desc_234_buck(3),
+	regulator_77802_desc_234_buck(4),
+	regulator_77802_desc_buck5(5),
+	regulator_77802_desc_16_buck(6),
+	regulator_77802_desc_buck7_10(7),
+	regulator_77802_desc_buck7_10(8),
+	regulator_77802_desc_buck7_10(9),
+	regulator_77802_desc_buck7_10(10),
+};
+
+#ifdef CONFIG_OF
+static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
+					struct max77802_platform_data *pdata)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct device_node *pmic_np, *regulators_np;
+	struct max77802_regulator_data *rdata;
+	struct of_regulator_match rmatch;
+	unsigned int i;
+
+	pmic_np = iodev->dev->of_node;
+	regulators_np = of_get_child_by_name(pmic_np, "voltage-regulators");
+	if (!regulators_np) {
+		dev_err(&pdev->dev, "could not find regulators sub-node\n");
+		return -EINVAL;
+	}
+
+	pdata->num_regulators = ARRAY_SIZE(regulators);
+	rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) *
+			     pdata->num_regulators, GFP_KERNEL);
+	if (!rdata) {
+		of_node_put(regulators_np);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < pdata->num_regulators; i++) {
+		rmatch.name = regulators[i].name;
+		rmatch.init_data = NULL;
+		rmatch.of_node = NULL;
+		if (of_regulator_match(&pdev->dev, regulators_np, &rmatch,
+				       1) != 1) {
+			dev_warn(&pdev->dev,
+				 "No matching regulator for '%s'\n", rmatch.name);
+			continue;
+		}
+		rdata[i].initdata = rmatch.init_data;
+		rdata[i].of_node = rmatch.of_node;
+		rdata[i].id = regulators[i].id;
+	}
+
+	pdata->regulators = rdata;
+	of_node_put(regulators_np);
+
+	return 0;
+}
+#else
+static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
+					struct max77802_platform_data *pdata)
+{
+	return 0;
+}
+#endif /* CONFIG_OF */
+
+/**
+ * max77802_setup_gpios - init DVS-related GPIOs
+ *
+ * This function claims / initalizations GPIOs related to DVS if they are
+ * defined. This may have the effect of switching voltages if the
+ * pdata->buck_default_idx does not match the boot time state of pins.
+ */
+static int max77802_setup_gpios(struct device *dev,
+				struct max77802_platform_data *pdata)
+{
+	int buck_default_idx = pdata->buck_default_idx;
+	int ret;
+	int i;
+
+	/* Set all SELB high to avoid glitching while DVS is changing */
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_selb); i++) {
+		int gpio = pdata->buck_gpio_selb[i];
+
+		/* OK if some GPIOs aren't defined */
+		if (!gpio_is_valid(gpio))
+			continue;
+
+		/* If a GPIO is valid, we'd better be able to claim it */
+		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
+					    "max77802 selb");
+		if (ret) {
+			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
+			return ret;
+		}
+	}
+
+	/* Set our initial setting */
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_dvs); i++) {
+		int gpio = pdata->buck_gpio_dvs[i];
+
+		/* OK if some GPIOs aren't defined */
+		if (!gpio_is_valid(gpio))
+			continue;
+
+		/* If a GPIO is valid, we'd better be able to claim it */
+		ret = devm_gpio_request(dev, gpio, "max77802 dvs");
+		if (ret) {
+			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
+			return ret;
+		}
+		gpio_direction_output(gpio, (buck_default_idx >> i) & 1);
+	}
+
+	/* Now set SELB low to take effect */
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_selb); i++) {
+		int gpio = pdata->buck_gpio_selb[i];
+
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, 0);
+	}
+
+	return 0;
+}
+
+/**
+ * max77802_read_gpios - read the current state of the dvs GPIOs
+ *
+ * We call this function at bootup to detect what slot the firmware was
+ * using for the DVS GPIOs.  That way we can properly preserve the firmware's
+ * voltage settings
+ */
+static int max77802_read_gpios(struct max77802_platform_data *pdata)
+{
+	int buck_default_idx = pdata->buck_default_idx;
+	int result = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pdata->buck_gpio_dvs); i++) {
+		int gpio = pdata->buck_gpio_dvs[i];
+
+		/* OK if some GPIOs aren't defined; we'll use default */
+		if (!gpio_is_valid(gpio)) {
+			result |= buck_default_idx & (1 << i);
+			continue;
+		}
+
+		if (gpio_get_value_cansleep(gpio))
+			result |= 1 << i;
+	}
+
+	return result;
+}
+
+static inline bool max77802_is_dvs_buck(int id)
+{
+	/* on 77802 bucks 1-4, 6 are */
+	return ((id >= MAX77802_BUCK1 && id <= MAX77802_BUCK4) ||
+		(id == MAX77802_BUCK6));
+}
+
+static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
+			      int from_reg, int to_reg)
+{
+	int val;
+	int ret;
+
+	if (from_reg == to_reg)
+		return;
+
+	ret = regmap_read(regmap, from_reg, &val);
+	if (!ret)
+		ret = regmap_write(regmap, to_reg, val);
+
+	if (ret)
+		dev_warn(dev, "Copy err %d => %d (%d)\n",
+			 from_reg, to_reg, ret);
+}
+
+static int max77802_pmic_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_platform_data *pdata = dev_get_platdata(iodev->dev);
+	struct max77802_regulator_prv *max77802;
+	int i, ret = 0;
+	struct regulator_config config = { };
+	unsigned int reg;
+	int buck_default_idx;
+	int buck_old_idx;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	/* This is allocated by the MFD driver */
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data found for regulator\n");
+		return -ENODEV;
+	}
+
+	max77802 = devm_kzalloc(&pdev->dev,
+				sizeof(struct max77802_regulator_prv),
+				GFP_KERNEL);
+	if (!max77802)
+		return -ENOMEM;
+
+	if (iodev->dev->of_node) {
+		ret = max77802_pmic_dt_parse_pdata(pdev, pdata);
+		if (ret)
+			return ret;
+	}
+
+	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
+		dev_err(&pdev->dev,
+			"Invalid initial data for regulator's initialiation: " \
+			"expected %d, pdata/dt provided %d\n",
+			MAX77802_MAX_REGULATORS,
+			pdata->num_regulators);
+		return -EINVAL;
+	}
+
+	config.dev = &pdev->dev;
+	config.regmap = iodev->regmap;
+	config.driver_data = max77802;
+	platform_set_drvdata(pdev, max77802);
+
+	buck_default_idx = pdata->buck_default_idx;
+	buck_old_idx = max77802_read_gpios(pdata);
+
+	for (i = 0; i < MAX77802_MAX_REGULATORS; i++) {
+		struct regulator_dev *rdev;
+		int id = pdata->regulators[i].id;
+
+		config.init_data = pdata->regulators[i].initdata;
+		config.of_node = pdata->regulators[i].of_node;
+
+		max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
+
+		if (max77802_is_dvs_buck(id)) {
+			/* Try to copy over data so we keep firmware settings */
+			reg = regulators[i].vsel_reg;
+			max77802_copy_reg(&pdev->dev, iodev->regmap,
+					  reg + buck_old_idx,
+					  reg + buck_default_idx);
+			regulators[i].vsel_reg += buck_default_idx;
+		}
+
+		rdev = devm_regulator_register(&pdev->dev,
+						&regulators[i], &config);
+		if (IS_ERR(rdev)) {
+			dev_err(&pdev->dev,
+				"regulator init failed for %d\n", i);
+			return PTR_ERR(rdev);
+		}
+	}
+
+	ret = max77802_setup_gpios(&pdev->dev, pdata);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct platform_device_id max77802_pmic_id[] = {
+	{"max77802-pmic", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77802_pmic_id);
+
+static struct platform_driver max77802_pmic_driver = {
+	.driver = {
+		.name = "max77802-pmic",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77802_pmic_probe,
+	.id_table = max77802_pmic_id,
+};
+
+static int __init max77802_pmic_init(void)
+{
+	return platform_driver_register(&max77802_pmic_driver);
+}
+subsys_initcall(max77802_pmic_init);
+
+static void __exit max77802_pmic_cleanup(void)
+{
+	platform_driver_unregister(&max77802_pmic_driver);
+}
+module_exit(max77802_pmic_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77802 Regulator Driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
  2014-06-09  9:37 ` Javier Martinez Canillas
  (?)
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel,
	Javier Martinez Canillas

The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
Low Jitter Mode. This patch adds support for these two clocks.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
 drivers/clk/Kconfig                                |   6 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
 drivers/mfd/max77802.c                             |   3 +
 include/dt-bindings/clock/maxim,max77802.h         |  22 ++
 6 files changed, 325 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
 create mode 100644 drivers/clk/clk-max77802.c
 create mode 100644 include/dt-bindings/clock/maxim,max77802.h

diff --git a/Documentation/devicetree/bindings/clock/maxim,max77802.txt b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
new file mode 100644
index 0000000..1d3fb64
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
@@ -0,0 +1,40 @@
+Binding for Maxim MAX77802 32k clock generator block
+
+This is a part of device tree bindings of MAX77802 multi-function device.
+More information can be found in bindings/mfd/max77802.txt file.
+
+The MAX77802 contains two 32.768khz clock outputs that can be controlled
+(gated/ungated) over I2C.
+
+Following properties should be presend in main device node of the MFD chip.
+
+Required properties:
+
+- #clock-cells: from common clock binding; shall be set to 1.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
+
+Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77802.h
+header and can be used in device tree sources.
+
+Example: Node of the MFD chip
+
+	max77802: max77802@09 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&wakeup_eint>;
+		interrupts = <26 0>;
+		reg = <0x09>;
+		#clock-cells = <1>;
+
+		/* ... */
+	};
+
+Example: Clock consumer node
+
+	foo@0 {
+		compatible = "bar,foo";
+		/* ... */
+		clock-names = "32khz_ap";
+		clocks = <&max77802 MAX77802_CLK_32K_AP>;
+	};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 9f9c5ae..74c71a4 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -38,6 +38,12 @@ config COMMON_CLK_MAX77686
 	---help---
 	  This driver supports Maxim 77686 crystal oscillator clock. 
 
+config COMMON_CLK_MAX77802
+	tristate "Clock driver for Maxim 77802 MFD"
+	depends on MFD_MAX77802
+	---help---
+	  This driver supports Maxim 77802 crystal oscillator clock.
+
 config COMMON_CLK_SI5351
 	tristate "Clock driver for SiLabs 5351A/B/C"
 	depends on I2C
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..677692f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_EFM32)		+= clk-efm32gg.o
 obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
 obj-$(CONFIG_MACH_LOONGSON1)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_MAX77802)	+= clk-max77802.o
 obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
 obj-$(CONFIG_ARCH_NOMADIK)		+= clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)		+= clk-nspire.o
diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c
new file mode 100644
index 0000000..a98fc29
--- /dev/null
+++ b/drivers/clk/clk-max77802.c
@@ -0,0 +1,253 @@
+/*
+ * clk-max77802.c - Clock driver for Maxim 77802
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * 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 clk-max77686.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+#include <dt-bindings/clock/maxim,max77802.h>
+
+#define MAX77802_CLOCK_OPMODE_MASK	0x1
+#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
+
+struct max77802_clk {
+	struct max77802_dev *iodev;
+	u32 mask;
+	struct clk_hw hw;
+	struct clk_lookup *lookup;
+};
+
+static struct max77802_clk *to_max77802_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct max77802_clk, hw);
+}
+
+static int max77802_clk_prepare(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+	return regmap_update_bits(max77802->iodev->regmap,
+				  MAX77802_REG_32KHZ, max77802->mask,
+				  max77802->mask);
+}
+
+static void max77802_clk_unprepare(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+	regmap_update_bits(max77802->iodev->regmap,
+		MAX77802_REG_32KHZ, max77802->mask, ~max77802->mask);
+}
+
+static int max77802_clk_is_prepared(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+	int ret;
+	u32 val;
+
+	ret = regmap_read(max77802->iodev->regmap,
+				MAX77802_REG_32KHZ, &val);
+
+	if (ret < 0)
+		return -EINVAL;
+
+	return val & max77802->mask;
+}
+
+static unsigned long max77802_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return 32768;
+}
+
+static struct clk_ops max77802_clk_ops = {
+	.prepare	= max77802_clk_prepare,
+	.unprepare	= max77802_clk_unprepare,
+	.is_prepared	= max77802_clk_is_prepared,
+	.recalc_rate	= max77802_recalc_rate,
+};
+
+static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = {
+	[MAX77802_CLK_32K_AP] = {
+		.name = "32khz_ap",
+		.ops = &max77802_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+	[MAX77802_CLK_32K_CP] = {
+		.name = "32khz_cp",
+		.ops = &max77802_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+};
+
+static struct clk *max77802_clk_register(struct device *dev,
+					struct max77802_clk *max77802)
+{
+	struct clk *clk;
+	struct clk_hw *hw = &max77802->hw;
+
+	clk = clk_register(dev, hw);
+	if (IS_ERR(clk))
+		return clk;
+
+	max77802->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
+	if (!max77802->lookup)
+		return ERR_PTR(-ENOMEM);
+
+	max77802->lookup->con_id = hw->init->name;
+	max77802->lookup->clk = clk;
+
+	clkdev_add(max77802->lookup);
+
+	return clk;
+}
+
+static int max77802_clk_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_clk *max77802_clks[MAX77802_CLKS_NUM];
+	struct clk **clocks;
+	int i, ret;
+
+	clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
+					* MAX77802_CLKS_NUM, GFP_KERNEL);
+	if (!clocks)
+		return -ENOMEM;
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		max77802_clks[i] = devm_kzalloc(&pdev->dev,
+					sizeof(struct max77802_clk), GFP_KERNEL);
+		if (!max77802_clks[i])
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		max77802_clks[i]->iodev = iodev;
+		max77802_clks[i]->mask = MAX77802_CLOCK_OPMODE_MASK << i;
+		max77802_clks[i]->hw.init = &max77802_clks_init[i];
+
+		clocks[i] = max77802_clk_register(&pdev->dev, max77802_clks[i]);
+		if (IS_ERR(clocks[i])) {
+			ret = PTR_ERR(clocks[i]);
+			dev_err(&pdev->dev, "failed to register %s\n",
+				max77802_clks[i]->hw.init->name);
+			goto err_clocks;
+		}
+	}
+
+	/* Enable low-jitter mode on the 32khz clocks. */
+	ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ,
+				 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
+				 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to enable low-jitter mode\n");
+		goto err_clocks;
+	}
+
+	platform_set_drvdata(pdev, clocks);
+
+	if (iodev->dev->of_node) {
+		struct clk_onecell_data *of_data;
+
+		of_data = devm_kzalloc(&pdev->dev,
+					sizeof(*of_data), GFP_KERNEL);
+		if (!of_data) {
+			ret = -ENOMEM;
+			goto err_clocks;
+		}
+
+		of_data->clks = clocks;
+		of_data->clk_num = MAX77802_CLKS_NUM;
+		ret = of_clk_add_provider(iodev->dev->of_node,
+					  of_clk_src_onecell_get, of_data);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register OF clock provider\n");
+			goto err_clocks;
+		}
+	}
+
+	return 0;
+
+err_clocks:
+	for (--i; i >= 0; --i) {
+		clkdev_drop(max77802_clks[i]->lookup);
+		clk_unregister(max77802_clks[i]->hw.clk);
+	}
+
+	return ret;
+}
+
+static int max77802_clk_remove(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct clk **clocks = platform_get_drvdata(pdev);
+	int i;
+
+	if (iodev->dev->of_node)
+		of_clk_del_provider(iodev->dev->of_node);
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		struct clk_hw *hw = __clk_get_hw(clocks[i]);
+		struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+		clkdev_drop(max77802->lookup);
+		clk_unregister(clocks[i]);
+	}
+	return 0;
+}
+
+static const struct platform_device_id max77802_clk_id[] = {
+	{ "max77802-clk", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77802_clk_id);
+
+static struct platform_driver max77802_clk_driver = {
+	.driver = {
+		.name  = "max77802-clk",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77802_clk_probe,
+	.remove = max77802_clk_remove,
+	.id_table = max77802_clk_id,
+};
+
+static int __init max77802_clk_init(void)
+{
+	return platform_driver_register(&max77802_clk_driver);
+}
+subsys_initcall(max77802_clk_init);
+
+static void __init max77802_clk_cleanup(void)
+{
+	platform_driver_unregister(&max77802_clk_driver);
+}
+module_exit(max77802_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
+MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
index 59696dd..33e8023 100644
--- a/drivers/mfd/max77802.c
+++ b/drivers/mfd/max77802.c
@@ -35,6 +35,9 @@
 
 static const struct mfd_cell max77802_devs[] = {
 	{ .name = "max77802-pmic", },
+#if defined(CONFIG_COMMON_CLK_MAX77802)
+	{ .name = "max77802-clk", },
+#endif
 };
 
 static bool max77802_pmic_is_accessible_reg(struct device *dev,
diff --git a/include/dt-bindings/clock/maxim,max77802.h b/include/dt-bindings/clock/maxim,max77802.h
new file mode 100644
index 0000000..997312e
--- /dev/null
+++ b/include/dt-bindings/clock/maxim,max77802.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Device Tree binding constants clocks for the Maxim 77802 PMIC.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H
+#define _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H
+
+/* Fixed rate clocks. */
+
+#define MAX77802_CLK_32K_AP		0
+#define MAX77802_CLK_32K_CP		1
+
+/* Total number of clocks. */
+#define MAX77802_CLKS_NUM		(MAX77802_CLK_32K_CP + 1)
+
+#endif /* _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H */
-- 
2.0.0.rc2


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Alessandro Zummo, Kukjin Kim, Mike Turquette, Samuel Ortiz,
	Tomeu Vizoso, devicetree, linux-kernel, Liam Girdwood,
	Doug Anderson, linux-samsung-soc, Sjoerd Simons, Mark Brown,
	Olof Johansson, Javier Martinez Canillas, linux-arm-kernel,
	Daniel Stone

The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
Low Jitter Mode. This patch adds support for these two clocks.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
 drivers/clk/Kconfig                                |   6 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
 drivers/mfd/max77802.c                             |   3 +
 include/dt-bindings/clock/maxim,max77802.h         |  22 ++
 6 files changed, 325 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
 create mode 100644 drivers/clk/clk-max77802.c
 create mode 100644 include/dt-bindings/clock/maxim,max77802.h

diff --git a/Documentation/devicetree/bindings/clock/maxim,max77802.txt b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
new file mode 100644
index 0000000..1d3fb64
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
@@ -0,0 +1,40 @@
+Binding for Maxim MAX77802 32k clock generator block
+
+This is a part of device tree bindings of MAX77802 multi-function device.
+More information can be found in bindings/mfd/max77802.txt file.
+
+The MAX77802 contains two 32.768khz clock outputs that can be controlled
+(gated/ungated) over I2C.
+
+Following properties should be presend in main device node of the MFD chip.
+
+Required properties:
+
+- #clock-cells: from common clock binding; shall be set to 1.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
+
+Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77802.h
+header and can be used in device tree sources.
+
+Example: Node of the MFD chip
+
+	max77802: max77802@09 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&wakeup_eint>;
+		interrupts = <26 0>;
+		reg = <0x09>;
+		#clock-cells = <1>;
+
+		/* ... */
+	};
+
+Example: Clock consumer node
+
+	foo@0 {
+		compatible = "bar,foo";
+		/* ... */
+		clock-names = "32khz_ap";
+		clocks = <&max77802 MAX77802_CLK_32K_AP>;
+	};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 9f9c5ae..74c71a4 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -38,6 +38,12 @@ config COMMON_CLK_MAX77686
 	---help---
 	  This driver supports Maxim 77686 crystal oscillator clock. 
 
+config COMMON_CLK_MAX77802
+	tristate "Clock driver for Maxim 77802 MFD"
+	depends on MFD_MAX77802
+	---help---
+	  This driver supports Maxim 77802 crystal oscillator clock.
+
 config COMMON_CLK_SI5351
 	tristate "Clock driver for SiLabs 5351A/B/C"
 	depends on I2C
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..677692f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_EFM32)		+= clk-efm32gg.o
 obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
 obj-$(CONFIG_MACH_LOONGSON1)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_MAX77802)	+= clk-max77802.o
 obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
 obj-$(CONFIG_ARCH_NOMADIK)		+= clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)		+= clk-nspire.o
diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c
new file mode 100644
index 0000000..a98fc29
--- /dev/null
+++ b/drivers/clk/clk-max77802.c
@@ -0,0 +1,253 @@
+/*
+ * clk-max77802.c - Clock driver for Maxim 77802
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * 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 clk-max77686.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+#include <dt-bindings/clock/maxim,max77802.h>
+
+#define MAX77802_CLOCK_OPMODE_MASK	0x1
+#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
+
+struct max77802_clk {
+	struct max77802_dev *iodev;
+	u32 mask;
+	struct clk_hw hw;
+	struct clk_lookup *lookup;
+};
+
+static struct max77802_clk *to_max77802_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct max77802_clk, hw);
+}
+
+static int max77802_clk_prepare(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+	return regmap_update_bits(max77802->iodev->regmap,
+				  MAX77802_REG_32KHZ, max77802->mask,
+				  max77802->mask);
+}
+
+static void max77802_clk_unprepare(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+	regmap_update_bits(max77802->iodev->regmap,
+		MAX77802_REG_32KHZ, max77802->mask, ~max77802->mask);
+}
+
+static int max77802_clk_is_prepared(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+	int ret;
+	u32 val;
+
+	ret = regmap_read(max77802->iodev->regmap,
+				MAX77802_REG_32KHZ, &val);
+
+	if (ret < 0)
+		return -EINVAL;
+
+	return val & max77802->mask;
+}
+
+static unsigned long max77802_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return 32768;
+}
+
+static struct clk_ops max77802_clk_ops = {
+	.prepare	= max77802_clk_prepare,
+	.unprepare	= max77802_clk_unprepare,
+	.is_prepared	= max77802_clk_is_prepared,
+	.recalc_rate	= max77802_recalc_rate,
+};
+
+static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = {
+	[MAX77802_CLK_32K_AP] = {
+		.name = "32khz_ap",
+		.ops = &max77802_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+	[MAX77802_CLK_32K_CP] = {
+		.name = "32khz_cp",
+		.ops = &max77802_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+};
+
+static struct clk *max77802_clk_register(struct device *dev,
+					struct max77802_clk *max77802)
+{
+	struct clk *clk;
+	struct clk_hw *hw = &max77802->hw;
+
+	clk = clk_register(dev, hw);
+	if (IS_ERR(clk))
+		return clk;
+
+	max77802->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
+	if (!max77802->lookup)
+		return ERR_PTR(-ENOMEM);
+
+	max77802->lookup->con_id = hw->init->name;
+	max77802->lookup->clk = clk;
+
+	clkdev_add(max77802->lookup);
+
+	return clk;
+}
+
+static int max77802_clk_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_clk *max77802_clks[MAX77802_CLKS_NUM];
+	struct clk **clocks;
+	int i, ret;
+
+	clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
+					* MAX77802_CLKS_NUM, GFP_KERNEL);
+	if (!clocks)
+		return -ENOMEM;
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		max77802_clks[i] = devm_kzalloc(&pdev->dev,
+					sizeof(struct max77802_clk), GFP_KERNEL);
+		if (!max77802_clks[i])
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		max77802_clks[i]->iodev = iodev;
+		max77802_clks[i]->mask = MAX77802_CLOCK_OPMODE_MASK << i;
+		max77802_clks[i]->hw.init = &max77802_clks_init[i];
+
+		clocks[i] = max77802_clk_register(&pdev->dev, max77802_clks[i]);
+		if (IS_ERR(clocks[i])) {
+			ret = PTR_ERR(clocks[i]);
+			dev_err(&pdev->dev, "failed to register %s\n",
+				max77802_clks[i]->hw.init->name);
+			goto err_clocks;
+		}
+	}
+
+	/* Enable low-jitter mode on the 32khz clocks. */
+	ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ,
+				 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
+				 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to enable low-jitter mode\n");
+		goto err_clocks;
+	}
+
+	platform_set_drvdata(pdev, clocks);
+
+	if (iodev->dev->of_node) {
+		struct clk_onecell_data *of_data;
+
+		of_data = devm_kzalloc(&pdev->dev,
+					sizeof(*of_data), GFP_KERNEL);
+		if (!of_data) {
+			ret = -ENOMEM;
+			goto err_clocks;
+		}
+
+		of_data->clks = clocks;
+		of_data->clk_num = MAX77802_CLKS_NUM;
+		ret = of_clk_add_provider(iodev->dev->of_node,
+					  of_clk_src_onecell_get, of_data);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register OF clock provider\n");
+			goto err_clocks;
+		}
+	}
+
+	return 0;
+
+err_clocks:
+	for (--i; i >= 0; --i) {
+		clkdev_drop(max77802_clks[i]->lookup);
+		clk_unregister(max77802_clks[i]->hw.clk);
+	}
+
+	return ret;
+}
+
+static int max77802_clk_remove(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct clk **clocks = platform_get_drvdata(pdev);
+	int i;
+
+	if (iodev->dev->of_node)
+		of_clk_del_provider(iodev->dev->of_node);
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		struct clk_hw *hw = __clk_get_hw(clocks[i]);
+		struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+		clkdev_drop(max77802->lookup);
+		clk_unregister(clocks[i]);
+	}
+	return 0;
+}
+
+static const struct platform_device_id max77802_clk_id[] = {
+	{ "max77802-clk", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77802_clk_id);
+
+static struct platform_driver max77802_clk_driver = {
+	.driver = {
+		.name  = "max77802-clk",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77802_clk_probe,
+	.remove = max77802_clk_remove,
+	.id_table = max77802_clk_id,
+};
+
+static int __init max77802_clk_init(void)
+{
+	return platform_driver_register(&max77802_clk_driver);
+}
+subsys_initcall(max77802_clk_init);
+
+static void __init max77802_clk_cleanup(void)
+{
+	platform_driver_unregister(&max77802_clk_driver);
+}
+module_exit(max77802_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
+MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
index 59696dd..33e8023 100644
--- a/drivers/mfd/max77802.c
+++ b/drivers/mfd/max77802.c
@@ -35,6 +35,9 @@
 
 static const struct mfd_cell max77802_devs[] = {
 	{ .name = "max77802-pmic", },
+#if defined(CONFIG_COMMON_CLK_MAX77802)
+	{ .name = "max77802-clk", },
+#endif
 };
 
 static bool max77802_pmic_is_accessible_reg(struct device *dev,
diff --git a/include/dt-bindings/clock/maxim,max77802.h b/include/dt-bindings/clock/maxim,max77802.h
new file mode 100644
index 0000000..997312e
--- /dev/null
+++ b/include/dt-bindings/clock/maxim,max77802.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Device Tree binding constants clocks for the Maxim 77802 PMIC.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H
+#define _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H
+
+/* Fixed rate clocks. */
+
+#define MAX77802_CLK_32K_AP		0
+#define MAX77802_CLK_32K_CP		1
+
+/* Total number of clocks. */
+#define MAX77802_CLKS_NUM		(MAX77802_CLK_32K_CP + 1)
+
+#endif /* _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H */
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
Low Jitter Mode. This patch adds support for these two clocks.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
 drivers/clk/Kconfig                                |   6 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
 drivers/mfd/max77802.c                             |   3 +
 include/dt-bindings/clock/maxim,max77802.h         |  22 ++
 6 files changed, 325 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
 create mode 100644 drivers/clk/clk-max77802.c
 create mode 100644 include/dt-bindings/clock/maxim,max77802.h

diff --git a/Documentation/devicetree/bindings/clock/maxim,max77802.txt b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
new file mode 100644
index 0000000..1d3fb64
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/maxim,max77802.txt
@@ -0,0 +1,40 @@
+Binding for Maxim MAX77802 32k clock generator block
+
+This is a part of device tree bindings of MAX77802 multi-function device.
+More information can be found in bindings/mfd/max77802.txt file.
+
+The MAX77802 contains two 32.768khz clock outputs that can be controlled
+(gated/ungated) over I2C.
+
+Following properties should be presend in main device node of the MFD chip.
+
+Required properties:
+
+- #clock-cells: from common clock binding; shall be set to 1.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume.
+
+Clocks are defined as preprocessor macros in dt-bindings/clock/maxim,max77802.h
+header and can be used in device tree sources.
+
+Example: Node of the MFD chip
+
+	max77802: max77802 at 09 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&wakeup_eint>;
+		interrupts = <26 0>;
+		reg = <0x09>;
+		#clock-cells = <1>;
+
+		/* ... */
+	};
+
+Example: Clock consumer node
+
+	foo at 0 {
+		compatible = "bar,foo";
+		/* ... */
+		clock-names = "32khz_ap";
+		clocks = <&max77802 MAX77802_CLK_32K_AP>;
+	};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 9f9c5ae..74c71a4 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -38,6 +38,12 @@ config COMMON_CLK_MAX77686
 	---help---
 	  This driver supports Maxim 77686 crystal oscillator clock. 
 
+config COMMON_CLK_MAX77802
+	tristate "Clock driver for Maxim 77802 MFD"
+	depends on MFD_MAX77802
+	---help---
+	  This driver supports Maxim 77802 crystal oscillator clock.
+
 config COMMON_CLK_SI5351
 	tristate "Clock driver for SiLabs 5351A/B/C"
 	depends on I2C
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..677692f 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_EFM32)		+= clk-efm32gg.o
 obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
 obj-$(CONFIG_MACH_LOONGSON1)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_MAX77802)	+= clk-max77802.o
 obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
 obj-$(CONFIG_ARCH_NOMADIK)		+= clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)		+= clk-nspire.o
diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c
new file mode 100644
index 0000000..a98fc29
--- /dev/null
+++ b/drivers/clk/clk-max77802.c
@@ -0,0 +1,253 @@
+/*
+ * clk-max77802.c - Clock driver for Maxim 77802
+ *
+ * Copyright (C) 2014 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * 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 clk-max77686.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77802.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+#include <dt-bindings/clock/maxim,max77802.h>
+
+#define MAX77802_CLOCK_OPMODE_MASK	0x1
+#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
+
+struct max77802_clk {
+	struct max77802_dev *iodev;
+	u32 mask;
+	struct clk_hw hw;
+	struct clk_lookup *lookup;
+};
+
+static struct max77802_clk *to_max77802_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct max77802_clk, hw);
+}
+
+static int max77802_clk_prepare(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+	return regmap_update_bits(max77802->iodev->regmap,
+				  MAX77802_REG_32KHZ, max77802->mask,
+				  max77802->mask);
+}
+
+static void max77802_clk_unprepare(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+	regmap_update_bits(max77802->iodev->regmap,
+		MAX77802_REG_32KHZ, max77802->mask, ~max77802->mask);
+}
+
+static int max77802_clk_is_prepared(struct clk_hw *hw)
+{
+	struct max77802_clk *max77802 = to_max77802_clk(hw);
+	int ret;
+	u32 val;
+
+	ret = regmap_read(max77802->iodev->regmap,
+				MAX77802_REG_32KHZ, &val);
+
+	if (ret < 0)
+		return -EINVAL;
+
+	return val & max77802->mask;
+}
+
+static unsigned long max77802_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	return 32768;
+}
+
+static struct clk_ops max77802_clk_ops = {
+	.prepare	= max77802_clk_prepare,
+	.unprepare	= max77802_clk_unprepare,
+	.is_prepared	= max77802_clk_is_prepared,
+	.recalc_rate	= max77802_recalc_rate,
+};
+
+static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = {
+	[MAX77802_CLK_32K_AP] = {
+		.name = "32khz_ap",
+		.ops = &max77802_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+	[MAX77802_CLK_32K_CP] = {
+		.name = "32khz_cp",
+		.ops = &max77802_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+};
+
+static struct clk *max77802_clk_register(struct device *dev,
+					struct max77802_clk *max77802)
+{
+	struct clk *clk;
+	struct clk_hw *hw = &max77802->hw;
+
+	clk = clk_register(dev, hw);
+	if (IS_ERR(clk))
+		return clk;
+
+	max77802->lookup = kzalloc(sizeof(struct clk_lookup), GFP_KERNEL);
+	if (!max77802->lookup)
+		return ERR_PTR(-ENOMEM);
+
+	max77802->lookup->con_id = hw->init->name;
+	max77802->lookup->clk = clk;
+
+	clkdev_add(max77802->lookup);
+
+	return clk;
+}
+
+static int max77802_clk_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_clk *max77802_clks[MAX77802_CLKS_NUM];
+	struct clk **clocks;
+	int i, ret;
+
+	clocks = devm_kzalloc(&pdev->dev, sizeof(struct clk *)
+					* MAX77802_CLKS_NUM, GFP_KERNEL);
+	if (!clocks)
+		return -ENOMEM;
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		max77802_clks[i] = devm_kzalloc(&pdev->dev,
+					sizeof(struct max77802_clk), GFP_KERNEL);
+		if (!max77802_clks[i])
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		max77802_clks[i]->iodev = iodev;
+		max77802_clks[i]->mask = MAX77802_CLOCK_OPMODE_MASK << i;
+		max77802_clks[i]->hw.init = &max77802_clks_init[i];
+
+		clocks[i] = max77802_clk_register(&pdev->dev, max77802_clks[i]);
+		if (IS_ERR(clocks[i])) {
+			ret = PTR_ERR(clocks[i]);
+			dev_err(&pdev->dev, "failed to register %s\n",
+				max77802_clks[i]->hw.init->name);
+			goto err_clocks;
+		}
+	}
+
+	/* Enable low-jitter mode on the 32khz clocks. */
+	ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ,
+				 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
+				 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to enable low-jitter mode\n");
+		goto err_clocks;
+	}
+
+	platform_set_drvdata(pdev, clocks);
+
+	if (iodev->dev->of_node) {
+		struct clk_onecell_data *of_data;
+
+		of_data = devm_kzalloc(&pdev->dev,
+					sizeof(*of_data), GFP_KERNEL);
+		if (!of_data) {
+			ret = -ENOMEM;
+			goto err_clocks;
+		}
+
+		of_data->clks = clocks;
+		of_data->clk_num = MAX77802_CLKS_NUM;
+		ret = of_clk_add_provider(iodev->dev->of_node,
+					  of_clk_src_onecell_get, of_data);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register OF clock provider\n");
+			goto err_clocks;
+		}
+	}
+
+	return 0;
+
+err_clocks:
+	for (--i; i >= 0; --i) {
+		clkdev_drop(max77802_clks[i]->lookup);
+		clk_unregister(max77802_clks[i]->hw.clk);
+	}
+
+	return ret;
+}
+
+static int max77802_clk_remove(struct platform_device *pdev)
+{
+	struct max77802_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct clk **clocks = platform_get_drvdata(pdev);
+	int i;
+
+	if (iodev->dev->of_node)
+		of_clk_del_provider(iodev->dev->of_node);
+
+	for (i = 0; i < MAX77802_CLKS_NUM; i++) {
+		struct clk_hw *hw = __clk_get_hw(clocks[i]);
+		struct max77802_clk *max77802 = to_max77802_clk(hw);
+
+		clkdev_drop(max77802->lookup);
+		clk_unregister(clocks[i]);
+	}
+	return 0;
+}
+
+static const struct platform_device_id max77802_clk_id[] = {
+	{ "max77802-clk", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77802_clk_id);
+
+static struct platform_driver max77802_clk_driver = {
+	.driver = {
+		.name  = "max77802-clk",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77802_clk_probe,
+	.remove = max77802_clk_remove,
+	.id_table = max77802_clk_id,
+};
+
+static int __init max77802_clk_init(void)
+{
+	return platform_driver_register(&max77802_clk_driver);
+}
+subsys_initcall(max77802_clk_init);
+
+static void __init max77802_clk_cleanup(void)
+{
+	platform_driver_unregister(&max77802_clk_driver);
+}
+module_exit(max77802_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
+MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
index 59696dd..33e8023 100644
--- a/drivers/mfd/max77802.c
+++ b/drivers/mfd/max77802.c
@@ -35,6 +35,9 @@
 
 static const struct mfd_cell max77802_devs[] = {
 	{ .name = "max77802-pmic", },
+#if defined(CONFIG_COMMON_CLK_MAX77802)
+	{ .name = "max77802-clk", },
+#endif
 };
 
 static bool max77802_pmic_is_accessible_reg(struct device *dev,
diff --git a/include/dt-bindings/clock/maxim,max77802.h b/include/dt-bindings/clock/maxim,max77802.h
new file mode 100644
index 0000000..997312e
--- /dev/null
+++ b/include/dt-bindings/clock/maxim,max77802.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Device Tree binding constants clocks for the Maxim 77802 PMIC.
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H
+#define _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H
+
+/* Fixed rate clocks. */
+
+#define MAX77802_CLK_32K_AP		0
+#define MAX77802_CLK_32K_CP		1
+
+/* Total number of clocks. */
+#define MAX77802_CLKS_NUM		(MAX77802_CLK_32K_CP + 1)
+
+#endif /* _DT_BINDINGS_CLOCK_MAXIM_MAX77802_CLOCK_H */
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
  2014-06-09  9:37 ` Javier Martinez Canillas
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel,
	Javier Martinez Canillas

The MAX7802 PMIC has a Real-Time-Clock (RTC) with two alarms.
This patch adds support for the RTC 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>
---
 drivers/mfd/max77802.c     |   3 +
 drivers/rtc/Kconfig        |  10 +
 drivers/rtc/Makefile       |   1 +
 drivers/rtc/rtc-max77802.c | 632 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 646 insertions(+)
 create mode 100644 drivers/rtc/rtc-max77802.c

diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
index 33e8023..62127fb 100644
--- a/drivers/mfd/max77802.c
+++ b/drivers/mfd/max77802.c
@@ -35,6 +35,9 @@
 
 static const struct mfd_cell max77802_devs[] = {
 	{ .name = "max77802-pmic", },
+#if defined(CONFIG_RTC_DRV_MAX77802)
+	{ .name = "max77802-rtc", },
+#endif
 #if defined(CONFIG_COMMON_CLK_MAX77802)
 	{ .name = "max77802-clk", },
 #endif
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0754f5c..e0b6495 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -288,6 +288,16 @@ config RTC_DRV_MAX77686
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-max77686.
 
+config RTC_DRV_MAX77802
+	tristate "Maxim MAX77802"
+	depends on MFD_MAX77802
+	help
+	  If you say yes here you will get support for the
+	  RTC of Maxim MAX77802 PMIC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-max77802.
+
 config RTC_DRV_RS5C372
 	tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
 	help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 70347d0..247de78 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_MAX8998)	+= rtc-max8998.o
 obj-$(CONFIG_RTC_DRV_MAX8997)	+= rtc-max8997.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
 obj-$(CONFIG_RTC_DRV_MAX77686)	+= rtc-max77686.o
+obj-$(CONFIG_RTC_DRV_MAX77802)  += rtc-max77802.o
 obj-$(CONFIG_RTC_DRV_MC13XXX)	+= rtc-mc13xxx.o
 obj-$(CONFIG_RTC_DRV_MCP795)	+= rtc-mcp795.o
 obj-$(CONFIG_RTC_DRV_MSM6242)	+= rtc-msm6242.o
diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c
new file mode 100644
index 0000000..cb85b1d
--- /dev/null
+++ b/drivers/rtc/rtc-max77802.c
@@ -0,0 +1,632 @@
+/*
+ * RTC driver for Maxim MAX77802
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ *  based on rtc-max8997.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 the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT			0
+#define BCD_EN_MASK			(1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT			1
+#define MODEL24_MASK			(1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT			0
+#define RTC_UDR_MASK			(1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT			4
+#define RTC_RBUDR_MASK			(1 << RTC_RBUDR_SHIFT)
+/* WTSR and SMPL Register */
+#define WTSRT_SHIFT			0
+#define SMPLT_SHIFT			2
+#define WTSR_EN_SHIFT			6
+#define SMPL_EN_SHIFT			7
+#define WTSRT_MASK			(3 << WTSRT_SHIFT)
+#define SMPLT_MASK			(3 << SMPLT_SHIFT)
+#define WTSR_EN_MASK			(1 << WTSR_EN_SHIFT)
+#define SMPL_EN_MASK			(1 << SMPL_EN_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT			6
+#define HOUR_PM_MASK			(1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT		7
+#define ALARM_ENABLE_MASK		(1 << ALARM_ENABLE_SHIFT)
+
+/* For the RTCAE1 register, we write this value to enable the alarm */
+#define ALARM_ENABLE_VALUE		0x77
+
+#define MAX77802_RTC_UPDATE_DELAY_US	200
+#undef MAX77802_RTC_WTSR_SMPL
+
+enum {
+	RTC_SEC = 0,
+	RTC_MIN,
+	RTC_HOUR,
+	RTC_WEEKDAY,
+	RTC_MONTH,
+	RTC_YEAR,
+	RTC_DATE,
+	RTC_NR_TIME
+};
+
+struct max77802_rtc_info {
+	struct device		*dev;
+	struct max77802_dev	*max77802;
+	struct i2c_client	*rtc;
+	struct rtc_device	*rtc_dev;
+	struct mutex		lock;
+
+	struct regmap		*regmap;
+
+	int virq;
+	int rtc_24hr_mode;
+};
+
+enum MAX77802_RTC_OP {
+	MAX77802_RTC_WRITE,
+	MAX77802_RTC_READ,
+};
+
+static inline int max77802_rtc_calculate_wday(u8 shifted)
+{
+	int counter = -1;
+
+	while (shifted) {
+		shifted >>= 1;
+		counter++;
+	}
+
+	return counter;
+}
+
+static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+				   int rtc_24hr_mode)
+{
+	tm->tm_sec = data[RTC_SEC] & 0xff;
+	tm->tm_min = data[RTC_MIN] & 0xff;
+	if (rtc_24hr_mode)
+		tm->tm_hour = data[RTC_HOUR] & 0x1f;
+	else {
+		tm->tm_hour = data[RTC_HOUR] & 0x0f;
+		if (data[RTC_HOUR] & HOUR_PM_MASK)
+			tm->tm_hour += 12;
+	}
+
+	tm->tm_wday = max77802_rtc_calculate_wday(data[RTC_WEEKDAY] & 0xff);
+	tm->tm_mday = data[RTC_DATE] & 0x1f;
+	tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+
+	tm->tm_year = data[RTC_YEAR] & 0xff;
+	tm->tm_yday = 0;
+	tm->tm_isdst = 0;
+}
+
+static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+	data[RTC_SEC] = tm->tm_sec;
+	data[RTC_MIN] = tm->tm_min;
+	data[RTC_HOUR] = tm->tm_hour;
+	data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+	data[RTC_DATE] = tm->tm_mday;
+	data[RTC_MONTH] = tm->tm_mon + 1;
+	data[RTC_YEAR] = tm->tm_year;
+
+	return 0;
+}
+
+static int max77802_rtc_update(struct max77802_rtc_info *info,
+	enum MAX77802_RTC_OP op)
+{
+	int ret;
+	unsigned int data;
+
+	if (op == MAX77802_RTC_WRITE)
+		data = 1 << RTC_UDR_SHIFT;
+	else
+		data = 1 << RTC_RBUDR_SHIFT;
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_RTC_UPDATE0, data, data);
+	if (ret < 0)
+		dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
+				__func__, ret, data);
+	else {
+		/* Minimum delay required before RTC update. */
+		usleep_range(MAX77802_RTC_UPDATE_DELAY_US,
+			     MAX77802_RTC_UPDATE_DELAY_US * 2);
+	}
+
+	return ret;
+}
+
+static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	int ret;
+
+	mutex_lock(&info->lock);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_bulk_read(info->max77802->regmap,
+				MAX77802_RTC_SEC, data, RTC_NR_TIME);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,	ret);
+		goto out;
+	}
+
+	max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+	ret = rtc_valid_tm(tm);
+
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	int ret;
+
+	ret = max77802_rtc_tm_to_data(tm, data);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&info->lock);
+
+	ret = regmap_bulk_write(info->max77802->regmap,
+				 MAX77802_RTC_SEC, data, RTC_NR_TIME);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+				ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	unsigned int val;
+	int ret;
+
+	mutex_lock(&info->lock);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_bulk_read(info->max77802->regmap,
+				 MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+				__func__, __LINE__, ret);
+		goto out;
+	}
+
+	max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+	alrm->enabled = 0;
+	ret = regmap_read(info->max77802->regmap,
+			  MAX77802_RTC_AE1, &val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n",
+			__func__, __LINE__, ret);
+		goto out;
+	}
+	if (val)
+		alrm->enabled = 1;
+
+	alrm->pending = 0;
+	ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
+				__func__, __LINE__, ret);
+		goto out;
+	}
+
+	if (val & (1 << 2)) /* RTCA1 */
+		alrm->pending = 1;
+
+out:
+	mutex_unlock(&info->lock);
+	return 0;
+}
+
+static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info)
+{
+	int ret;
+
+	if (!mutex_is_locked(&info->lock))
+		dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_write(info->max77802->regmap,
+			   MAX77802_RTC_AE1, 0);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+			__func__, ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+	return ret;
+}
+
+static int max77802_rtc_start_alarm(struct max77802_rtc_info *info)
+{
+	int ret;
+
+	if (!mutex_is_locked(&info->lock))
+		dev_warn(info->dev, "%s: should have mutex locked\n",
+			 __func__);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_write(info->max77802->regmap,
+				   MAX77802_RTC_AE1,
+				   ALARM_ENABLE_VALUE);
+
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+				__func__, ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+	return ret;
+}
+
+static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	int ret;
+
+	ret = max77802_rtc_tm_to_data(&alrm->time, data);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&info->lock);
+
+	ret = max77802_rtc_stop_alarm(info);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_bulk_write(info->max77802->regmap,
+				 MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+				__func__, ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+	if (ret < 0)
+		goto out;
+
+	if (alrm->enabled)
+		ret = max77802_rtc_start_alarm(info);
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77802_rtc_alarm_irq_enable(struct device *dev,
+					 unsigned int enabled)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&info->lock);
+	if (enabled)
+		ret = max77802_rtc_start_alarm(info);
+	else
+		ret = max77802_rtc_stop_alarm(info);
+	mutex_unlock(&info->lock);
+
+	return ret;
+}
+
+static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data)
+{
+	struct max77802_rtc_info *info = data;
+
+	dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77802_rtc_ops = {
+	.read_time = max77802_rtc_read_time,
+	.set_time = max77802_rtc_set_time,
+	.read_alarm = max77802_rtc_read_alarm,
+	.set_alarm = max77802_rtc_set_alarm,
+	.alarm_irq_enable = max77802_rtc_alarm_irq_enable,
+};
+
+#ifdef MAX77802_RTC_WTSR_SMPL
+static void max77802_rtc_enable_wtsr(struct max77802_rtc_info *info, bool enable)
+{
+	int ret;
+	unsigned int val, mask;
+
+	if (enable)
+		val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
+	else
+		val = 0;
+
+	mask = WTSR_EN_MASK | WTSRT_MASK;
+
+	dev_info(info->dev, "%s: %s WTSR\n", __func__,
+			enable ? "enable" : "disable");
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_WTSR_SMPL_CNTL, mask, val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+
+	max77802_rtc_update(info, MAX77802_RTC_WRITE);
+}
+
+static void max77802_rtc_enable_smpl(struct max77802_rtc_info *info, bool enable)
+{
+	int ret;
+	unsigned int val, mask;
+
+	if (enable)
+		val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
+	else
+		val = 0;
+
+	mask = SMPL_EN_MASK | SMPLT_MASK;
+
+	dev_info(info->dev, "%s: %s SMPL\n", __func__,
+			enable ? "enable" : "disable");
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_WTSR_SMPL_CNTL, mask, val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+
+	max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+	val = 0;
+	regmap_read(info->max77802->regmap, MAX77802_WTSR_SMPL_CNTL, &val);
+	dev_info(info->dev, "%s: WTSR_SMPL(0x%02x)\n", __func__, val);
+}
+#endif /* MAX77802_RTC_WTSR_SMPL */
+
+static int max77802_rtc_init_reg(struct max77802_rtc_info *info)
+{
+	u8 data[2];
+	int ret;
+
+	max77802_rtc_update(info, MAX77802_RTC_READ);
+
+	/* Set RTC control register : Binary mode, 24hour mdoe */
+	data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+	data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+	info->rtc_24hr_mode = 1;
+
+	ret = regmap_bulk_write(info->max77802->regmap,
+				MAX77802_RTC_CONTROLM, data, 2);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+	/* Mask control register */
+	max77802_rtc_update(info, MAX77802_RTC_READ);
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_RTC_CONTROLM, 0x0, 0x3);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to mask CONTROLM reg(%d)\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+	return ret;
+}
+
+static int max77802_rtc_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *max77802 = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_rtc_info *info;
+	int ret, virq;
+
+	dev_info(&pdev->dev, "%s\n", __func__);
+
+	info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info),
+				GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	mutex_init(&info->lock);
+	info->dev = &pdev->dev;
+	info->max77802 = max77802;
+	info->rtc = max77802->i2c;
+
+	platform_set_drvdata(pdev, info);
+
+	ret = max77802_rtc_init_reg(info);
+
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+		return ret;
+	}
+
+#ifdef MAX77802_RTC_WTSR_SMPL
+	max77802_rtc_enable_wtsr(info, true);
+	max77802_rtc_enable_smpl(info, true);
+#endif
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc",
+					&max77802_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(info->rtc_dev)) {
+		dev_info(&pdev->dev, "%s: fail\n", __func__);
+
+		ret = PTR_ERR(info->rtc_dev);
+		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+		if (ret == 0)
+			ret = -EINVAL;
+		return ret;
+	}
+	virq = irq_create_mapping(max77802->irq_domain, MAX77802_RTCIRQ_RTCA1);
+	if (!virq) {
+		ret = -ENXIO;
+		return ret;
+	}
+	info->virq = virq;
+
+	ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
+				max77802_rtc_alarm_irq, 0, "rtc-alarm1", info);
+	if (ret < 0)
+		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+			info->virq, ret);
+
+	return ret;
+}
+
+static int max77802_rtc_remove(struct platform_device *pdev)
+{
+	struct max77802_rtc_info *info = platform_get_drvdata(pdev);
+
+	free_irq(info->virq, info);
+	rtc_device_unregister(info->rtc_dev);
+
+	return 0;
+}
+
+static void max77802_rtc_shutdown(struct platform_device *pdev)
+{
+#ifdef MAX77802_RTC_WTSR_SMPL
+	struct max77802_rtc_info *info = platform_get_drvdata(pdev);
+	int i;
+	u8 val = 0;
+
+	for (i = 0; i < 3; i++) {
+		max77802_rtc_enable_wtsr(info, false);
+		regmap_read(info->max77802->regmap,
+			    MAX77802_WTSR_SMPL_CNTL, &val);
+		dev_info(info->dev, "%s: WTSR_SMPL reg(0x%02x)\n", __func__,
+				val);
+		if (val & WTSR_EN_MASK) {
+			dev_emerg(info->dev, "%s: fail to disable WTSR\n",
+					__func__);
+		} else {
+			dev_info(info->dev, "%s: success to disable WTSR\n",
+					__func__);
+			break;
+		}
+	}
+
+	/* Disable SMPL when power off */
+	max77802_rtc_enable_smpl(info, false);
+#endif /* MAX77802_RTC_WTSR_SMPL */
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77802_rtc_suspend(struct device *dev)
+{
+	if (device_may_wakeup(dev)) {
+		struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+		return enable_irq_wake(info->virq);
+	}
+
+	return 0;
+}
+
+static int max77802_rtc_resume(struct device *dev)
+{
+	if (device_may_wakeup(dev)) {
+		struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+		return disable_irq_wake(info->virq);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops,
+			 max77802_rtc_suspend, max77802_rtc_resume);
+
+static const struct platform_device_id rtc_id[] = {
+	{ "max77802-rtc", 0 },
+	{},
+};
+
+static struct platform_driver max77802_rtc_driver = {
+	.driver		= {
+		.name	= "max77802-rtc",
+		.owner	= THIS_MODULE,
+		.pm	= &max77802_rtc_pm_ops,
+	},
+	.probe		= max77802_rtc_probe,
+	.remove		= max77802_rtc_remove,
+	.shutdown	= max77802_rtc_shutdown,
+	.id_table	= rtc_id,
+};
+
+module_platform_driver(max77802_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77802 RTC driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
-- 
2.0.0.rc2


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

The MAX7802 PMIC has a Real-Time-Clock (RTC) with two alarms.
This patch adds support for the RTC 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>
---
 drivers/mfd/max77802.c     |   3 +
 drivers/rtc/Kconfig        |  10 +
 drivers/rtc/Makefile       |   1 +
 drivers/rtc/rtc-max77802.c | 632 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 646 insertions(+)
 create mode 100644 drivers/rtc/rtc-max77802.c

diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
index 33e8023..62127fb 100644
--- a/drivers/mfd/max77802.c
+++ b/drivers/mfd/max77802.c
@@ -35,6 +35,9 @@
 
 static const struct mfd_cell max77802_devs[] = {
 	{ .name = "max77802-pmic", },
+#if defined(CONFIG_RTC_DRV_MAX77802)
+	{ .name = "max77802-rtc", },
+#endif
 #if defined(CONFIG_COMMON_CLK_MAX77802)
 	{ .name = "max77802-clk", },
 #endif
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0754f5c..e0b6495 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -288,6 +288,16 @@ config RTC_DRV_MAX77686
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-max77686.
 
+config RTC_DRV_MAX77802
+	tristate "Maxim MAX77802"
+	depends on MFD_MAX77802
+	help
+	  If you say yes here you will get support for the
+	  RTC of Maxim MAX77802 PMIC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-max77802.
+
 config RTC_DRV_RS5C372
 	tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
 	help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 70347d0..247de78 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_MAX8998)	+= rtc-max8998.o
 obj-$(CONFIG_RTC_DRV_MAX8997)	+= rtc-max8997.o
 obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
 obj-$(CONFIG_RTC_DRV_MAX77686)	+= rtc-max77686.o
+obj-$(CONFIG_RTC_DRV_MAX77802)  += rtc-max77802.o
 obj-$(CONFIG_RTC_DRV_MC13XXX)	+= rtc-mc13xxx.o
 obj-$(CONFIG_RTC_DRV_MCP795)	+= rtc-mcp795.o
 obj-$(CONFIG_RTC_DRV_MSM6242)	+= rtc-msm6242.o
diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c
new file mode 100644
index 0000000..cb85b1d
--- /dev/null
+++ b/drivers/rtc/rtc-max77802.c
@@ -0,0 +1,632 @@
+/*
+ * RTC driver for Maxim MAX77802
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ *  based on rtc-max8997.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 the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77802-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT			0
+#define BCD_EN_MASK			(1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT			1
+#define MODEL24_MASK			(1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT			0
+#define RTC_UDR_MASK			(1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT			4
+#define RTC_RBUDR_MASK			(1 << RTC_RBUDR_SHIFT)
+/* WTSR and SMPL Register */
+#define WTSRT_SHIFT			0
+#define SMPLT_SHIFT			2
+#define WTSR_EN_SHIFT			6
+#define SMPL_EN_SHIFT			7
+#define WTSRT_MASK			(3 << WTSRT_SHIFT)
+#define SMPLT_MASK			(3 << SMPLT_SHIFT)
+#define WTSR_EN_MASK			(1 << WTSR_EN_SHIFT)
+#define SMPL_EN_MASK			(1 << SMPL_EN_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT			6
+#define HOUR_PM_MASK			(1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT		7
+#define ALARM_ENABLE_MASK		(1 << ALARM_ENABLE_SHIFT)
+
+/* For the RTCAE1 register, we write this value to enable the alarm */
+#define ALARM_ENABLE_VALUE		0x77
+
+#define MAX77802_RTC_UPDATE_DELAY_US	200
+#undef MAX77802_RTC_WTSR_SMPL
+
+enum {
+	RTC_SEC = 0,
+	RTC_MIN,
+	RTC_HOUR,
+	RTC_WEEKDAY,
+	RTC_MONTH,
+	RTC_YEAR,
+	RTC_DATE,
+	RTC_NR_TIME
+};
+
+struct max77802_rtc_info {
+	struct device		*dev;
+	struct max77802_dev	*max77802;
+	struct i2c_client	*rtc;
+	struct rtc_device	*rtc_dev;
+	struct mutex		lock;
+
+	struct regmap		*regmap;
+
+	int virq;
+	int rtc_24hr_mode;
+};
+
+enum MAX77802_RTC_OP {
+	MAX77802_RTC_WRITE,
+	MAX77802_RTC_READ,
+};
+
+static inline int max77802_rtc_calculate_wday(u8 shifted)
+{
+	int counter = -1;
+
+	while (shifted) {
+		shifted >>= 1;
+		counter++;
+	}
+
+	return counter;
+}
+
+static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+				   int rtc_24hr_mode)
+{
+	tm->tm_sec = data[RTC_SEC] & 0xff;
+	tm->tm_min = data[RTC_MIN] & 0xff;
+	if (rtc_24hr_mode)
+		tm->tm_hour = data[RTC_HOUR] & 0x1f;
+	else {
+		tm->tm_hour = data[RTC_HOUR] & 0x0f;
+		if (data[RTC_HOUR] & HOUR_PM_MASK)
+			tm->tm_hour += 12;
+	}
+
+	tm->tm_wday = max77802_rtc_calculate_wday(data[RTC_WEEKDAY] & 0xff);
+	tm->tm_mday = data[RTC_DATE] & 0x1f;
+	tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+
+	tm->tm_year = data[RTC_YEAR] & 0xff;
+	tm->tm_yday = 0;
+	tm->tm_isdst = 0;
+}
+
+static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+	data[RTC_SEC] = tm->tm_sec;
+	data[RTC_MIN] = tm->tm_min;
+	data[RTC_HOUR] = tm->tm_hour;
+	data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+	data[RTC_DATE] = tm->tm_mday;
+	data[RTC_MONTH] = tm->tm_mon + 1;
+	data[RTC_YEAR] = tm->tm_year;
+
+	return 0;
+}
+
+static int max77802_rtc_update(struct max77802_rtc_info *info,
+	enum MAX77802_RTC_OP op)
+{
+	int ret;
+	unsigned int data;
+
+	if (op == MAX77802_RTC_WRITE)
+		data = 1 << RTC_UDR_SHIFT;
+	else
+		data = 1 << RTC_RBUDR_SHIFT;
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_RTC_UPDATE0, data, data);
+	if (ret < 0)
+		dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
+				__func__, ret, data);
+	else {
+		/* Minimum delay required before RTC update. */
+		usleep_range(MAX77802_RTC_UPDATE_DELAY_US,
+			     MAX77802_RTC_UPDATE_DELAY_US * 2);
+	}
+
+	return ret;
+}
+
+static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	int ret;
+
+	mutex_lock(&info->lock);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_bulk_read(info->max77802->regmap,
+				MAX77802_RTC_SEC, data, RTC_NR_TIME);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,	ret);
+		goto out;
+	}
+
+	max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+	ret = rtc_valid_tm(tm);
+
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	int ret;
+
+	ret = max77802_rtc_tm_to_data(tm, data);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&info->lock);
+
+	ret = regmap_bulk_write(info->max77802->regmap,
+				 MAX77802_RTC_SEC, data, RTC_NR_TIME);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+				ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	unsigned int val;
+	int ret;
+
+	mutex_lock(&info->lock);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_bulk_read(info->max77802->regmap,
+				 MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+				__func__, __LINE__, ret);
+		goto out;
+	}
+
+	max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+	alrm->enabled = 0;
+	ret = regmap_read(info->max77802->regmap,
+			  MAX77802_RTC_AE1, &val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n",
+			__func__, __LINE__, ret);
+		goto out;
+	}
+	if (val)
+		alrm->enabled = 1;
+
+	alrm->pending = 0;
+	ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
+				__func__, __LINE__, ret);
+		goto out;
+	}
+
+	if (val & (1 << 2)) /* RTCA1 */
+		alrm->pending = 1;
+
+out:
+	mutex_unlock(&info->lock);
+	return 0;
+}
+
+static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info)
+{
+	int ret;
+
+	if (!mutex_is_locked(&info->lock))
+		dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_write(info->max77802->regmap,
+			   MAX77802_RTC_AE1, 0);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+			__func__, ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+	return ret;
+}
+
+static int max77802_rtc_start_alarm(struct max77802_rtc_info *info)
+{
+	int ret;
+
+	if (!mutex_is_locked(&info->lock))
+		dev_warn(info->dev, "%s: should have mutex locked\n",
+			 __func__);
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_write(info->max77802->regmap,
+				   MAX77802_RTC_AE1,
+				   ALARM_ENABLE_VALUE);
+
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+				__func__, ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+	return ret;
+}
+
+static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	u8 data[RTC_NR_TIME];
+	int ret;
+
+	ret = max77802_rtc_tm_to_data(&alrm->time, data);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&info->lock);
+
+	ret = max77802_rtc_stop_alarm(info);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_bulk_write(info->max77802->regmap,
+				 MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+				__func__, ret);
+		goto out;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+	if (ret < 0)
+		goto out;
+
+	if (alrm->enabled)
+		ret = max77802_rtc_start_alarm(info);
+out:
+	mutex_unlock(&info->lock);
+	return ret;
+}
+
+static int max77802_rtc_alarm_irq_enable(struct device *dev,
+					 unsigned int enabled)
+{
+	struct max77802_rtc_info *info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&info->lock);
+	if (enabled)
+		ret = max77802_rtc_start_alarm(info);
+	else
+		ret = max77802_rtc_stop_alarm(info);
+	mutex_unlock(&info->lock);
+
+	return ret;
+}
+
+static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data)
+{
+	struct max77802_rtc_info *info = data;
+
+	dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+	rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77802_rtc_ops = {
+	.read_time = max77802_rtc_read_time,
+	.set_time = max77802_rtc_set_time,
+	.read_alarm = max77802_rtc_read_alarm,
+	.set_alarm = max77802_rtc_set_alarm,
+	.alarm_irq_enable = max77802_rtc_alarm_irq_enable,
+};
+
+#ifdef MAX77802_RTC_WTSR_SMPL
+static void max77802_rtc_enable_wtsr(struct max77802_rtc_info *info, bool enable)
+{
+	int ret;
+	unsigned int val, mask;
+
+	if (enable)
+		val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
+	else
+		val = 0;
+
+	mask = WTSR_EN_MASK | WTSRT_MASK;
+
+	dev_info(info->dev, "%s: %s WTSR\n", __func__,
+			enable ? "enable" : "disable");
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_WTSR_SMPL_CNTL, mask, val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+
+	max77802_rtc_update(info, MAX77802_RTC_WRITE);
+}
+
+static void max77802_rtc_enable_smpl(struct max77802_rtc_info *info, bool enable)
+{
+	int ret;
+	unsigned int val, mask;
+
+	if (enable)
+		val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
+	else
+		val = 0;
+
+	mask = SMPL_EN_MASK | SMPLT_MASK;
+
+	dev_info(info->dev, "%s: %s SMPL\n", __func__,
+			enable ? "enable" : "disable");
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_WTSR_SMPL_CNTL, mask, val);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
+				__func__, ret);
+		return;
+	}
+
+	max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+	val = 0;
+	regmap_read(info->max77802->regmap, MAX77802_WTSR_SMPL_CNTL, &val);
+	dev_info(info->dev, "%s: WTSR_SMPL(0x%02x)\n", __func__, val);
+}
+#endif /* MAX77802_RTC_WTSR_SMPL */
+
+static int max77802_rtc_init_reg(struct max77802_rtc_info *info)
+{
+	u8 data[2];
+	int ret;
+
+	max77802_rtc_update(info, MAX77802_RTC_READ);
+
+	/* Set RTC control register : Binary mode, 24hour mdoe */
+	data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+	data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+	info->rtc_24hr_mode = 1;
+
+	ret = regmap_bulk_write(info->max77802->regmap,
+				MAX77802_RTC_CONTROLM, data, 2);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+	/* Mask control register */
+	max77802_rtc_update(info, MAX77802_RTC_READ);
+
+	ret = regmap_update_bits(info->max77802->regmap,
+				 MAX77802_RTC_CONTROLM, 0x0, 0x3);
+	if (ret < 0) {
+		dev_err(info->dev, "%s: fail to mask CONTROLM reg(%d)\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+	return ret;
+}
+
+static int max77802_rtc_probe(struct platform_device *pdev)
+{
+	struct max77802_dev *max77802 = dev_get_drvdata(pdev->dev.parent);
+	struct max77802_rtc_info *info;
+	int ret, virq;
+
+	dev_info(&pdev->dev, "%s\n", __func__);
+
+	info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info),
+				GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	mutex_init(&info->lock);
+	info->dev = &pdev->dev;
+	info->max77802 = max77802;
+	info->rtc = max77802->i2c;
+
+	platform_set_drvdata(pdev, info);
+
+	ret = max77802_rtc_init_reg(info);
+
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+		return ret;
+	}
+
+#ifdef MAX77802_RTC_WTSR_SMPL
+	max77802_rtc_enable_wtsr(info, true);
+	max77802_rtc_enable_smpl(info, true);
+#endif
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc",
+					&max77802_rtc_ops, THIS_MODULE);
+
+	if (IS_ERR(info->rtc_dev)) {
+		dev_info(&pdev->dev, "%s: fail\n", __func__);
+
+		ret = PTR_ERR(info->rtc_dev);
+		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+		if (ret == 0)
+			ret = -EINVAL;
+		return ret;
+	}
+	virq = irq_create_mapping(max77802->irq_domain, MAX77802_RTCIRQ_RTCA1);
+	if (!virq) {
+		ret = -ENXIO;
+		return ret;
+	}
+	info->virq = virq;
+
+	ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
+				max77802_rtc_alarm_irq, 0, "rtc-alarm1", info);
+	if (ret < 0)
+		dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+			info->virq, ret);
+
+	return ret;
+}
+
+static int max77802_rtc_remove(struct platform_device *pdev)
+{
+	struct max77802_rtc_info *info = platform_get_drvdata(pdev);
+
+	free_irq(info->virq, info);
+	rtc_device_unregister(info->rtc_dev);
+
+	return 0;
+}
+
+static void max77802_rtc_shutdown(struct platform_device *pdev)
+{
+#ifdef MAX77802_RTC_WTSR_SMPL
+	struct max77802_rtc_info *info = platform_get_drvdata(pdev);
+	int i;
+	u8 val = 0;
+
+	for (i = 0; i < 3; i++) {
+		max77802_rtc_enable_wtsr(info, false);
+		regmap_read(info->max77802->regmap,
+			    MAX77802_WTSR_SMPL_CNTL, &val);
+		dev_info(info->dev, "%s: WTSR_SMPL reg(0x%02x)\n", __func__,
+				val);
+		if (val & WTSR_EN_MASK) {
+			dev_emerg(info->dev, "%s: fail to disable WTSR\n",
+					__func__);
+		} else {
+			dev_info(info->dev, "%s: success to disable WTSR\n",
+					__func__);
+			break;
+		}
+	}
+
+	/* Disable SMPL when power off */
+	max77802_rtc_enable_smpl(info, false);
+#endif /* MAX77802_RTC_WTSR_SMPL */
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77802_rtc_suspend(struct device *dev)
+{
+	if (device_may_wakeup(dev)) {
+		struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+		return enable_irq_wake(info->virq);
+	}
+
+	return 0;
+}
+
+static int max77802_rtc_resume(struct device *dev)
+{
+	if (device_may_wakeup(dev)) {
+		struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+		return disable_irq_wake(info->virq);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops,
+			 max77802_rtc_suspend, max77802_rtc_resume);
+
+static const struct platform_device_id rtc_id[] = {
+	{ "max77802-rtc", 0 },
+	{},
+};
+
+static struct platform_driver max77802_rtc_driver = {
+	.driver		= {
+		.name	= "max77802-rtc",
+		.owner	= THIS_MODULE,
+		.pm	= &max77802_rtc_pm_ops,
+	},
+	.probe		= max77802_rtc_probe,
+	.remove		= max77802_rtc_remove,
+	.shutdown	= max77802_rtc_shutdown,
+	.id_table	= rtc_id,
+};
+
+module_platform_driver(max77802_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77802 RTC driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
  2014-06-09  9:37 ` Javier Martinez Canillas
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel,
	Javier Martinez Canillas

Peach pit board uses a Maxim 77802 Power Management IC to
drive regulators and its Real Time Clock. This patch adds
support for this chip.

These are the device nodes and pinctrl configuration that
is present on the Peach pit DeviceTree source file in the
the Chrome OS kernel 3.8 tree.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 arch/arm/boot/dts/exynos5420-peach-pit.dts | 320 +++++++++++++++++++++++++++++
 1 file changed, 320 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts
index 1c5b8f9..cb7d720 100644
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts
@@ -111,6 +111,13 @@
 		samsung,pin-drv = <0>;
 	};
 
+	max77802_irq: max77802-irq {
+		samsung,pins = "gpx3-1";
+		samsung,pin-function = <0>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
+
 	hdmi_hpd_irq: hdmi-hpd-irq {
 		samsung,pins = "gpx3-7";
 		samsung,pin-function = <0>;
@@ -124,6 +131,29 @@
 		samsung,pin-pud = <3>;
 		samsung,pin-drv = <0>;
 	};
+
+	pmic_dvs_1: pmic-dvs-1 {
+		samsung,pins = "gpy7-6";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
+};
+
+&pinctrl_2 {
+	pmic_dvs_2: pmic-dvs-2 {
+		samsung,pins = "gpj4-2";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
+
+	pmic_dvs_3: pmic-dvs-3 {
+		samsung,pins = "gpj4-3";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
 };
 
 &pinctrl_3 {
@@ -140,6 +170,14 @@
 		samsung,pin-pud = <0>;
 		samsung,pin-drv = <0>;
 	};
+
+	pmic_selb: pmic-selb {
+		samsung,pins = "gph0-2", "gph0-3", "gph0-4", "gph0-5",
+			       "gph0-6";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
 };
 
 &rtc {
@@ -189,6 +227,288 @@
 	};
 };
 
+&hsi2c_4 {
+	status = "okay";
+	clock-frequency = <400000>;
+
+	max77802-pmic@9 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&gpx3>;
+		interrupts = <1 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&max77802_irq>, <&pmic_selb>,
+			    <&pmic_dvs_1>, <&pmic_dvs_2>, <&pmic_dvs_3>;
+		wakeup-source;
+		reg = <0x9>;
+		#clock-cells = <1>;
+
+		/* Using idx 1 means warm reset will get good voltage */
+		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 {
+			ldo1_reg: LDO1 {
+				regulator-name = "vdd_1v0";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo2_reg: LDO2 {
+				regulator-name = "vdd_1v2_2";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ldo3_reg: LDO3 {
+				regulator-name = "vdd_1v8_3";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			vqmmc_sdcard: ldo4_reg: LDO4 {
+				regulator-name = "vdd_sd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+			};
+			ldo5_reg: LDO5 {
+				regulator-name = "vdd_1v8_5";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo6_reg: LDO6 {
+				regulator-name = "vdd_1v8_6";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo7_reg: LDO7 {
+				regulator-name = "vdd_1v8_7";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo8_reg: LDO8 {
+				regulator-name = "vdd_ldo8";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo9_reg: LDO9 {
+				regulator-name = "vdd_ldo9";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo10_reg: LDO10 {
+				regulator-name = "vdd_ldo10";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo11_reg: LDO11 {
+				regulator-name = "vdd_ldo11";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo12_reg: LDO12 {
+				regulator-name = "vdd_ldo12";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+				regulator-always-on;
+			};
+			ldo13_reg: LDO13 {
+				regulator-name = "vdd_ldo13";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo14_reg: LDO14 {
+				regulator-name = "vdd_ldo14";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo15_reg: LDO15 {
+				regulator-name = "vdd_ldo15";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo17_reg: LDO17 {
+				regulator-name = "vdd_g3ds";
+				regulator-min-microvolt = <900000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-always-on;
+			};
+			ldo18_reg: LDO18 {
+				regulator-name = "ldo_18";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo19_reg: LDO19 {
+				regulator-name = "ldo_19";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo20_reg: LDO20 {
+				regulator-name = "ldo_20";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo21_reg: LDO21 {
+				regulator-name = "ldo_21";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+			};
+			ldo23_reg: LDO23 {
+				regulator-name = "ld0_23";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+			ldo24_reg: LDO24 {
+				regulator-name = "ldo_24";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+			};
+			ldo25_reg: LDO25 {
+				regulator-name = "ldo_25";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+			ldo26_reg: LDO26 {
+				regulator-name = "ldo_26";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ldo27_reg: LDO27 {
+				regulator-name = "ldo_27";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ldo28_reg: LDO28 {
+				regulator-name = "ldo_28";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo29_reg: LDO29 {
+				regulator-name = "ldo_29";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo30_reg: LDO30 {
+				regulator-name = "vdd_mifs";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo32_reg: LDO32 {
+				regulator-name = "ldo_32";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+			};
+			ldo33_reg: LDO33 {
+				regulator-name = "ldo_33";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+			};
+			ldo34_reg: LDO34 {
+				regulator-name = "ldo_34";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+			};
+			ldo35_reg: LDO35 {
+				regulator-name = "ldo_35";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			buck1_reg: BUCK1 {
+				regulator-name = "vdd_mif";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1300000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck2_reg: BUCK2 {
+				regulator-name = "vdd_arm_real";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck3_reg: BUCK3 {
+				regulator-name = "vdd_int_real";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck4_reg: BUCK4 {
+				regulator-name = "vdd_g3d";
+				regulator-min-microvolt = <700000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck5_reg: BUCK5 {
+				regulator-name = "vdd_1v2";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck6_reg: BUCK6 {
+				regulator-name = "vdd_kfc";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck7_reg: BUCK7 {
+				regulator-name = "vdd_1v35";
+				regulator-min-microvolt = <1350000>;
+				regulator-max-microvolt = <1350000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck8_reg: BUCK8 {
+				regulator-name = "vdd_emmc";
+				regulator-min-microvolt = <2850000>;
+				regulator-max-microvolt = <2850000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck9_reg: BUCK9 {
+				regulator-name = "vdd_2v";
+				regulator-min-microvolt = <2000000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck10_reg: BUCK10 {
+				regulator-name = "vdd_1v8";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+		};
+	};
+};
+
 &hsi2c_7 {
 	status = "okay";
 
-- 
2.0.0.rc2


^ permalink raw reply related	[flat|nested] 54+ messages in thread

* [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
@ 2014-06-09  9:37   ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

Peach pit board uses a Maxim 77802 Power Management IC to
drive regulators and its Real Time Clock. This patch adds
support for this chip.

These are the device nodes and pinctrl configuration that
is present on the Peach pit DeviceTree source file in the
the Chrome OS kernel 3.8 tree.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
---
 arch/arm/boot/dts/exynos5420-peach-pit.dts | 320 +++++++++++++++++++++++++++++
 1 file changed, 320 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts
index 1c5b8f9..cb7d720 100644
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts
@@ -111,6 +111,13 @@
 		samsung,pin-drv = <0>;
 	};
 
+	max77802_irq: max77802-irq {
+		samsung,pins = "gpx3-1";
+		samsung,pin-function = <0>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
+
 	hdmi_hpd_irq: hdmi-hpd-irq {
 		samsung,pins = "gpx3-7";
 		samsung,pin-function = <0>;
@@ -124,6 +131,29 @@
 		samsung,pin-pud = <3>;
 		samsung,pin-drv = <0>;
 	};
+
+	pmic_dvs_1: pmic-dvs-1 {
+		samsung,pins = "gpy7-6";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
+};
+
+&pinctrl_2 {
+	pmic_dvs_2: pmic-dvs-2 {
+		samsung,pins = "gpj4-2";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
+
+	pmic_dvs_3: pmic-dvs-3 {
+		samsung,pins = "gpj4-3";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
 };
 
 &pinctrl_3 {
@@ -140,6 +170,14 @@
 		samsung,pin-pud = <0>;
 		samsung,pin-drv = <0>;
 	};
+
+	pmic_selb: pmic-selb {
+		samsung,pins = "gph0-2", "gph0-3", "gph0-4", "gph0-5",
+			       "gph0-6";
+		samsung,pin-function = <1>;
+		samsung,pin-pud = <0>;
+		samsung,pin-drv = <0>;
+	};
 };
 
 &rtc {
@@ -189,6 +227,288 @@
 	};
 };
 
+&hsi2c_4 {
+	status = "okay";
+	clock-frequency = <400000>;
+
+	max77802-pmic at 9 {
+		compatible = "maxim,max77802";
+		interrupt-parent = <&gpx3>;
+		interrupts = <1 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&max77802_irq>, <&pmic_selb>,
+			    <&pmic_dvs_1>, <&pmic_dvs_2>, <&pmic_dvs_3>;
+		wakeup-source;
+		reg = <0x9>;
+		#clock-cells = <1>;
+
+		/* Using idx 1 means warm reset will get good voltage */
+		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 {
+			ldo1_reg: LDO1 {
+				regulator-name = "vdd_1v0";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo2_reg: LDO2 {
+				regulator-name = "vdd_1v2_2";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ldo3_reg: LDO3 {
+				regulator-name = "vdd_1v8_3";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			vqmmc_sdcard: ldo4_reg: LDO4 {
+				regulator-name = "vdd_sd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+			};
+			ldo5_reg: LDO5 {
+				regulator-name = "vdd_1v8_5";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo6_reg: LDO6 {
+				regulator-name = "vdd_1v8_6";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo7_reg: LDO7 {
+				regulator-name = "vdd_1v8_7";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo8_reg: LDO8 {
+				regulator-name = "vdd_ldo8";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo9_reg: LDO9 {
+				regulator-name = "vdd_ldo9";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo10_reg: LDO10 {
+				regulator-name = "vdd_ldo10";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo11_reg: LDO11 {
+				regulator-name = "vdd_ldo11";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo12_reg: LDO12 {
+				regulator-name = "vdd_ldo12";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+				regulator-always-on;
+			};
+			ldo13_reg: LDO13 {
+				regulator-name = "vdd_ldo13";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo14_reg: LDO14 {
+				regulator-name = "vdd_ldo14";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo15_reg: LDO15 {
+				regulator-name = "vdd_ldo15";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo17_reg: LDO17 {
+				regulator-name = "vdd_g3ds";
+				regulator-min-microvolt = <900000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-always-on;
+			};
+			ldo18_reg: LDO18 {
+				regulator-name = "ldo_18";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo19_reg: LDO19 {
+				regulator-name = "ldo_19";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo20_reg: LDO20 {
+				regulator-name = "ldo_20";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+			ldo21_reg: LDO21 {
+				regulator-name = "ldo_21";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+			};
+			ldo23_reg: LDO23 {
+				regulator-name = "ld0_23";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+			ldo24_reg: LDO24 {
+				regulator-name = "ldo_24";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+			};
+			ldo25_reg: LDO25 {
+				regulator-name = "ldo_25";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+			ldo26_reg: LDO26 {
+				regulator-name = "ldo_26";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ldo27_reg: LDO27 {
+				regulator-name = "ldo_27";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			ldo28_reg: LDO28 {
+				regulator-name = "ldo_28";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo29_reg: LDO29 {
+				regulator-name = "ldo_29";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+			};
+			ldo30_reg: LDO30 {
+				regulator-name = "vdd_mifs";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+			ldo32_reg: LDO32 {
+				regulator-name = "ldo_32";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+			};
+			ldo33_reg: LDO33 {
+				regulator-name = "ldo_33";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+			};
+			ldo34_reg: LDO34 {
+				regulator-name = "ldo_34";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+			};
+			ldo35_reg: LDO35 {
+				regulator-name = "ldo_35";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+			};
+			buck1_reg: BUCK1 {
+				regulator-name = "vdd_mif";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1300000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck2_reg: BUCK2 {
+				regulator-name = "vdd_arm_real";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck3_reg: BUCK3 {
+				regulator-name = "vdd_int_real";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck4_reg: BUCK4 {
+				regulator-name = "vdd_g3d";
+				regulator-min-microvolt = <700000>;
+				regulator-max-microvolt = <1400000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck5_reg: BUCK5 {
+				regulator-name = "vdd_1v2";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck6_reg: BUCK6 {
+				regulator-name = "vdd_kfc";
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1500000>;
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-ramp-delay = <12500>;
+			};
+			buck7_reg: BUCK7 {
+				regulator-name = "vdd_1v35";
+				regulator-min-microvolt = <1350000>;
+				regulator-max-microvolt = <1350000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck8_reg: BUCK8 {
+				regulator-name = "vdd_emmc";
+				regulator-min-microvolt = <2850000>;
+				regulator-max-microvolt = <2850000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck9_reg: BUCK9 {
+				regulator-name = "vdd_2v";
+				regulator-min-microvolt = <2000000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			buck10_reg: BUCK10 {
+				regulator-name = "vdd_1v8";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+		};
+	};
+};
+
 &hsi2c_7 {
 	status = "okay";
 
-- 
2.0.0.rc2

^ permalink raw reply related	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-09  9:37 ` Javier Martinez Canillas
@ 2014-06-09 10:16   ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-09 10:16 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Lee Jones, Alessandro Zummo, Kukjin Kim, Mike Turquette,
	Samuel Ortiz, Tomeu Vizoso, devicetree, linux-kernel,
	Liam Girdwood, Doug Anderson, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> regulators, clocks and the RTC.
> 
> This series are based on drivers added by Simon Glass to the Chrome OS
> kernel and adds support for the Maxim 77802 Power Management IC, their
> regulators, clocks, RTC and I2C interface. It is composed of patches:
> 
> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> 
> Patches 1-4 add support for the different devices and Patch 5 enables
> the MAX77802 PMIC on the Exynos5420 based Peach pit board.


Hi,

The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
drivers. I haven't checked other Maxim drivers but I think there will be
a lot of similarities with them also. It is almost common for Maxim
chipsets to share components between each other.

I think there is no need in duplicating all that stuff once again in new
driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
merge it with max77686 (or other better candidate).

The only difference is in regulator driver. I am not sure whether this
is a result of differences in chip or differences in driver design.

Best regards,
Krzysztof



> Lee,
> 
> Patches 2-4 depend on Patch 1 so I think that it makes sense if you take
> 1-4 through your mfd tree once the relevant maintainers ack the drivers
> added to the other subsystems (regulator, clk and rtc).
> 
> Patch 5 can go through Kukjin tree since is just DTS changes.
> 
> Thanks a lot and best regards,
> Javier




^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 10:16   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-09 10:16 UTC (permalink / raw)
  To: linux-arm-kernel

On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> regulators, clocks and the RTC.
> 
> This series are based on drivers added by Simon Glass to the Chrome OS
> kernel and adds support for the Maxim 77802 Power Management IC, their
> regulators, clocks, RTC and I2C interface. It is composed of patches:
> 
> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> 
> Patches 1-4 add support for the different devices and Patch 5 enables
> the MAX77802 PMIC on the Exynos5420 based Peach pit board.


Hi,

The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
drivers. I haven't checked other Maxim drivers but I think there will be
a lot of similarities with them also. It is almost common for Maxim
chipsets to share components between each other.

I think there is no need in duplicating all that stuff once again in new
driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
merge it with max77686 (or other better candidate).

The only difference is in regulator driver. I am not sure whether this
is a result of differences in chip or differences in driver design.

Best regards,
Krzysztof



> Lee,
> 
> Patches 2-4 depend on Patch 1 so I think that it makes sense if you take
> 1-4 through your mfd tree once the relevant maintainers ack the drivers
> added to the other subsystems (regulator, clk and rtc).
> 
> Patch 5 can go through Kukjin tree since is just DTS changes.
> 
> Thanks a lot and best regards,
> Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
  2014-06-09  9:37   ` Javier Martinez Canillas
@ 2014-06-09 10:22     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-09 10:22 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Lee Jones, Alessandro Zummo, Kukjin Kim, Mike Turquette,
	Samuel Ortiz, Tomeu Vizoso, devicetree, linux-kernel,
	Liam Girdwood, Doug Anderson, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> 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>

(...)


> 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),
> +};

Why just not use two regmap_irq_chips (for PMIC and RTC blocks)
replacing whole max77802-irq.c file?

Best regards,
Krzysztof



^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
@ 2014-06-09 10:22     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-09 10:22 UTC (permalink / raw)
  To: linux-arm-kernel

On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> 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>

(...)


> 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),
> +};

Why just not use two regmap_irq_chips (for PMIC and RTC blocks)
replacing whole max77802-irq.c file?

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
  2014-06-09 10:22     ` Krzysztof Kozlowski
@ 2014-06-09 11:56       ` Mark Brown
  -1 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 11:56 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Javier Martinez Canillas, Lee Jones, Alessandro Zummo,
	Kukjin Kim, Mike Turquette, Samuel Ortiz, Tomeu Vizoso,
	devicetree, linux-kernel, Liam Girdwood, Doug Anderson,
	linux-samsung-soc, Sjoerd Simons, Olof Johansson,
	linux-arm-kernel, Daniel Stone

[-- Attachment #1: Type: text/plain, Size: 552 bytes --]

On Mon, Jun 09, 2014 at 12:22:41PM +0200, Krzysztof Kozlowski wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:

> > +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),

> Why just not use two regmap_irq_chips (for PMIC and RTC blocks)
> replacing whole max77802-irq.c file?

If they share the same primary IRQ line something like what the arizona
drivers do should handle things well enough.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
@ 2014-06-09 11:56       ` Mark Brown
  0 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 11:56 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jun 09, 2014 at 12:22:41PM +0200, Krzysztof Kozlowski wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:

> > +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),

> Why just not use two regmap_irq_chips (for PMIC and RTC blocks)
> replacing whole max77802-irq.c file?

If they share the same primary IRQ line something like what the arizona
drivers do should handle things well enough.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140609/ad590f22/attachment.sig>

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-09 10:16   ` Krzysztof Kozlowski
  (?)
@ 2014-06-09 16:04     ` Doug Anderson
  -1 siblings, 0 replies; 54+ messages in thread
From: Doug Anderson @ 2014-06-09 16:04 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Javier Martinez Canillas, Lee Jones, Alessandro Zummo,
	Kukjin Kim, Mike Turquette, Samuel Ortiz, Tomeu Vizoso,
	devicetree, linux-kernel, Liam Girdwood, linux-samsung-soc,
	Sjoerd Simons, Mark Brown, Olof Johansson, linux-arm-kernel,
	Daniel Stone

Krzystof,

On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
<k.kozlowski@samsung.com> wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>> regulators, clocks and the RTC.
>>
>> This series are based on drivers added by Simon Glass to the Chrome OS
>> kernel and adds support for the Maxim 77802 Power Management IC, their
>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>
>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>
>> Patches 1-4 add support for the different devices and Patch 5 enables
>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>
>
> Hi,
>
> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> drivers. I haven't checked other Maxim drivers but I think there will be
> a lot of similarities with them also. It is almost common for Maxim
> chipsets to share components between each other.
>
> I think there is no need in duplicating all that stuff once again in new
> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> merge it with max77686 (or other better candidate).
>
> The only difference is in regulator driver. I am not sure whether this
> is a result of differences in chip or differences in driver design.

Yes, we thought the same thing when we added support for the max77802
in the ChromeOS tree.  Unfortunately it didn't work out half as well
as we thought it would.  When Javier was asking advice about sending
things upstream we suggested that perhaps he should split the two up.


You can see the result of the combined driver the ChromeOS tree (the
code there is older, probably misnamed as max77xxx, and doesn't have
the proper clock pieces, but you can get the gist):

https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c


Specific problems that made it ugly to have a combined driver:

* The RTC has many subtle differences between the 77686 and 77802.
They expanded it to handle a 200 year timeframe instead of 100 and
that meant that they had to shuffle the bits around everywhere.  They
also moved it to have the same i2c address as the main PMIC so all
addresses are different (see max77686_map in the RTC link above).

* The regulator itself has similar concepts between the two, but the
list of bucks / ldos and how they behave is quite different.  Trying
to understand the complex tables in
<https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
was not easy.


If we really need to write a single driver it certainly can be done,
but please look at the above to be sure this is what you want.


NOTE: it's possible that things could be more sane with more driver
redesign, possibly making things more data driven.  The thing that
would be really nice to do would be to avoid all of the crazy
"regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
doing it to be sure it's cleaner, though...


-Doug

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 16:04     ` Doug Anderson
  0 siblings, 0 replies; 54+ messages in thread
From: Doug Anderson @ 2014-06-09 16:04 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Javier Martinez Canillas, Lee Jones, Alessandro Zummo,
	Kukjin Kim, Mike Turquette, Samuel Ortiz, Tomeu Vizoso,
	devicetree, linux-kernel, Liam Girdwood, linux-samsung-soc,
	Sjoerd Simons, Mark Brown, Olof Johansson, linux-arm-kernel,
	Daniel Stone

Krzystof,

On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
<k.kozlowski@samsung.com> wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>> regulators, clocks and the RTC.
>>
>> This series are based on drivers added by Simon Glass to the Chrome OS
>> kernel and adds support for the Maxim 77802 Power Management IC, their
>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>
>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>
>> Patches 1-4 add support for the different devices and Patch 5 enables
>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>
>
> Hi,
>
> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> drivers. I haven't checked other Maxim drivers but I think there will be
> a lot of similarities with them also. It is almost common for Maxim
> chipsets to share components between each other.
>
> I think there is no need in duplicating all that stuff once again in new
> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> merge it with max77686 (or other better candidate).
>
> The only difference is in regulator driver. I am not sure whether this
> is a result of differences in chip or differences in driver design.

Yes, we thought the same thing when we added support for the max77802
in the ChromeOS tree.  Unfortunately it didn't work out half as well
as we thought it would.  When Javier was asking advice about sending
things upstream we suggested that perhaps he should split the two up.


You can see the result of the combined driver the ChromeOS tree (the
code there is older, probably misnamed as max77xxx, and doesn't have
the proper clock pieces, but you can get the gist):

https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c


Specific problems that made it ugly to have a combined driver:

* The RTC has many subtle differences between the 77686 and 77802.
They expanded it to handle a 200 year timeframe instead of 100 and
that meant that they had to shuffle the bits around everywhere.  They
also moved it to have the same i2c address as the main PMIC so all
addresses are different (see max77686_map in the RTC link above).

* The regulator itself has similar concepts between the two, but the
list of bucks / ldos and how they behave is quite different.  Trying
to understand the complex tables in
<https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
was not easy.


If we really need to write a single driver it certainly can be done,
but please look at the above to be sure this is what you want.


NOTE: it's possible that things could be more sane with more driver
redesign, possibly making things more data driven.  The thing that
would be really nice to do would be to avoid all of the crazy
"regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
doing it to be sure it's cleaner, though...


-Doug

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 16:04     ` Doug Anderson
  0 siblings, 0 replies; 54+ messages in thread
From: Doug Anderson @ 2014-06-09 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

Krzystof,

On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
<k.kozlowski@samsung.com> wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>> regulators, clocks and the RTC.
>>
>> This series are based on drivers added by Simon Glass to the Chrome OS
>> kernel and adds support for the Maxim 77802 Power Management IC, their
>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>
>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>
>> Patches 1-4 add support for the different devices and Patch 5 enables
>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>
>
> Hi,
>
> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> drivers. I haven't checked other Maxim drivers but I think there will be
> a lot of similarities with them also. It is almost common for Maxim
> chipsets to share components between each other.
>
> I think there is no need in duplicating all that stuff once again in new
> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> merge it with max77686 (or other better candidate).
>
> The only difference is in regulator driver. I am not sure whether this
> is a result of differences in chip or differences in driver design.

Yes, we thought the same thing when we added support for the max77802
in the ChromeOS tree.  Unfortunately it didn't work out half as well
as we thought it would.  When Javier was asking advice about sending
things upstream we suggested that perhaps he should split the two up.


You can see the result of the combined driver the ChromeOS tree (the
code there is older, probably misnamed as max77xxx, and doesn't have
the proper clock pieces, but you can get the gist):

https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c


Specific problems that made it ugly to have a combined driver:

* The RTC has many subtle differences between the 77686 and 77802.
They expanded it to handle a 200 year timeframe instead of 100 and
that meant that they had to shuffle the bits around everywhere.  They
also moved it to have the same i2c address as the main PMIC so all
addresses are different (see max77686_map in the RTC link above).

* The regulator itself has similar concepts between the two, but the
list of bucks / ldos and how they behave is quite different.  Trying
to understand the complex tables in
<https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
was not easy.


If we really need to write a single driver it certainly can be done,
but please look at the above to be sure this is what you want.


NOTE: it's possible that things could be more sane with more driver
redesign, possibly making things more data driven.  The thing that
would be really nice to do would be to avoid all of the crazy
"regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
doing it to be sure it's cleaner, though...


-Doug

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
@ 2014-06-09 19:38     ` Mark Brown
  0 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 19:38 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Lee Jones, Samuel Ortiz, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1928 bytes --]

On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:

> +	case REGULATOR_MODE_STANDBY:			/* switch off */
> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
> +			val = MAX77802_OPMODE_STANDBY;
> +			break;
> +		}
> +		/* no break */

This sounds very broken...

> +		/* OK if some GPIOs aren't defined */
> +		if (!gpio_is_valid(gpio))
> +			continue;
> +
> +		/* If a GPIO is valid, we'd better be able to claim it */
> +		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
> +					    "max77802 selb");
> +		if (ret) {
> +			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
> +			return ret;
> +		}

Can this use the GPIO descriptor API?

> +static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
> +			      int from_reg, int to_reg)
> +{
> +	int val;
> +	int ret;
> +
> +	if (from_reg == to_reg)
> +		return;
> +
> +	ret = regmap_read(regmap, from_reg, &val);
> +	if (!ret)
> +		ret = regmap_write(regmap, to_reg, val);
> +
> +	if (ret)
> +		dev_warn(dev, "Copy err %d => %d (%d)\n",
> +			 from_reg, to_reg, ret);
> +}

This doesn't look at all device specific, better implement it in generic
code.

> +	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
> +		dev_err(&pdev->dev,
> +			"Invalid initial data for regulator's initialiation: " \
> +			"expected %d, pdata/dt provided %d\n",
> +			MAX77802_MAX_REGULATORS,
> +			pdata->num_regulators);
> +		return -EINVAL;
> +	}

Don't split log messages over multiple lines so people can find the log
message in the kernel source with grep, though in any case checking for
this is a bug - the driver should always be at least able to read the
current state from the hardware.

> +static int __init max77802_pmic_init(void)
> +{
> +	return platform_driver_register(&max77802_pmic_driver);
> +}
> +subsys_initcall(max77802_pmic_init);

module_platform_driver().

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
@ 2014-06-09 19:38     ` Mark Brown
  0 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 19:38 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Lee Jones, Samuel Ortiz, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 1928 bytes --]

On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:

> +	case REGULATOR_MODE_STANDBY:			/* switch off */
> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
> +			val = MAX77802_OPMODE_STANDBY;
> +			break;
> +		}
> +		/* no break */

This sounds very broken...

> +		/* OK if some GPIOs aren't defined */
> +		if (!gpio_is_valid(gpio))
> +			continue;
> +
> +		/* If a GPIO is valid, we'd better be able to claim it */
> +		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
> +					    "max77802 selb");
> +		if (ret) {
> +			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
> +			return ret;
> +		}

Can this use the GPIO descriptor API?

> +static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
> +			      int from_reg, int to_reg)
> +{
> +	int val;
> +	int ret;
> +
> +	if (from_reg == to_reg)
> +		return;
> +
> +	ret = regmap_read(regmap, from_reg, &val);
> +	if (!ret)
> +		ret = regmap_write(regmap, to_reg, val);
> +
> +	if (ret)
> +		dev_warn(dev, "Copy err %d => %d (%d)\n",
> +			 from_reg, to_reg, ret);
> +}

This doesn't look at all device specific, better implement it in generic
code.

> +	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
> +		dev_err(&pdev->dev,
> +			"Invalid initial data for regulator's initialiation: " \
> +			"expected %d, pdata/dt provided %d\n",
> +			MAX77802_MAX_REGULATORS,
> +			pdata->num_regulators);
> +		return -EINVAL;
> +	}

Don't split log messages over multiple lines so people can find the log
message in the kernel source with grep, though in any case checking for
this is a bug - the driver should always be at least able to read the
current state from the hardware.

> +static int __init max77802_pmic_init(void)
> +{
> +	return platform_driver_register(&max77802_pmic_driver);
> +}
> +subsys_initcall(max77802_pmic_init);

module_platform_driver().

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
@ 2014-06-09 19:38     ` Mark Brown
  0 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 19:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:

> +	case REGULATOR_MODE_STANDBY:			/* switch off */
> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
> +			val = MAX77802_OPMODE_STANDBY;
> +			break;
> +		}
> +		/* no break */

This sounds very broken...

> +		/* OK if some GPIOs aren't defined */
> +		if (!gpio_is_valid(gpio))
> +			continue;
> +
> +		/* If a GPIO is valid, we'd better be able to claim it */
> +		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
> +					    "max77802 selb");
> +		if (ret) {
> +			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
> +			return ret;
> +		}

Can this use the GPIO descriptor API?

> +static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
> +			      int from_reg, int to_reg)
> +{
> +	int val;
> +	int ret;
> +
> +	if (from_reg == to_reg)
> +		return;
> +
> +	ret = regmap_read(regmap, from_reg, &val);
> +	if (!ret)
> +		ret = regmap_write(regmap, to_reg, val);
> +
> +	if (ret)
> +		dev_warn(dev, "Copy err %d => %d (%d)\n",
> +			 from_reg, to_reg, ret);
> +}

This doesn't look at all device specific, better implement it in generic
code.

> +	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
> +		dev_err(&pdev->dev,
> +			"Invalid initial data for regulator's initialiation: " \
> +			"expected %d, pdata/dt provided %d\n",
> +			MAX77802_MAX_REGULATORS,
> +			pdata->num_regulators);
> +		return -EINVAL;
> +	}

Don't split log messages over multiple lines so people can find the log
message in the kernel source with grep, though in any case checking for
this is a bug - the driver should always be at least able to read the
current state from the hardware.

> +static int __init max77802_pmic_init(void)
> +{
> +	return platform_driver_register(&max77802_pmic_driver);
> +}
> +subsys_initcall(max77802_pmic_init);

module_platform_driver().
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140609/7365afd5/attachment.sig>

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
  2014-06-09  9:37   ` Javier Martinez Canillas
@ 2014-06-09 19:47     ` Mark Brown
  -1 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 19:47 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Lee Jones, Samuel Ortiz, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1755 bytes --]

On Mon, Jun 09, 2014 at 11:37:46AM +0200, Javier Martinez Canillas wrote:

> +Optional node:
> +- voltage-regulators : The regulators of max77802 have to be instantiated
> +  under subnode named "voltage-regulators" using the following format.

Every other PMIC calls this node regulators...

> +	regulator_name {
> +		regulator-compatible = LDOn/BUCKn

regulator-compatible is deprecated, use the node name instead.

> +config MFD_MAX77802
> +	bool "Maxim Integrated MAX77802 PMIC Support"

Why is this bool and not tristate?

> +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;
> +}

As covered in another subthread all this code looks like it should be
regmap-irq.

> +	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;

If this is not an error why is it printed as dev_err()?  It does look
like an error to me, though.

> +	} else {
> +		dev_info(max77802->dev, "device found\n");
> +	}

These sort of prints are just noise, remove this unless there is some
revision information you can display.  It's also better practice to
check that the device ID is actually what was expected in case there was
an error in the DT.

> +static const struct i2c_device_id max77802_i2c_id[] = {
> +	{ "max77802", TYPE_MAX77802 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, max77802_i2c_id);

We have type information here but not in the OF ID table (not that we
ever look at it).

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
@ 2014-06-09 19:47     ` Mark Brown
  0 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-09 19:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jun 09, 2014 at 11:37:46AM +0200, Javier Martinez Canillas wrote:

> +Optional node:
> +- voltage-regulators : The regulators of max77802 have to be instantiated
> +  under subnode named "voltage-regulators" using the following format.

Every other PMIC calls this node regulators...

> +	regulator_name {
> +		regulator-compatible = LDOn/BUCKn

regulator-compatible is deprecated, use the node name instead.

> +config MFD_MAX77802
> +	bool "Maxim Integrated MAX77802 PMIC Support"

Why is this bool and not tristate?

> +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;
> +}

As covered in another subthread all this code looks like it should be
regmap-irq.

> +	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;

If this is not an error why is it printed as dev_err()?  It does look
like an error to me, though.

> +	} else {
> +		dev_info(max77802->dev, "device found\n");
> +	}

These sort of prints are just noise, remove this unless there is some
revision information you can display.  It's also better practice to
check that the device ID is actually what was expected in case there was
an error in the DT.

> +static const struct i2c_device_id max77802_i2c_id[] = {
> +	{ "max77802", TYPE_MAX77802 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, max77802_i2c_id);

We have type information here but not in the OF ID table (not that we
ever look at it).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140609/4eb88b9b/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-09 16:04     ` Doug Anderson
  (?)
@ 2014-06-09 22:55       ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 22:55 UTC (permalink / raw)
  To: Doug Anderson, Krzysztof Kozlowski
  Cc: Lee Jones, Alessandro Zummo, Kukjin Kim, Mike Turquette,
	Samuel Ortiz, Tomeu Vizoso, devicetree, linux-kernel,
	Liam Girdwood, linux-samsung-soc, Sjoerd Simons, Mark Brown,
	Olof Johansson, linux-arm-kernel, Daniel Stone

Hello Krzystof,

Thanks a lot for your feedback.

On 06/09/2014 06:04 PM, Doug Anderson wrote:
> Krzystof,
> 
> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> <k.kozlowski@samsung.com> wrote:
>> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>>> regulators, clocks and the RTC.
>>>
>>> This series are based on drivers added by Simon Glass to the Chrome OS
>>> kernel and adds support for the Maxim 77802 Power Management IC, their
>>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>>
>>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>>
>>> Patches 1-4 add support for the different devices and Patch 5 enables
>>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>>
>>
>> Hi,
>>
>> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
>> drivers. I haven't checked other Maxim drivers but I think there will be
>> a lot of similarities with them also. It is almost common for Maxim
>> chipsets to share components between each other.
>>
>> I think there is no need in duplicating all that stuff once again in new
>> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
>> merge it with max77686 (or other better candidate).
>>
>> The only difference is in regulator driver. I am not sure whether this
>> is a result of differences in chip or differences in driver design.
> 
> Yes, we thought the same thing when we added support for the max77802
> in the ChromeOS tree.  Unfortunately it didn't work out half as well
> as we thought it would.  When Javier was asking advice about sending
> things upstream we suggested that perhaps he should split the two up.
> 
> 
> You can see the result of the combined driver the ChromeOS tree (the
> code there is older, probably misnamed as max77xxx, and doesn't have
> the proper clock pieces, but you can get the gist):
> 
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> 
> 
> Specific problems that made it ugly to have a combined driver:
> 
> * The RTC has many subtle differences between the 77686 and 77802.
> They expanded it to handle a 200 year timeframe instead of 100 and
> that meant that they had to shuffle the bits around everywhere.  They
> also moved it to have the same i2c address as the main PMIC so all
> addresses are different (see max77686_map in the RTC link above).
>
> * The regulator itself has similar concepts between the two, but the
> list of bucks / ldos and how they behave is quite different.  Trying
> to understand the complex tables in
> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> was not easy.
> 
> 

There are other differences that were not mentioned:

- The max77802 uses a single register to enable RTC alarm while max77686 uses 1
bit from a set of registers.
- Each chip has some regulators that are not available and you have to take care
of those exceptions (e.g: LDO16, LDO22 and LD31 on max77802).
- The max77802 has 2 clocks outputs while the max77686 has three.

So, as Doug said there are many differences on these two chips besides the
regulators. It's true that these two drivers share a lot of the structure and
there is code duplication (this applies to most maxim drivers btw) but I have my
doubts that the combined approach will make the code more easy to maintain.

The biggest problem is the i2c addresses space being different so you need an
indirection level to access registers and have duplicated registers definition
for each chip.

> If we really need to write a single driver it certainly can be done,
> but please look at the above to be sure this is what you want.
> 
>

Yes, if the combined driver is preferred over having a separate driver for
max77802 then I will try to find the more elegant way to merge both drivers. But
I tried to do it already and I can't say I liked the end result more than having
two separate drivers.

> NOTE: it's possible that things could be more sane with more driver
> redesign, possibly making things more data driven.  The thing that
> would be really nice to do would be to avoid all of the crazy
> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> doing it to be sure it's cleaner, though...
> 

Another option is to use an hybrid approach. Merge the mfd core, irq and clk
drivers but have different platform drivers for rtc and regulators. Still the
end result is not great imho but better than trying merging the regulators and
RTC drivers where most of the differences are.

> 
> -Doug
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 22:55       ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 22:55 UTC (permalink / raw)
  To: Doug Anderson, Krzysztof Kozlowski
  Cc: Lee Jones, Alessandro Zummo, Kukjin Kim, Mike Turquette,
	Samuel Ortiz, Tomeu Vizoso, devicetree, linux-kernel,
	Liam Girdwood, linux-samsung-soc, Sjoerd Simons, Mark Brown,
	Olof Johansson, linux-arm-kernel, Daniel Stone

Hello Krzystof,

Thanks a lot for your feedback.

On 06/09/2014 06:04 PM, Doug Anderson wrote:
> Krzystof,
> 
> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> <k.kozlowski@samsung.com> wrote:
>> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>>> regulators, clocks and the RTC.
>>>
>>> This series are based on drivers added by Simon Glass to the Chrome OS
>>> kernel and adds support for the Maxim 77802 Power Management IC, their
>>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>>
>>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>>
>>> Patches 1-4 add support for the different devices and Patch 5 enables
>>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>>
>>
>> Hi,
>>
>> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
>> drivers. I haven't checked other Maxim drivers but I think there will be
>> a lot of similarities with them also. It is almost common for Maxim
>> chipsets to share components between each other.
>>
>> I think there is no need in duplicating all that stuff once again in new
>> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
>> merge it with max77686 (or other better candidate).
>>
>> The only difference is in regulator driver. I am not sure whether this
>> is a result of differences in chip or differences in driver design.
> 
> Yes, we thought the same thing when we added support for the max77802
> in the ChromeOS tree.  Unfortunately it didn't work out half as well
> as we thought it would.  When Javier was asking advice about sending
> things upstream we suggested that perhaps he should split the two up.
> 
> 
> You can see the result of the combined driver the ChromeOS tree (the
> code there is older, probably misnamed as max77xxx, and doesn't have
> the proper clock pieces, but you can get the gist):
> 
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> 
> 
> Specific problems that made it ugly to have a combined driver:
> 
> * The RTC has many subtle differences between the 77686 and 77802.
> They expanded it to handle a 200 year timeframe instead of 100 and
> that meant that they had to shuffle the bits around everywhere.  They
> also moved it to have the same i2c address as the main PMIC so all
> addresses are different (see max77686_map in the RTC link above).
>
> * The regulator itself has similar concepts between the two, but the
> list of bucks / ldos and how they behave is quite different.  Trying
> to understand the complex tables in
> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> was not easy.
> 
> 

There are other differences that were not mentioned:

- The max77802 uses a single register to enable RTC alarm while max77686 uses 1
bit from a set of registers.
- Each chip has some regulators that are not available and you have to take care
of those exceptions (e.g: LDO16, LDO22 and LD31 on max77802).
- The max77802 has 2 clocks outputs while the max77686 has three.

So, as Doug said there are many differences on these two chips besides the
regulators. It's true that these two drivers share a lot of the structure and
there is code duplication (this applies to most maxim drivers btw) but I have my
doubts that the combined approach will make the code more easy to maintain.

The biggest problem is the i2c addresses space being different so you need an
indirection level to access registers and have duplicated registers definition
for each chip.

> If we really need to write a single driver it certainly can be done,
> but please look at the above to be sure this is what you want.
> 
>

Yes, if the combined driver is preferred over having a separate driver for
max77802 then I will try to find the more elegant way to merge both drivers. But
I tried to do it already and I can't say I liked the end result more than having
two separate drivers.

> NOTE: it's possible that things could be more sane with more driver
> redesign, possibly making things more data driven.  The thing that
> would be really nice to do would be to avoid all of the crazy
> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> doing it to be sure it's cleaner, though...
> 

Another option is to use an hybrid approach. Merge the mfd core, irq and clk
drivers but have different platform drivers for rtc and regulators. Still the
end result is not great imho but better than trying merging the regulators and
RTC drivers where most of the differences are.

> 
> -Doug
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 22:55       ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 22:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Krzystof,

Thanks a lot for your feedback.

On 06/09/2014 06:04 PM, Doug Anderson wrote:
> Krzystof,
> 
> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> <k.kozlowski@samsung.com> wrote:
>> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>>> regulators, clocks and the RTC.
>>>
>>> This series are based on drivers added by Simon Glass to the Chrome OS
>>> kernel and adds support for the Maxim 77802 Power Management IC, their
>>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>>
>>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>>
>>> Patches 1-4 add support for the different devices and Patch 5 enables
>>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>>
>>
>> Hi,
>>
>> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
>> drivers. I haven't checked other Maxim drivers but I think there will be
>> a lot of similarities with them also. It is almost common for Maxim
>> chipsets to share components between each other.
>>
>> I think there is no need in duplicating all that stuff once again in new
>> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
>> merge it with max77686 (or other better candidate).
>>
>> The only difference is in regulator driver. I am not sure whether this
>> is a result of differences in chip or differences in driver design.
> 
> Yes, we thought the same thing when we added support for the max77802
> in the ChromeOS tree.  Unfortunately it didn't work out half as well
> as we thought it would.  When Javier was asking advice about sending
> things upstream we suggested that perhaps he should split the two up.
> 
> 
> You can see the result of the combined driver the ChromeOS tree (the
> code there is older, probably misnamed as max77xxx, and doesn't have
> the proper clock pieces, but you can get the gist):
> 
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> 
> 
> Specific problems that made it ugly to have a combined driver:
> 
> * The RTC has many subtle differences between the 77686 and 77802.
> They expanded it to handle a 200 year timeframe instead of 100 and
> that meant that they had to shuffle the bits around everywhere.  They
> also moved it to have the same i2c address as the main PMIC so all
> addresses are different (see max77686_map in the RTC link above).
>
> * The regulator itself has similar concepts between the two, but the
> list of bucks / ldos and how they behave is quite different.  Trying
> to understand the complex tables in
> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> was not easy.
> 
> 

There are other differences that were not mentioned:

- The max77802 uses a single register to enable RTC alarm while max77686 uses 1
bit from a set of registers.
- Each chip has some regulators that are not available and you have to take care
of those exceptions (e.g: LDO16, LDO22 and LD31 on max77802).
- The max77802 has 2 clocks outputs while the max77686 has three.

So, as Doug said there are many differences on these two chips besides the
regulators. It's true that these two drivers share a lot of the structure and
there is code duplication (this applies to most maxim drivers btw) but I have my
doubts that the combined approach will make the code more easy to maintain.

The biggest problem is the i2c addresses space being different so you need an
indirection level to access registers and have duplicated registers definition
for each chip.

> If we really need to write a single driver it certainly can be done,
> but please look at the above to be sure this is what you want.
> 
>

Yes, if the combined driver is preferred over having a separate driver for
max77802 then I will try to find the more elegant way to merge both drivers. But
I tried to do it already and I can't say I liked the end result more than having
two separate drivers.

> NOTE: it's possible that things could be more sane with more driver
> redesign, possibly making things more data driven.  The thing that
> would be really nice to do would be to avoid all of the crazy
> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> doing it to be sure it's cleaner, though...
> 

Another option is to use an hybrid approach. Merge the mfd core, irq and clk
drivers but have different platform drivers for rtc and regulators. Still the
end result is not great imho but better than trying merging the regulators and
RTC drivers where most of the differences are.

> 
> -Doug
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
  2014-06-09 10:22     ` Krzysztof Kozlowski
@ 2014-06-09 23:07       ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 23:07 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Lee Jones, Alessandro Zummo, Kukjin Kim, Mike Turquette,
	Samuel Ortiz, Tomeu Vizoso, devicetree, linux-kernel,
	Liam Girdwood, Doug Anderson, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

Hello Krzysztof,

On 06/09/2014 12:22 PM, Krzysztof Kozlowski wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>> 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>
> 
> (...)
> 
> 
>> 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),
>> +};
> 
> Why just not use two regmap_irq_chips (for PMIC and RTC blocks)
> replacing whole max77802-irq.c file?
>

Great, I was not aware about regmap_irq_chip. I'll take a look to it for v2.

> Best regards,
> Krzysztof
> 
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
@ 2014-06-09 23:07       ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 23:07 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Krzysztof,

On 06/09/2014 12:22 PM, Krzysztof Kozlowski wrote:
> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>> 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>
> 
> (...)
> 
> 
>> 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),
>> +};
> 
> Why just not use two regmap_irq_chips (for PMIC and RTC blocks)
> replacing whole max77802-irq.c file?
>

Great, I was not aware about regmap_irq_chip. I'll take a look to it for v2.

> Best regards,
> Krzysztof
> 
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
  2014-06-09 19:38     ` Mark Brown
@ 2014-06-09 23:29       ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 23:29 UTC (permalink / raw)
  To: Mark Brown
  Cc: Lee Jones, Samuel Ortiz, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

Hello Mark,

Thanks a lot for your feedback.

On 06/09/2014 09:38 PM, Mark Brown wrote:
> On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:
> 
>> +	case REGULATOR_MODE_STANDBY:			/* switch off */
>> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
>> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
>> +			val = MAX77802_OPMODE_STANDBY;
>> +			break;
>> +		}
>> +		/* no break */
> 
> This sounds very broken...
>

The problem is that not all regulators supports the same operational modes. For
instance regulators LDO 1, 20, 21 and 3 does not support REGULATOR_MODE_STANDBY
so if the condition is not met a break is not needed since the default case is
to warn that the mode is not supported.

But I'll rework that logic on v2 to make it cleaner and have a break on each
case and don't rely on case cascading.

>> +		/* OK if some GPIOs aren't defined */
>> +		if (!gpio_is_valid(gpio))
>> +			continue;
>> +
>> +		/* If a GPIO is valid, we'd better be able to claim it */
>> +		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
>> +					    "max77802 selb");
>> +		if (ret) {
>> +			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
>> +			return ret;
>> +		}
> 
> Can this use the GPIO descriptor API?
> 

Ok, I'll use gpiod_request() on v2.

>> +static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
>> +			      int from_reg, int to_reg)
>> +{
>> +	int val;
>> +	int ret;
>> +
>> +	if (from_reg == to_reg)
>> +		return;
>> +
>> +	ret = regmap_read(regmap, from_reg, &val);
>> +	if (!ret)
>> +		ret = regmap_write(regmap, to_reg, val);
>> +
>> +	if (ret)
>> +		dev_warn(dev, "Copy err %d => %d (%d)\n",
>> +			 from_reg, to_reg, ret);
>> +}
> 
> This doesn't look at all device specific, better implement it in generic
> code.
> 

Ok, will do.

>> +	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
>> +		dev_err(&pdev->dev,
>> +			"Invalid initial data for regulator's initialiation: " \
>> +			"expected %d, pdata/dt provided %d\n",
>> +			MAX77802_MAX_REGULATORS,
>> +			pdata->num_regulators);
>> +		return -EINVAL;
>> +	}
> 
> Don't split log messages over multiple lines so people can find the log
> message in the kernel source with grep, though in any case checking for
> this is a bug - the driver should always be at least able to read the
> current state from the hardware.
> 

Ok.

>> +static int __init max77802_pmic_init(void)
>> +{
>> +	return platform_driver_register(&max77802_pmic_driver);
>> +}
>> +subsys_initcall(max77802_pmic_init);
> 
> module_platform_driver().
> 

Ok.

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
@ 2014-06-09 23:29       ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 23:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

Thanks a lot for your feedback.

On 06/09/2014 09:38 PM, Mark Brown wrote:
> On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:
> 
>> +	case REGULATOR_MODE_STANDBY:			/* switch off */
>> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
>> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
>> +			val = MAX77802_OPMODE_STANDBY;
>> +			break;
>> +		}
>> +		/* no break */
> 
> This sounds very broken...
>

The problem is that not all regulators supports the same operational modes. For
instance regulators LDO 1, 20, 21 and 3 does not support REGULATOR_MODE_STANDBY
so if the condition is not met a break is not needed since the default case is
to warn that the mode is not supported.

But I'll rework that logic on v2 to make it cleaner and have a break on each
case and don't rely on case cascading.

>> +		/* OK if some GPIOs aren't defined */
>> +		if (!gpio_is_valid(gpio))
>> +			continue;
>> +
>> +		/* If a GPIO is valid, we'd better be able to claim it */
>> +		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
>> +					    "max77802 selb");
>> +		if (ret) {
>> +			dev_err(dev, "can't claim gpio[%d]: %d\n", i, ret);
>> +			return ret;
>> +		}
> 
> Can this use the GPIO descriptor API?
> 

Ok, I'll use gpiod_request() on v2.

>> +static void max77802_copy_reg(struct device *dev, struct regmap *regmap,
>> +			      int from_reg, int to_reg)
>> +{
>> +	int val;
>> +	int ret;
>> +
>> +	if (from_reg == to_reg)
>> +		return;
>> +
>> +	ret = regmap_read(regmap, from_reg, &val);
>> +	if (!ret)
>> +		ret = regmap_write(regmap, to_reg, val);
>> +
>> +	if (ret)
>> +		dev_warn(dev, "Copy err %d => %d (%d)\n",
>> +			 from_reg, to_reg, ret);
>> +}
> 
> This doesn't look at all device specific, better implement it in generic
> code.
> 

Ok, will do.

>> +	if (pdata->num_regulators != MAX77802_MAX_REGULATORS) {
>> +		dev_err(&pdev->dev,
>> +			"Invalid initial data for regulator's initialiation: " \
>> +			"expected %d, pdata/dt provided %d\n",
>> +			MAX77802_MAX_REGULATORS,
>> +			pdata->num_regulators);
>> +		return -EINVAL;
>> +	}
> 
> Don't split log messages over multiple lines so people can find the log
> message in the kernel source with grep, though in any case checking for
> this is a bug - the driver should always be at least able to read the
> current state from the hardware.
> 

Ok.

>> +static int __init max77802_pmic_init(void)
>> +{
>> +	return platform_driver_register(&max77802_pmic_driver);
>> +}
>> +subsys_initcall(max77802_pmic_init);
> 
> module_platform_driver().
> 

Ok.

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
  2014-06-09 19:47     ` Mark Brown
@ 2014-06-09 23:40       ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 23:40 UTC (permalink / raw)
  To: Mark Brown
  Cc: Lee Jones, Samuel Ortiz, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

Hello Mark,

On 06/09/2014 09:47 PM, Mark Brown wrote:
> On Mon, Jun 09, 2014 at 11:37:46AM +0200, Javier Martinez Canillas wrote:
> 
>> +Optional node:
>> +- voltage-regulators : The regulators of max77802 have to be instantiated
>> +  under subnode named "voltage-regulators" using the following format.
> 
> Every other PMIC calls this node regulators...
> 

Ok, I'll change for consistency.

>> +	regulator_name {
>> +		regulator-compatible = LDOn/BUCKn
> 
> regulator-compatible is deprecated, use the node name instead.
> 

Ok.

>> +config MFD_MAX77802
>> +	bool "Maxim Integrated MAX77802 PMIC Support"
> 
> Why is this bool and not tristate?
> 

I noticed that the majority of the mfd PMIC drivers were bool and not tristate
so I thought it was a convention. But nothing prevents this driver to be built
as a module so I'll change it to tristate.

>> +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;
>> +}
> 
> As covered in another subthread all this code looks like it should be
> regmap-irq.
> 

It seems so, I'll take that into account for v2.

>> +	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;
> 
> If this is not an error why is it printed as dev_err()?  It does look
> like an error to me, though.
> 

Yeah, it is an error so I'll clean that message.

>> +	} else {
>> +		dev_info(max77802->dev, "device found\n");
>> +	}
> 
> These sort of prints are just noise, remove this unless there is some
> revision information you can display.  It's also better practice to
> check that the device ID is actually what was expected in case there was
> an error in the DT.
> 

Ok, will do.

>> +static const struct i2c_device_id max77802_i2c_id[] = {
>> +	{ "max77802", TYPE_MAX77802 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, max77802_i2c_id);
> 
> We have type information here but not in the OF ID table (not that we
> ever look at it).
> 

Yeah, I'll remove the type information here. It is a left over when trying to
combine both max77802 and max77686 drivers since in a combined driver we need
the type information.

Thanks a lot for your feedback.

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
@ 2014-06-09 23:40       ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-09 23:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 06/09/2014 09:47 PM, Mark Brown wrote:
> On Mon, Jun 09, 2014 at 11:37:46AM +0200, Javier Martinez Canillas wrote:
> 
>> +Optional node:
>> +- voltage-regulators : The regulators of max77802 have to be instantiated
>> +  under subnode named "voltage-regulators" using the following format.
> 
> Every other PMIC calls this node regulators...
> 

Ok, I'll change for consistency.

>> +	regulator_name {
>> +		regulator-compatible = LDOn/BUCKn
> 
> regulator-compatible is deprecated, use the node name instead.
> 

Ok.

>> +config MFD_MAX77802
>> +	bool "Maxim Integrated MAX77802 PMIC Support"
> 
> Why is this bool and not tristate?
> 

I noticed that the majority of the mfd PMIC drivers were bool and not tristate
so I thought it was a convention. But nothing prevents this driver to be built
as a module so I'll change it to tristate.

>> +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;
>> +}
> 
> As covered in another subthread all this code looks like it should be
> regmap-irq.
> 

It seems so, I'll take that into account for v2.

>> +	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;
> 
> If this is not an error why is it printed as dev_err()?  It does look
> like an error to me, though.
> 

Yeah, it is an error so I'll clean that message.

>> +	} else {
>> +		dev_info(max77802->dev, "device found\n");
>> +	}
> 
> These sort of prints are just noise, remove this unless there is some
> revision information you can display.  It's also better practice to
> check that the device ID is actually what was expected in case there was
> an error in the DT.
> 

Ok, will do.

>> +static const struct i2c_device_id max77802_i2c_id[] = {
>> +	{ "max77802", TYPE_MAX77802 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, max77802_i2c_id);
> 
> We have type information here but not in the OF ID table (not that we
> ever look at it).
> 

Yeah, I'll remove the type information here. It is a left over when trying to
combine both max77802 and max77686 drivers since in a combined driver we need
the type information.

Thanks a lot for your feedback.

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-09 22:55       ` Javier Martinez Canillas
  (?)
@ 2014-06-09 23:57         ` Doug Anderson
  -1 siblings, 0 replies; 54+ messages in thread
From: Doug Anderson @ 2014-06-09 23:57 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Krzysztof Kozlowski, Lee Jones, Alessandro Zummo, Kukjin Kim,
	Mike Turquette, Samuel Ortiz, Tomeu Vizoso, devicetree,
	linux-kernel, Liam Girdwood, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

Javier,

On Mon, Jun 9, 2014 at 3:55 PM, Javier Martinez Canillas
<javier.martinez@collabora.co.uk> wrote:
>> * The RTC has many subtle differences between the 77686 and 77802.
>> They expanded it to handle a 200 year timeframe instead of 100 and
>> that meant that they had to shuffle the bits around everywhere.  They
>> also moved it to have the same i2c address as the main PMIC so all
>> addresses are different (see max77686_map in the RTC link above).
>>
> There are other differences that were not mentioned:
>
> - The max77802 uses a single register to enable RTC alarm while max77686 uses 1
> bit from a set of registers.

Ironically this is one and the same issue, but you're right that it's
more major than I made it out to be.  See RTCYEARA2.  My theory is
that to account for more possible year values they needed all 8 bits.
That meant that the enable bit needed to move to a different register.
...and once you moved one enable you might as well move them all, I
guess.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 23:57         ` Doug Anderson
  0 siblings, 0 replies; 54+ messages in thread
From: Doug Anderson @ 2014-06-09 23:57 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Krzysztof Kozlowski, Lee Jones, Alessandro Zummo, Kukjin Kim,
	Mike Turquette, Samuel Ortiz, Tomeu Vizoso, devicetree,
	linux-kernel, Liam Girdwood, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

Javier,

On Mon, Jun 9, 2014 at 3:55 PM, Javier Martinez Canillas
<javier.martinez@collabora.co.uk> wrote:
>> * The RTC has many subtle differences between the 77686 and 77802.
>> They expanded it to handle a 200 year timeframe instead of 100 and
>> that meant that they had to shuffle the bits around everywhere.  They
>> also moved it to have the same i2c address as the main PMIC so all
>> addresses are different (see max77686_map in the RTC link above).
>>
> There are other differences that were not mentioned:
>
> - The max77802 uses a single register to enable RTC alarm while max77686 uses 1
> bit from a set of registers.

Ironically this is one and the same issue, but you're right that it's
more major than I made it out to be.  See RTCYEARA2.  My theory is
that to account for more possible year values they needed all 8 bits.
That meant that the enable bit needed to move to a different register.
...and once you moved one enable you might as well move them all, I
guess.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-09 23:57         ` Doug Anderson
  0 siblings, 0 replies; 54+ messages in thread
From: Doug Anderson @ 2014-06-09 23:57 UTC (permalink / raw)
  To: linux-arm-kernel

Javier,

On Mon, Jun 9, 2014 at 3:55 PM, Javier Martinez Canillas
<javier.martinez@collabora.co.uk> wrote:
>> * The RTC has many subtle differences between the 77686 and 77802.
>> They expanded it to handle a 200 year timeframe instead of 100 and
>> that meant that they had to shuffle the bits around everywhere.  They
>> also moved it to have the same i2c address as the main PMIC so all
>> addresses are different (see max77686_map in the RTC link above).
>>
> There are other differences that were not mentioned:
>
> - The max77802 uses a single register to enable RTC alarm while max77686 uses 1
> bit from a set of registers.

Ironically this is one and the same issue, but you're right that it's
more major than I made it out to be.  See RTCYEARA2.  My theory is
that to account for more possible year values they needed all 8 bits.
That meant that the enable bit needed to move to a different register.
...and once you moved one enable you might as well move them all, I
guess.

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-09 16:04     ` Doug Anderson
  (?)
@ 2014-06-10  7:32       ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-10  7:32 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Javier Martinez Canillas, Lee Jones, Alessandro Zummo,
	Kukjin Kim, Mike Turquette, Samuel Ortiz, Tomeu Vizoso,
	devicetree, linux-kernel, Liam Girdwood, linux-samsung-soc,
	Sjoerd Simons, Mark Brown, Olof Johansson, linux-arm-kernel,
	Daniel Stone

On pon, 2014-06-09 at 09:04 -0700, Doug Anderson wrote:
> Krzystof,
> 
> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> <k.kozlowski@samsung.com> wrote:
> > On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> >> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> >> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> >> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> >> regulators, clocks and the RTC.
> >>
> >> This series are based on drivers added by Simon Glass to the Chrome OS
> >> kernel and adds support for the Maxim 77802 Power Management IC, their
> >> regulators, clocks, RTC and I2C interface. It is composed of patches:
> >>
> >> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> >> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> >> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> >> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> >> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> >>
> >> Patches 1-4 add support for the different devices and Patch 5 enables
> >> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
> >
> >
> > Hi,
> >
> > The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> > drivers. I haven't checked other Maxim drivers but I think there will be
> > a lot of similarities with them also. It is almost common for Maxim
> > chipsets to share components between each other.
> >
> > I think there is no need in duplicating all that stuff once again in new
> > driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> > merge it with max77686 (or other better candidate).
> >
> > The only difference is in regulator driver. I am not sure whether this
> > is a result of differences in chip or differences in driver design.
> 
> Yes, we thought the same thing when we added support for the max77802
> in the ChromeOS tree.  Unfortunately it didn't work out half as well
> as we thought it would.  When Javier was asking advice about sending
> things upstream we suggested that perhaps he should split the two up.
> 
> 
> You can see the result of the combined driver the ChromeOS tree (the
> code there is older, probably misnamed as max77xxx, and doesn't have
> the proper clock pieces, but you can get the gist):
> 
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> 
> 
> Specific problems that made it ugly to have a combined driver:
> 
> * The RTC has many subtle differences between the 77686 and 77802.
> They expanded it to handle a 200 year timeframe instead of 100 and
> that meant that they had to shuffle the bits around everywhere.  They
> also moved it to have the same i2c address as the main PMIC so all
> addresses are different (see max77686_map in the RTC link above).

The difference in RTC registers seems the biggest but it can be solved
in readable manner. I see other differences but there aren't many. It
just hurts seeing so much code duplication:
$ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
  -i drivers/rtc/rtc-max77686.c
$ diff -ubB drivers/rtc/rtc-max77686.c drivers/rtc/rtc-max77802.c

The combined RTC driver from ChromeOS seems fine to me... but I do not
insist.

> * The regulator itself has similar concepts between the two, but the
> list of bucks / ldos and how they behave is quite different.  Trying
> to understand the complex tables in
> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> was not easy.
> 
> 
> If we really need to write a single driver it certainly can be done,
> but please look at the above to be sure this is what you want.

Sure, I don't stick to the idea of one merged driver where this
increases code size and complexity. I see your point that merging
regulator drivers won't bring benefits but please:
$ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
  -i drivers/clk/clk-max77686.c
$ diff -ubB drivers/clk/clk-max77686.c drivers/clk/clk-max77802.c

The difference in number of clocks (2 vs 3) is not an obstacle here.

The same applies to main MFD driver and IRQ code. However MAX77686
doesn't use regmap_irq_chip so it needs changes before merging the IRQ
code into one piece.

Best regards,
Krzysztof

> 
> NOTE: it's possible that things could be more sane with more driver
> redesign, possibly making things more data driven.  The thing that
> would be really nice to do would be to avoid all of the crazy
> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> doing it to be sure it's cleaner, though...
> 
> 
> -Doug


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-10  7:32       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-10  7:32 UTC (permalink / raw)
  To: Doug Anderson
  Cc: Javier Martinez Canillas, Lee Jones, Alessandro Zummo,
	Kukjin Kim, Mike Turquette, Samuel Ortiz, Tomeu Vizoso,
	devicetree, linux-kernel, Liam Girdwood, linux-samsung-soc,
	Sjoerd Simons, Mark Brown, Olof Johansson, linux-arm-kernel,
	Daniel Stone

On pon, 2014-06-09 at 09:04 -0700, Doug Anderson wrote:
> Krzystof,
> 
> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> <k.kozlowski@samsung.com> wrote:
> > On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> >> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> >> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> >> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> >> regulators, clocks and the RTC.
> >>
> >> This series are based on drivers added by Simon Glass to the Chrome OS
> >> kernel and adds support for the Maxim 77802 Power Management IC, their
> >> regulators, clocks, RTC and I2C interface. It is composed of patches:
> >>
> >> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> >> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> >> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> >> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> >> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> >>
> >> Patches 1-4 add support for the different devices and Patch 5 enables
> >> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
> >
> >
> > Hi,
> >
> > The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> > drivers. I haven't checked other Maxim drivers but I think there will be
> > a lot of similarities with them also. It is almost common for Maxim
> > chipsets to share components between each other.
> >
> > I think there is no need in duplicating all that stuff once again in new
> > driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> > merge it with max77686 (or other better candidate).
> >
> > The only difference is in regulator driver. I am not sure whether this
> > is a result of differences in chip or differences in driver design.
> 
> Yes, we thought the same thing when we added support for the max77802
> in the ChromeOS tree.  Unfortunately it didn't work out half as well
> as we thought it would.  When Javier was asking advice about sending
> things upstream we suggested that perhaps he should split the two up.
> 
> 
> You can see the result of the combined driver the ChromeOS tree (the
> code there is older, probably misnamed as max77xxx, and doesn't have
> the proper clock pieces, but you can get the gist):
> 
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> 
> 
> Specific problems that made it ugly to have a combined driver:
> 
> * The RTC has many subtle differences between the 77686 and 77802.
> They expanded it to handle a 200 year timeframe instead of 100 and
> that meant that they had to shuffle the bits around everywhere.  They
> also moved it to have the same i2c address as the main PMIC so all
> addresses are different (see max77686_map in the RTC link above).

The difference in RTC registers seems the biggest but it can be solved
in readable manner. I see other differences but there aren't many. It
just hurts seeing so much code duplication:
$ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
  -i drivers/rtc/rtc-max77686.c
$ diff -ubB drivers/rtc/rtc-max77686.c drivers/rtc/rtc-max77802.c

The combined RTC driver from ChromeOS seems fine to me... but I do not
insist.

> * The regulator itself has similar concepts between the two, but the
> list of bucks / ldos and how they behave is quite different.  Trying
> to understand the complex tables in
> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> was not easy.
> 
> 
> If we really need to write a single driver it certainly can be done,
> but please look at the above to be sure this is what you want.

Sure, I don't stick to the idea of one merged driver where this
increases code size and complexity. I see your point that merging
regulator drivers won't bring benefits but please:
$ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
  -i drivers/clk/clk-max77686.c
$ diff -ubB drivers/clk/clk-max77686.c drivers/clk/clk-max77802.c

The difference in number of clocks (2 vs 3) is not an obstacle here.

The same applies to main MFD driver and IRQ code. However MAX77686
doesn't use regmap_irq_chip so it needs changes before merging the IRQ
code into one piece.

Best regards,
Krzysztof

> 
> NOTE: it's possible that things could be more sane with more driver
> redesign, possibly making things more data driven.  The thing that
> would be really nice to do would be to avoid all of the crazy
> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> doing it to be sure it's cleaner, though...
> 
> 
> -Doug

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-10  7:32       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-10  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

On pon, 2014-06-09 at 09:04 -0700, Doug Anderson wrote:
> Krzystof,
> 
> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> <k.kozlowski@samsung.com> wrote:
> > On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> >> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> >> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> >> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> >> regulators, clocks and the RTC.
> >>
> >> This series are based on drivers added by Simon Glass to the Chrome OS
> >> kernel and adds support for the Maxim 77802 Power Management IC, their
> >> regulators, clocks, RTC and I2C interface. It is composed of patches:
> >>
> >> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> >> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> >> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> >> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> >> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> >>
> >> Patches 1-4 add support for the different devices and Patch 5 enables
> >> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
> >
> >
> > Hi,
> >
> > The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> > drivers. I haven't checked other Maxim drivers but I think there will be
> > a lot of similarities with them also. It is almost common for Maxim
> > chipsets to share components between each other.
> >
> > I think there is no need in duplicating all that stuff once again in new
> > driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> > merge it with max77686 (or other better candidate).
> >
> > The only difference is in regulator driver. I am not sure whether this
> > is a result of differences in chip or differences in driver design.
> 
> Yes, we thought the same thing when we added support for the max77802
> in the ChromeOS tree.  Unfortunately it didn't work out half as well
> as we thought it would.  When Javier was asking advice about sending
> things upstream we suggested that perhaps he should split the two up.
> 
> 
> You can see the result of the combined driver the ChromeOS tree (the
> code there is older, probably misnamed as max77xxx, and doesn't have
> the proper clock pieces, but you can get the gist):
> 
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> 
> 
> Specific problems that made it ugly to have a combined driver:
> 
> * The RTC has many subtle differences between the 77686 and 77802.
> They expanded it to handle a 200 year timeframe instead of 100 and
> that meant that they had to shuffle the bits around everywhere.  They
> also moved it to have the same i2c address as the main PMIC so all
> addresses are different (see max77686_map in the RTC link above).

The difference in RTC registers seems the biggest but it can be solved
in readable manner. I see other differences but there aren't many. It
just hurts seeing so much code duplication:
$ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
  -i drivers/rtc/rtc-max77686.c
$ diff -ubB drivers/rtc/rtc-max77686.c drivers/rtc/rtc-max77802.c

The combined RTC driver from ChromeOS seems fine to me... but I do not
insist.

> * The regulator itself has similar concepts between the two, but the
> list of bucks / ldos and how they behave is quite different.  Trying
> to understand the complex tables in
> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> was not easy.
> 
> 
> If we really need to write a single driver it certainly can be done,
> but please look at the above to be sure this is what you want.

Sure, I don't stick to the idea of one merged driver where this
increases code size and complexity. I see your point that merging
regulator drivers won't bring benefits but please:
$ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
  -i drivers/clk/clk-max77686.c
$ diff -ubB drivers/clk/clk-max77686.c drivers/clk/clk-max77802.c

The difference in number of clocks (2 vs 3) is not an obstacle here.

The same applies to main MFD driver and IRQ code. However MAX77686
doesn't use regmap_irq_chip so it needs changes before merging the IRQ
code into one piece.

Best regards,
Krzysztof

> 
> NOTE: it's possible that things could be more sane with more driver
> redesign, possibly making things more data driven.  The thing that
> would be really nice to do would be to avoid all of the crazy
> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> doing it to be sure it's cleaner, though...
> 
> 
> -Doug

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-09 22:55       ` Javier Martinez Canillas
  (?)
@ 2014-06-10  7:45         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-10  7:45 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Doug Anderson, Lee Jones, Alessandro Zummo, Kukjin Kim,
	Mike Turquette, Samuel Ortiz, Tomeu Vizoso, devicetree,
	linux-kernel, Liam Girdwood, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

On wto, 2014-06-10 at 00:55 +0200, Javier Martinez Canillas wrote:
> Hello Krzystof,
> 
> Thanks a lot for your feedback.
> 
> On 06/09/2014 06:04 PM, Doug Anderson wrote:
> > Krzystof,
> > 
> > On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> > <k.kozlowski@samsung.com> wrote:
> >> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> >>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> >>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> >>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> >>> regulators, clocks and the RTC.
> >>>
> >>> This series are based on drivers added by Simon Glass to the Chrome OS
> >>> kernel and adds support for the Maxim 77802 Power Management IC, their
> >>> regulators, clocks, RTC and I2C interface. It is composed of patches:
> >>>
> >>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> >>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> >>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> >>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> >>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> >>>
> >>> Patches 1-4 add support for the different devices and Patch 5 enables
> >>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
> >>
> >>
> >> Hi,
> >>
> >> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> >> drivers. I haven't checked other Maxim drivers but I think there will be
> >> a lot of similarities with them also. It is almost common for Maxim
> >> chipsets to share components between each other.
> >>
> >> I think there is no need in duplicating all that stuff once again in new
> >> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> >> merge it with max77686 (or other better candidate).
> >>
> >> The only difference is in regulator driver. I am not sure whether this
> >> is a result of differences in chip or differences in driver design.
> > 
> > Yes, we thought the same thing when we added support for the max77802
> > in the ChromeOS tree.  Unfortunately it didn't work out half as well
> > as we thought it would.  When Javier was asking advice about sending
> > things upstream we suggested that perhaps he should split the two up.
> > 
> > 
> > You can see the result of the combined driver the ChromeOS tree (the
> > code there is older, probably misnamed as max77xxx, and doesn't have
> > the proper clock pieces, but you can get the gist):
> > 
> > https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> > https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> > 
> > 
> > Specific problems that made it ugly to have a combined driver:
> > 
> > * The RTC has many subtle differences between the 77686 and 77802.
> > They expanded it to handle a 200 year timeframe instead of 100 and
> > that meant that they had to shuffle the bits around everywhere.  They
> > also moved it to have the same i2c address as the main PMIC so all
> > addresses are different (see max77686_map in the RTC link above).
> >
> > * The regulator itself has similar concepts between the two, but the
> > list of bucks / ldos and how they behave is quite different.  Trying
> > to understand the complex tables in
> > <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> > was not easy.
> > 
> > 
> 
> There are other differences that were not mentioned:
> 
> - The max77802 uses a single register to enable RTC alarm while max77686 uses 1
> bit from a set of registers.

But still this will be one additional 'if' statement in one common
code...

> - Each chip has some regulators that are not available and you have to take care
> of those exceptions (e.g: LDO16, LDO22 and LD31 on max77802).

Missing/new regulator does not look like a problem to me in combined
driver from ChromeOS. The main problem with combined driver for
regulators is that you still need to provide all the
regulator_ops/regulator_desc for each of the chipsets. Thus the combined
driver is almost as big as two drivers.

> - The max77802 has 2 clocks outputs while the max77686 has three.

This leads to one exception in combined clock driver. This will still
reduce LOC.

> So, as Doug said there are many differences on these two chips besides the
> regulators. It's true that these two drivers share a lot of the structure and
> there is code duplication (this applies to most maxim drivers btw) but I have my
> doubts that the combined approach will make the code more easy to maintain.
> 
> The biggest problem is the i2c addresses space being different so you need an
> indirection level to access registers and have duplicated registers definition
> for each chip.

Yep, I agree. I had the same problem with S5M/S2M RTC driver and I am
not quite sure that I've chosen the right solution :).

> > If we really need to write a single driver it certainly can be done,
> > but please look at the above to be sure this is what you want.
> > 
> >
> 
> Yes, if the combined driver is preferred over having a separate driver for
> max77802 then I will try to find the more elegant way to merge both drivers. But
> I tried to do it already and I can't say I liked the end result more than having
> two separate drivers.
> 
> > NOTE: it's possible that things could be more sane with more driver
> > redesign, possibly making things more data driven.  The thing that
> > would be really nice to do would be to avoid all of the crazy
> > "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> > doing it to be sure it's cleaner, though...
> > 
> 
> Another option is to use an hybrid approach. Merge the mfd core, irq and clk
> drivers but have different platform drivers for rtc and regulators.

I think that would be the best solution. Probably it would be useful to
convert the max77686 IRQ code to regmap_irq_chip but I do not insist on
that.

Best regards,
Krzysztof


> Still the
> end result is not great imho but better than trying merging the regulators and
> RTC drivers where most of the differences are.


> 
> > 
> > -Doug
> > 
> 
> Best regards,
> Javier


^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-10  7:45         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-10  7:45 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Doug Anderson, Lee Jones, Alessandro Zummo, Kukjin Kim,
	Mike Turquette, Samuel Ortiz, Tomeu Vizoso, devicetree,
	linux-kernel, Liam Girdwood, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

On wto, 2014-06-10 at 00:55 +0200, Javier Martinez Canillas wrote:
> Hello Krzystof,
> 
> Thanks a lot for your feedback.
> 
> On 06/09/2014 06:04 PM, Doug Anderson wrote:
> > Krzystof,
> > 
> > On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> > <k.kozlowski@samsung.com> wrote:
> >> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> >>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> >>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> >>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> >>> regulators, clocks and the RTC.
> >>>
> >>> This series are based on drivers added by Simon Glass to the Chrome OS
> >>> kernel and adds support for the Maxim 77802 Power Management IC, their
> >>> regulators, clocks, RTC and I2C interface. It is composed of patches:
> >>>
> >>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> >>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> >>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> >>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> >>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> >>>
> >>> Patches 1-4 add support for the different devices and Patch 5 enables
> >>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
> >>
> >>
> >> Hi,
> >>
> >> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> >> drivers. I haven't checked other Maxim drivers but I think there will be
> >> a lot of similarities with them also. It is almost common for Maxim
> >> chipsets to share components between each other.
> >>
> >> I think there is no need in duplicating all that stuff once again in new
> >> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> >> merge it with max77686 (or other better candidate).
> >>
> >> The only difference is in regulator driver. I am not sure whether this
> >> is a result of differences in chip or differences in driver design.
> > 
> > Yes, we thought the same thing when we added support for the max77802
> > in the ChromeOS tree.  Unfortunately it didn't work out half as well
> > as we thought it would.  When Javier was asking advice about sending
> > things upstream we suggested that perhaps he should split the two up.
> > 
> > 
> > You can see the result of the combined driver the ChromeOS tree (the
> > code there is older, probably misnamed as max77xxx, and doesn't have
> > the proper clock pieces, but you can get the gist):
> > 
> > https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> > https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> > 
> > 
> > Specific problems that made it ugly to have a combined driver:
> > 
> > * The RTC has many subtle differences between the 77686 and 77802.
> > They expanded it to handle a 200 year timeframe instead of 100 and
> > that meant that they had to shuffle the bits around everywhere.  They
> > also moved it to have the same i2c address as the main PMIC so all
> > addresses are different (see max77686_map in the RTC link above).
> >
> > * The regulator itself has similar concepts between the two, but the
> > list of bucks / ldos and how they behave is quite different.  Trying
> > to understand the complex tables in
> > <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> > was not easy.
> > 
> > 
> 
> There are other differences that were not mentioned:
> 
> - The max77802 uses a single register to enable RTC alarm while max77686 uses 1
> bit from a set of registers.

But still this will be one additional 'if' statement in one common
code...

> - Each chip has some regulators that are not available and you have to take care
> of those exceptions (e.g: LDO16, LDO22 and LD31 on max77802).

Missing/new regulator does not look like a problem to me in combined
driver from ChromeOS. The main problem with combined driver for
regulators is that you still need to provide all the
regulator_ops/regulator_desc for each of the chipsets. Thus the combined
driver is almost as big as two drivers.

> - The max77802 has 2 clocks outputs while the max77686 has three.

This leads to one exception in combined clock driver. This will still
reduce LOC.

> So, as Doug said there are many differences on these two chips besides the
> regulators. It's true that these two drivers share a lot of the structure and
> there is code duplication (this applies to most maxim drivers btw) but I have my
> doubts that the combined approach will make the code more easy to maintain.
> 
> The biggest problem is the i2c addresses space being different so you need an
> indirection level to access registers and have duplicated registers definition
> for each chip.

Yep, I agree. I had the same problem with S5M/S2M RTC driver and I am
not quite sure that I've chosen the right solution :).

> > If we really need to write a single driver it certainly can be done,
> > but please look at the above to be sure this is what you want.
> > 
> >
> 
> Yes, if the combined driver is preferred over having a separate driver for
> max77802 then I will try to find the more elegant way to merge both drivers. But
> I tried to do it already and I can't say I liked the end result more than having
> two separate drivers.
> 
> > NOTE: it's possible that things could be more sane with more driver
> > redesign, possibly making things more data driven.  The thing that
> > would be really nice to do would be to avoid all of the crazy
> > "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> > doing it to be sure it's cleaner, though...
> > 
> 
> Another option is to use an hybrid approach. Merge the mfd core, irq and clk
> drivers but have different platform drivers for rtc and regulators.

I think that would be the best solution. Probably it would be useful to
convert the max77686 IRQ code to regmap_irq_chip but I do not insist on
that.

Best regards,
Krzysztof


> Still the
> end result is not great imho but better than trying merging the regulators and
> RTC drivers where most of the differences are.


> 
> > 
> > -Doug
> > 
> 
> Best regards,
> Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-10  7:45         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 54+ messages in thread
From: Krzysztof Kozlowski @ 2014-06-10  7:45 UTC (permalink / raw)
  To: linux-arm-kernel

On wto, 2014-06-10 at 00:55 +0200, Javier Martinez Canillas wrote:
> Hello Krzystof,
> 
> Thanks a lot for your feedback.
> 
> On 06/09/2014 06:04 PM, Doug Anderson wrote:
> > Krzystof,
> > 
> > On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
> > <k.kozlowski@samsung.com> wrote:
> >> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
> >>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
> >>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
> >>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
> >>> regulators, clocks and the RTC.
> >>>
> >>> This series are based on drivers added by Simon Glass to the Chrome OS
> >>> kernel and adds support for the Maxim 77802 Power Management IC, their
> >>> regulators, clocks, RTC and I2C interface. It is composed of patches:
> >>>
> >>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
> >>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
> >>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
> >>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
> >>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
> >>>
> >>> Patches 1-4 add support for the different devices and Patch 5 enables
> >>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
> >>
> >>
> >> Hi,
> >>
> >> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
> >> drivers. I haven't checked other Maxim drivers but I think there will be
> >> a lot of similarities with them also. It is almost common for Maxim
> >> chipsets to share components between each other.
> >>
> >> I think there is no need in duplicating all that stuff once again in new
> >> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
> >> merge it with max77686 (or other better candidate).
> >>
> >> The only difference is in regulator driver. I am not sure whether this
> >> is a result of differences in chip or differences in driver design.
> > 
> > Yes, we thought the same thing when we added support for the max77802
> > in the ChromeOS tree.  Unfortunately it didn't work out half as well
> > as we thought it would.  When Javier was asking advice about sending
> > things upstream we suggested that perhaps he should split the two up.
> > 
> > 
> > You can see the result of the combined driver the ChromeOS tree (the
> > code there is older, probably misnamed as max77xxx, and doesn't have
> > the proper clock pieces, but you can get the gist):
> > 
> > https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
> > https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
> > 
> > 
> > Specific problems that made it ugly to have a combined driver:
> > 
> > * The RTC has many subtle differences between the 77686 and 77802.
> > They expanded it to handle a 200 year timeframe instead of 100 and
> > that meant that they had to shuffle the bits around everywhere.  They
> > also moved it to have the same i2c address as the main PMIC so all
> > addresses are different (see max77686_map in the RTC link above).
> >
> > * The regulator itself has similar concepts between the two, but the
> > list of bucks / ldos and how they behave is quite different.  Trying
> > to understand the complex tables in
> > <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
> > was not easy.
> > 
> > 
> 
> There are other differences that were not mentioned:
> 
> - The max77802 uses a single register to enable RTC alarm while max77686 uses 1
> bit from a set of registers.

But still this will be one additional 'if' statement in one common
code...

> - Each chip has some regulators that are not available and you have to take care
> of those exceptions (e.g: LDO16, LDO22 and LD31 on max77802).

Missing/new regulator does not look like a problem to me in combined
driver from ChromeOS. The main problem with combined driver for
regulators is that you still need to provide all the
regulator_ops/regulator_desc for each of the chipsets. Thus the combined
driver is almost as big as two drivers.

> - The max77802 has 2 clocks outputs while the max77686 has three.

This leads to one exception in combined clock driver. This will still
reduce LOC.

> So, as Doug said there are many differences on these two chips besides the
> regulators. It's true that these two drivers share a lot of the structure and
> there is code duplication (this applies to most maxim drivers btw) but I have my
> doubts that the combined approach will make the code more easy to maintain.
> 
> The biggest problem is the i2c addresses space being different so you need an
> indirection level to access registers and have duplicated registers definition
> for each chip.

Yep, I agree. I had the same problem with S5M/S2M RTC driver and I am
not quite sure that I've chosen the right solution :).

> > If we really need to write a single driver it certainly can be done,
> > but please look at the above to be sure this is what you want.
> > 
> >
> 
> Yes, if the combined driver is preferred over having a separate driver for
> max77802 then I will try to find the more elegant way to merge both drivers. But
> I tried to do it already and I can't say I liked the end result more than having
> two separate drivers.
> 
> > NOTE: it's possible that things could be more sane with more driver
> > redesign, possibly making things more data driven.  The thing that
> > would be really nice to do would be to avoid all of the crazy
> > "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
> > doing it to be sure it's cleaner, though...
> > 
> 
> Another option is to use an hybrid approach. Merge the mfd core, irq and clk
> drivers but have different platform drivers for rtc and regulators.

I think that would be the best solution. Probably it would be useful to
convert the max77686 IRQ code to regmap_irq_chip but I do not insist on
that.

Best regards,
Krzysztof


> Still the
> end result is not great imho but better than trying merging the regulators and
> RTC drivers where most of the differences are.


> 
> > 
> > -Doug
> > 
> 
> Best regards,
> Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
  2014-06-10  7:32       ` Krzysztof Kozlowski
  (?)
@ 2014-06-10  7:50         ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-10  7:50 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Doug Anderson, Lee Jones, Alessandro Zummo, Kukjin Kim,
	Mike Turquette, Samuel Ortiz, Tomeu Vizoso, devicetree,
	linux-kernel, Liam Girdwood, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

Hello Krzysztof,

> On 10/06/2014, at 09:32, Krzysztof Kozlowski <k.kozlowski@samsung.com> wrote:
> 
>> On pon, 2014-06-09 at 09:04 -0700, Doug Anderson wrote:
>> Krzystof,
>> 
>> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
>> <k.kozlowski@samsung.com> wrote:
>>> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>>>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>>>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>>>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>>>> regulators, clocks and the RTC.
>>>> 
>>>> This series are based on drivers added by Simon Glass to the Chrome OS
>>>> kernel and adds support for the Maxim 77802 Power Management IC, their
>>>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>>> 
>>>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>>>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>>>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>>>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>>>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>>> 
>>>> Patches 1-4 add support for the different devices and Patch 5 enables
>>>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>>> 
>>> 
>>> Hi,
>>> 
>>> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
>>> drivers. I haven't checked other Maxim drivers but I think there will be
>>> a lot of similarities with them also. It is almost common for Maxim
>>> chipsets to share components between each other.
>>> 
>>> I think there is no need in duplicating all that stuff once again in new
>>> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
>>> merge it with max77686 (or other better candidate).
>>> 
>>> The only difference is in regulator driver. I am not sure whether this
>>> is a result of differences in chip or differences in driver design.
>> 
>> Yes, we thought the same thing when we added support for the max77802
>> in the ChromeOS tree.  Unfortunately it didn't work out half as well
>> as we thought it would.  When Javier was asking advice about sending
>> things upstream we suggested that perhaps he should split the two up.
>> 
>> 
>> You can see the result of the combined driver the ChromeOS tree (the
>> code there is older, probably misnamed as max77xxx, and doesn't have
>> the proper clock pieces, but you can get the gist):
>> 
>> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
>> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
>> 
>> 
>> Specific problems that made it ugly to have a combined driver:
>> 
>> * The RTC has many subtle differences between the 77686 and 77802.
>> They expanded it to handle a 200 year timeframe instead of 100 and
>> that meant that they had to shuffle the bits around everywhere.  They
>> also moved it to have the same i2c address as the main PMIC so all
>> addresses are different (see max77686_map in the RTC link above).
> 
> The difference in RTC registers seems the biggest but it can be solved
> in readable manner. I see other differences but there aren't many. It
> just hurts seeing so much code duplication:
> $ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
>  -i drivers/rtc/rtc-max77686.c
> $ diff -ubB drivers/rtc/rtc-max77686.c drivers/rtc/rtc-max77802.c
> 
> The combined RTC driver from ChromeOS seems fine to me... but I do not
> insist.
> 
>> * The regulator itself has similar concepts between the two, but the
>> list of bucks / ldos and how they behave is quite different.  Trying
>> to understand the complex tables in
>> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
>> was not easy.
>> 
>> 
>> If we really need to write a single driver it certainly can be done,
>> but please look at the above to be sure this is what you want.
> 
> Sure, I don't stick to the idea of one merged driver where this
> increases code size and complexity. I see your point that merging
> regulator drivers won't bring benefits but please:
> $ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
>  -i drivers/clk/clk-max77686.c
> $ diff -ubB drivers/clk/clk-max77686.c drivers/clk/clk-max77802.c
> 

I agree that there is too much duplicated code between those two and others Maxim PMIC drivers.

I also agree that at some point we have to stop duplicating code and clean up all this.

So, I think that instead of trying to make a single driver support two similar but different PMIC I can factor out the generic code in a max-core driver so the PMIC specific code is small and well contained.

I'll work on that and post a v2.

Thanks a lot for your feedback and best regards,

Javier

> The difference in number of clocks (2 vs 3) is not an obstacle here.
> 
> The same applies to main MFD driver and IRQ code. However MAX77686
> doesn't use regmap_irq_chip so it needs changes before merging the IRQ
> code into one piece.
> 
> Best regards,
> Krzysztof
> 
>> 
>> NOTE: it's possible that things could be more sane with more driver
>> redesign, possibly making things more data driven.  The thing that
>> would be really nice to do would be to avoid all of the crazy
>> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
>> doing it to be sure it's cleaner, though...
>> 
>> 
>> -Doug
> 

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-10  7:50         ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-10  7:50 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Doug Anderson, Lee Jones, Alessandro Zummo, Kukjin Kim,
	Mike Turquette, Samuel Ortiz, Tomeu Vizoso, devicetree,
	linux-kernel, Liam Girdwood, linux-samsung-soc, Sjoerd Simons,
	Mark Brown, Olof Johansson, linux-arm-kernel, Daniel Stone

Hello Krzysztof,

> On 10/06/2014, at 09:32, Krzysztof Kozlowski <k.kozlowski@samsung.com> wrote:
> 
>> On pon, 2014-06-09 at 09:04 -0700, Doug Anderson wrote:
>> Krzystof,
>> 
>> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
>> <k.kozlowski@samsung.com> wrote:
>>> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>>>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>>>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>>>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>>>> regulators, clocks and the RTC.
>>>> 
>>>> This series are based on drivers added by Simon Glass to the Chrome OS
>>>> kernel and adds support for the Maxim 77802 Power Management IC, their
>>>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>>> 
>>>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>>>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>>>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>>>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>>>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>>> 
>>>> Patches 1-4 add support for the different devices and Patch 5 enables
>>>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>>> 
>>> 
>>> Hi,
>>> 
>>> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
>>> drivers. I haven't checked other Maxim drivers but I think there will be
>>> a lot of similarities with them also. It is almost common for Maxim
>>> chipsets to share components between each other.
>>> 
>>> I think there is no need in duplicating all that stuff once again in new
>>> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
>>> merge it with max77686 (or other better candidate).
>>> 
>>> The only difference is in regulator driver. I am not sure whether this
>>> is a result of differences in chip or differences in driver design.
>> 
>> Yes, we thought the same thing when we added support for the max77802
>> in the ChromeOS tree.  Unfortunately it didn't work out half as well
>> as we thought it would.  When Javier was asking advice about sending
>> things upstream we suggested that perhaps he should split the two up.
>> 
>> 
>> You can see the result of the combined driver the ChromeOS tree (the
>> code there is older, probably misnamed as max77xxx, and doesn't have
>> the proper clock pieces, but you can get the gist):
>> 
>> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
>> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
>> 
>> 
>> Specific problems that made it ugly to have a combined driver:
>> 
>> * The RTC has many subtle differences between the 77686 and 77802.
>> They expanded it to handle a 200 year timeframe instead of 100 and
>> that meant that they had to shuffle the bits around everywhere.  They
>> also moved it to have the same i2c address as the main PMIC so all
>> addresses are different (see max77686_map in the RTC link above).
> 
> The difference in RTC registers seems the biggest but it can be solved
> in readable manner. I see other differences but there aren't many. It
> just hurts seeing so much code duplication:
> $ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
>  -i drivers/rtc/rtc-max77686.c
> $ diff -ubB drivers/rtc/rtc-max77686.c drivers/rtc/rtc-max77802.c
> 
> The combined RTC driver from ChromeOS seems fine to me... but I do not
> insist.
> 
>> * The regulator itself has similar concepts between the two, but the
>> list of bucks / ldos and how they behave is quite different.  Trying
>> to understand the complex tables in
>> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
>> was not easy.
>> 
>> 
>> If we really need to write a single driver it certainly can be done,
>> but please look at the above to be sure this is what you want.
> 
> Sure, I don't stick to the idea of one merged driver where this
> increases code size and complexity. I see your point that merging
> regulator drivers won't bring benefits but please:
> $ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
>  -i drivers/clk/clk-max77686.c
> $ diff -ubB drivers/clk/clk-max77686.c drivers/clk/clk-max77802.c
> 

I agree that there is too much duplicated code between those two and others Maxim PMIC drivers.

I also agree that at some point we have to stop duplicating code and clean up all this.

So, I think that instead of trying to make a single driver support two similar but different PMIC I can factor out the generic code in a max-core driver so the PMIC specific code is small and well contained.

I'll work on that and post a v2.

Thanks a lot for your feedback and best regards,

Javier

> The difference in number of clocks (2 vs 3) is not an obstacle here.
> 
> The same applies to main MFD driver and IRQ code. However MAX77686
> doesn't use regmap_irq_chip so it needs changes before merging the IRQ
> code into one piece.
> 
> Best regards,
> Krzysztof
> 
>> 
>> NOTE: it's possible that things could be more sane with more driver
>> redesign, possibly making things more data driven.  The thing that
>> would be really nice to do would be to avoid all of the crazy
>> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
>> doing it to be sure it's cleaner, though...
>> 
>> 
>> -Doug
> 

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 0/5] Add Maxim 77802 PMIC support
@ 2014-06-10  7:50         ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-10  7:50 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Krzysztof,

> On 10/06/2014, at 09:32, Krzysztof Kozlowski <k.kozlowski@samsung.com> wrote:
> 
>> On pon, 2014-06-09 at 09:04 -0700, Doug Anderson wrote:
>> Krzystof,
>> 
>> On Mon, Jun 9, 2014 at 3:16 AM, Krzysztof Kozlowski
>> <k.kozlowski@samsung.com> wrote:
>>> On pon, 2014-06-09 at 11:37 +0200, Javier Martinez Canillas wrote:
>>>> MAX77802 is a PMIC that contains 10 high efficiency Buck regulators,
>>>> 32 Low-dropout (LDO) regulators, two 32kHz buffered clock outputs,
>>>> a Real-Time-Clock (RTC) and a I2C interface to program the individual
>>>> regulators, clocks and the RTC.
>>>> 
>>>> This series are based on drivers added by Simon Glass to the Chrome OS
>>>> kernel and adds support for the Maxim 77802 Power Management IC, their
>>>> regulators, clocks, RTC and I2C interface. It is composed of patches:
>>>> 
>>>> [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC
>>>> [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
>>>> [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
>>>> [PATCH 4/5] rtc: Add driver for Maxim 77802 PMIC Real-Time-Clock
>>>> [PATCH 5/5] ARM: dts: Add max77802 device node for exynos5420-peach-pit
>>>> 
>>>> Patches 1-4 add support for the different devices and Patch 5 enables
>>>> the MAX77802 PMIC on the Exynos5420 based Peach pit board.
>>> 
>>> 
>>> Hi,
>>> 
>>> The main mfd, mfd irq, clk and rtc drivers look very similar to max77686
>>> drivers. I haven't checked other Maxim drivers but I think there will be
>>> a lot of similarities with them also. It is almost common for Maxim
>>> chipsets to share components between each other.
>>> 
>>> I think there is no need in duplicating all that stuff once again in new
>>> driver for another Maxim-almost-the-same-as-others-XYZ chipset. Just
>>> merge it with max77686 (or other better candidate).
>>> 
>>> The only difference is in regulator driver. I am not sure whether this
>>> is a result of differences in chip or differences in driver design.
>> 
>> Yes, we thought the same thing when we added support for the max77802
>> in the ChromeOS tree.  Unfortunately it didn't work out half as well
>> as we thought it would.  When Javier was asking advice about sending
>> things upstream we suggested that perhaps he should split the two up.
>> 
>> 
>> You can see the result of the combined driver the ChromeOS tree (the
>> code there is older, probably misnamed as max77xxx, and doesn't have
>> the proper clock pieces, but you can get the gist):
>> 
>> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c
>> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/rtc/rtc-max77xxx.c
>> 
>> 
>> Specific problems that made it ugly to have a combined driver:
>> 
>> * The RTC has many subtle differences between the 77686 and 77802.
>> They expanded it to handle a 200 year timeframe instead of 100 and
>> that meant that they had to shuffle the bits around everywhere.  They
>> also moved it to have the same i2c address as the main PMIC so all
>> addresses are different (see max77686_map in the RTC link above).
> 
> The difference in RTC registers seems the biggest but it can be solved
> in readable manner. I see other differences but there aren't many. It
> just hurts seeing so much code duplication:
> $ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
>  -i drivers/rtc/rtc-max77686.c
> $ diff -ubB drivers/rtc/rtc-max77686.c drivers/rtc/rtc-max77802.c
> 
> The combined RTC driver from ChromeOS seems fine to me... but I do not
> insist.
> 
>> * The regulator itself has similar concepts between the two, but the
>> list of bucks / ldos and how they behave is quite different.  Trying
>> to understand the complex tables in
>> <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.8/drivers/regulator/max77xxx-regulator.c>
>> was not easy.
>> 
>> 
>> If we really need to write a single driver it certainly can be done,
>> but please look at the above to be sure this is what you want.
> 
> Sure, I don't stick to the idea of one merged driver where this
> increases code size and complexity. I see your point that merging
> regulator drivers won't bring benefits but please:
> $ sed -e 's/max77686/max77802/g' -e 's/MAX77686/MAX77802/g' \
>  -i drivers/clk/clk-max77686.c
> $ diff -ubB drivers/clk/clk-max77686.c drivers/clk/clk-max77802.c
> 

I agree that there is too much duplicated code between those two and others Maxim PMIC drivers.

I also agree that at some point we have to stop duplicating code and clean up all this.

So, I think that instead of trying to make a single driver support two similar but different PMIC I can factor out the generic code in a max-core driver so the PMIC specific code is small and well contained.

I'll work on that and post a v2.

Thanks a lot for your feedback and best regards,

Javier

> The difference in number of clocks (2 vs 3) is not an obstacle here.
> 
> The same applies to main MFD driver and IRQ code. However MAX77686
> doesn't use regmap_irq_chip so it needs changes before merging the IRQ
> code into one piece.
> 
> Best regards,
> Krzysztof
> 
>> 
>> NOTE: it's possible that things could be more sane with more driver
>> redesign, possibly making things more data driven.  The thing that
>> would be really nice to do would be to avoid all of the crazy
>> "regulator_zzz_desc_zzz" macros, maybe?  I'd have to actually try
>> doing it to be sure it's cleaner, though...
>> 
>> 
>> -Doug
> 

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
  2014-06-09 23:29       ` Javier Martinez Canillas
@ 2014-06-10 10:53         ` Mark Brown
  -1 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-10 10:53 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Lee Jones, Samuel Ortiz, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1068 bytes --]

On Tue, Jun 10, 2014 at 01:29:17AM +0200, Javier Martinez Canillas wrote:
> On 06/09/2014 09:38 PM, Mark Brown wrote:
> > On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:

> >> +	case REGULATOR_MODE_STANDBY:			/* switch off */
> >> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
> >> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
> >> +			val = MAX77802_OPMODE_STANDBY;
> >> +			break;
> >> +		}
> >> +		/* no break */

> > This sounds very broken...

> The problem is that not all regulators supports the same operational modes. For
> instance regulators LDO 1, 20, 21 and 3 does not support REGULATOR_MODE_STANDBY
> so if the condition is not met a break is not needed since the default case is
> to warn that the mode is not supported.

No, it's the "switch off" comment...

> But I'll rework that logic on v2 to make it cleaner and have a break on each
> case and don't rely on case cascading.

...though you should also consider splitting things up so you have
separate ops for separate regulators if they behave differently.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 2/5] regulator: Add driver for Maxim 77802 PMIC regulators
@ 2014-06-10 10:53         ` Mark Brown
  0 siblings, 0 replies; 54+ messages in thread
From: Mark Brown @ 2014-06-10 10:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jun 10, 2014 at 01:29:17AM +0200, Javier Martinez Canillas wrote:
> On 06/09/2014 09:38 PM, Mark Brown wrote:
> > On Mon, Jun 09, 2014 at 11:37:47AM +0200, Javier Martinez Canillas wrote:

> >> +	case REGULATOR_MODE_STANDBY:			/* switch off */
> >> +		if (id != MAX77802_LDO1 && id != MAX77802_LDO20 &&
> >> +			id != MAX77802_LDO21 && id != MAX77802_LDO3) {
> >> +			val = MAX77802_OPMODE_STANDBY;
> >> +			break;
> >> +		}
> >> +		/* no break */

> > This sounds very broken...

> The problem is that not all regulators supports the same operational modes. For
> instance regulators LDO 1, 20, 21 and 3 does not support REGULATOR_MODE_STANDBY
> so if the condition is not met a break is not needed since the default case is
> to warn that the mode is not supported.

No, it's the "switch off" comment...

> But I'll rework that logic on v2 to make it cleaner and have a break on each
> case and don't rely on case cascading.

...though you should also consider splitting things up so you have
separate ops for separate regulators if they behave differently.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140610/8897b49f/attachment.sig>

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
  2014-06-09  9:37   ` Javier Martinez Canillas
@ 2014-06-16  8:44     ` Lee Jones
  -1 siblings, 0 replies; 54+ messages in thread
From: Lee Jones @ 2014-06-16  8:44 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

> The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
> Low Jitter Mode. This patch adds support for these two clocks.
> 
> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
> ---
>  .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
>  drivers/clk/Kconfig                                |   6 +
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
>  drivers/mfd/max77802.c                             |   3 +
>  include/dt-bindings/clock/maxim,max77802.h         |  22 ++
>  6 files changed, 325 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
>  create mode 100644 drivers/clk/clk-max77802.c
>  create mode 100644 include/dt-bindings/clock/maxim,max77802.h

[...]

> diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
> index 59696dd..33e8023 100644
> --- a/drivers/mfd/max77802.c
> +++ b/drivers/mfd/max77802.c
> @@ -35,6 +35,9 @@
>  
>  static const struct mfd_cell max77802_devs[] = {
>  	{ .name = "max77802-pmic", },
> +#if defined(CONFIG_COMMON_CLK_MAX77802)

No, don't do that.  If COMMON_CLK_MAX77802 is not defined it just
won't be matched, which is enough to ensure it won't be probed.

> +	{ .name = "max77802-clk", },
> +#endif
>  };

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
@ 2014-06-16  8:44     ` Lee Jones
  0 siblings, 0 replies; 54+ messages in thread
From: Lee Jones @ 2014-06-16  8:44 UTC (permalink / raw)
  To: linux-arm-kernel

> The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
> Low Jitter Mode. This patch adds support for these two clocks.
> 
> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
> ---
>  .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
>  drivers/clk/Kconfig                                |   6 +
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
>  drivers/mfd/max77802.c                             |   3 +
>  include/dt-bindings/clock/maxim,max77802.h         |  22 ++
>  6 files changed, 325 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
>  create mode 100644 drivers/clk/clk-max77802.c
>  create mode 100644 include/dt-bindings/clock/maxim,max77802.h

[...]

> diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
> index 59696dd..33e8023 100644
> --- a/drivers/mfd/max77802.c
> +++ b/drivers/mfd/max77802.c
> @@ -35,6 +35,9 @@
>  
>  static const struct mfd_cell max77802_devs[] = {
>  	{ .name = "max77802-pmic", },
> +#if defined(CONFIG_COMMON_CLK_MAX77802)

No, don't do that.  If COMMON_CLK_MAX77802 is not defined it just
won't be matched, which is enough to ensure it won't be probed.

> +	{ .name = "max77802-clk", },
> +#endif
>  };

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 54+ messages in thread

* Re: [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
  2014-06-16  8:44     ` Lee Jones
@ 2014-06-16  8:54       ` Javier Martinez Canillas
  -1 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-16  8:54 UTC (permalink / raw)
  To: Lee Jones
  Cc: Samuel Ortiz, Mark Brown, Mike Turquette, Liam Girdwood,
	Alessandro Zummo, Kukjin Kim, Doug Anderson, Olof Johansson,
	Sjoerd Simons, Daniel Stone, Tomeu Vizoso, linux-arm-kernel,
	devicetree, linux-samsung-soc, linux-kernel

Hello Lee,

On 06/16/2014 10:44 AM, Lee Jones wrote:
>> The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
>> Low Jitter Mode. This patch adds support for these two clocks.
>> 
>> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
>> ---
>>  .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
>>  drivers/clk/Kconfig                                |   6 +
>>  drivers/clk/Makefile                               |   1 +
>>  drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
>>  drivers/mfd/max77802.c                             |   3 +
>>  include/dt-bindings/clock/maxim,max77802.h         |  22 ++
>>  6 files changed, 325 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
>>  create mode 100644 drivers/clk/clk-max77802.c
>>  create mode 100644 include/dt-bindings/clock/maxim,max77802.h
> 
> [...]
> 
>> diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
>> index 59696dd..33e8023 100644
>> --- a/drivers/mfd/max77802.c
>> +++ b/drivers/mfd/max77802.c
>> @@ -35,6 +35,9 @@
>>  
>>  static const struct mfd_cell max77802_devs[] = {
>>  	{ .name = "max77802-pmic", },
>> +#if defined(CONFIG_COMMON_CLK_MAX77802)
> 
> No, don't do that.  If COMMON_CLK_MAX77802 is not defined it just
> won't be matched, which is enough to ensure it won't be probed.
>

Thanks a lot for the explanation.

I'll send a v2 patch-set soon which has changes based on the feedback from Mark
and Krzysztof. I'll take this point into account as well.

>> +	{ .name = "max77802-clk", },
>> +#endif
>>  };
> 
> [...]
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

* [PATCH 3/5] clk: Add driver for Maxim 77802 PMIC clocks
@ 2014-06-16  8:54       ` Javier Martinez Canillas
  0 siblings, 0 replies; 54+ messages in thread
From: Javier Martinez Canillas @ 2014-06-16  8:54 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Lee,

On 06/16/2014 10:44 AM, Lee Jones wrote:
>> The MAX77802 PMIC has two 32.768kHz Buffered Clock Outputs with
>> Low Jitter Mode. This patch adds support for these two clocks.
>> 
>> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
>> ---
>>  .../devicetree/bindings/clock/maxim,max77802.txt   |  40 ++++
>>  drivers/clk/Kconfig                                |   6 +
>>  drivers/clk/Makefile                               |   1 +
>>  drivers/clk/clk-max77802.c                         | 253 +++++++++++++++++++++
>>  drivers/mfd/max77802.c                             |   3 +
>>  include/dt-bindings/clock/maxim,max77802.h         |  22 ++
>>  6 files changed, 325 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/clock/maxim,max77802.txt
>>  create mode 100644 drivers/clk/clk-max77802.c
>>  create mode 100644 include/dt-bindings/clock/maxim,max77802.h
> 
> [...]
> 
>> diff --git a/drivers/mfd/max77802.c b/drivers/mfd/max77802.c
>> index 59696dd..33e8023 100644
>> --- a/drivers/mfd/max77802.c
>> +++ b/drivers/mfd/max77802.c
>> @@ -35,6 +35,9 @@
>>  
>>  static const struct mfd_cell max77802_devs[] = {
>>  	{ .name = "max77802-pmic", },
>> +#if defined(CONFIG_COMMON_CLK_MAX77802)
> 
> No, don't do that.  If COMMON_CLK_MAX77802 is not defined it just
> won't be matched, which is enough to ensure it won't be probed.
>

Thanks a lot for the explanation.

I'll send a v2 patch-set soon which has changes based on the feedback from Mark
and Krzysztof. I'll take this point into account as well.

>> +	{ .name = "max77802-clk", },
>> +#endif
>>  };
> 
> [...]
> 

Best regards,
Javier

^ permalink raw reply	[flat|nested] 54+ messages in thread

end of thread, other threads:[~2014-06-16  8:55 UTC | newest]

Thread overview: 54+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH 1/5] mfd: Add driver for Maxim 77802 Power Management IC Javier Martinez Canillas
2014-06-09  9:37   ` 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

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.