* [PATCH] mfd: add MAX8907 core driver
@ 2012-07-26 19:40 Stephen Warren
2012-07-26 20:35 ` Mark Brown
2012-07-27 6:36 ` Laxman Dewangan
0 siblings, 2 replies; 11+ messages in thread
From: Stephen Warren @ 2012-07-26 19:40 UTC (permalink / raw)
To: Samuel Ortiz
Cc: Stephen Warren, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
Mark Brown, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Gyungoh Yoo,
Laxman Dewangan, Liam Girdwood
From: Gyungoh Yoo <jack.yoo-4qAbB/aHxuBWk0Htik3J/w@public.gmane.org>
The MAX8907 is an I2C-based power-management IC containing voltage
regulators, a reset controller, a real-time clock, and a touch-screen
controller.
The original driver was written by:
* Gyungoh Yoo <jack.yoo-4qAbB/aHxuBWk0Htik3J/w@public.gmane.org>
Various fixes and enhancements by:
* Jin Park <jinyoungp-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
* Tom Cherry <tcherry-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
* Prashant Gaikwad <pgaikwad-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
* Dan Willemsen <dwillemsen-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
* Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
During upstreaming, I (swarren):
* Converted to regmap.
* Converted to irq domain, and stopped storing state in globals.
* Allowed probing from device tree.
* Renamed from max8907c->max8907, since the driver covers at least the
C and B revisions.
* General cleanup.
Signed-off-by: Gyungoh Yoo <jack.yoo-4qAbB/aHxuBWk0Htik3J/w@public.gmane.org>
Signed-off-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
Note that I also have a regulator driver for this part, which I'll post
in the near future. That driver will depend on this patch (at least the
header file). I'm not sure how dependencies between the mfd and regulator
trees are usually managed.
.../devicetree/bindings/regulator/max8907.txt | 49 +++
drivers/mfd/Kconfig | 11 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max8907-irq.c | 436 ++++++++++++++++++++
drivers/mfd/max8907.c | 213 ++++++++++
include/linux/mfd/max8907.h | 248 +++++++++++
6 files changed, 958 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/regulator/max8907.txt
create mode 100644 drivers/mfd/max8907-irq.c
create mode 100644 drivers/mfd/max8907.c
create mode 100644 include/linux/mfd/max8907.h
diff --git a/Documentation/devicetree/bindings/regulator/max8907.txt b/Documentation/devicetree/bindings/regulator/max8907.txt
new file mode 100644
index 0000000..dd48036
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/max8907.txt
@@ -0,0 +1,49 @@
+MAX8907 regulator
+
+Required properties:
+- compatible: "maxim,max8907"
+- reg: I2C slave address
+- interrupts: The interrupt output of the controller
+- regulators: A node that houses a sub-node for each regulator within the
+ device. Each sub-node is identified using the regulator-compatible
+ property, with valid values listed below. The content of each sub-node
+ is defined by the standard binding for regulators; see regulator.txt.
+
+Valid regulator-compatible values are:
+
+ sd1, sd2, sd3, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8, ldo9, ldo10,
+ ldo11, ldo12, ldo13, ldo14, ldo15, ldo16, ldo17, ldo18, ldo19, ldo20, out5v,
+ out33v, bbat, sdby, vrtc, wled.
+
+Example:
+
+ max8907@3c {
+ compatible = "maxim,max8907";
+ reg = <0x3c>;
+ interrupts = <0 86 0x4>;
+
+ regulators {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ regulator@0 {
+ reg = <0>;
+ regulator-compatible = "sd1";
+ regulator-name = "nvvdd_sv1,vdd_cpu_pmu";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ regulator@1 {
+ reg = <1>;
+ regulator-compatible = "sd2";
+ regulator-name = "nvvdd_sv2,vdd_core";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+...
+ };
+ };
+ };
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 66fd378..1ef2814 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -476,6 +476,17 @@ config MFD_MAX77693
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
+ depends on I2C=y && GENERIC_HARDIRQS
+ select REGMAP_I2C
+ help
+ Say yes here to support for Maxim Semiconductor MAX8907. This is
+ a Power Management IC. 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_MAX8925
bool "Maxim Semiconductor MAX8925 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 79dd22d..3cc47ee 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
+obj-$(CONFIG_MFD_MAX8907) += max8907.o max8907-irq.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
diff --git a/drivers/mfd/max8907-irq.c b/drivers/mfd/max8907-irq.c
new file mode 100644
index 0000000..d459a6a
--- /dev/null
+++ b/drivers/mfd/max8907-irq.c
@@ -0,0 +1,436 @@
+/*
+ * Battery driver for Maxim MAX8907
+ *
+ * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ * Based on driver/mfd/max8925-core.c,
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ * Portions based on drives/mfd/tps65910-irq.c,
+ * Copyright 2010 Texas Instruments Inc.
+ * Author: Graeme Gregory <gg-kDsPt+C1G03kYMGBc/C6ZA@public.gmane.org>
+ * Author: Jorge Eduardo Candelaria <jedu-kDsPt+C1G03kYMGBc/C6ZA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+struct max8907_irq_data {
+ int reg;
+ int mask_reg;
+ int offs; /* bit offset in mask register */
+ bool is_rtc;
+};
+
+static struct max8907_irq_data max8907_irqs[] = {
+ [MAX8907_IRQ_VCHG_DC_OVP] = {
+ .reg = MAX8907_REG_CHG_IRQ1,
+ .mask_reg = MAX8907_REG_CHG_IRQ1_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907_IRQ_VCHG_DC_F] = {
+ .reg = MAX8907_REG_CHG_IRQ1,
+ .mask_reg = MAX8907_REG_CHG_IRQ1_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907_IRQ_VCHG_DC_R] = {
+ .reg = MAX8907_REG_CHG_IRQ1,
+ .mask_reg = MAX8907_REG_CHG_IRQ1_MASK,
+ .offs = 1 << 2,
+ },
+ [MAX8907_IRQ_VCHG_THM_OK_R] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907_IRQ_VCHG_THM_OK_F] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907_IRQ_VCHG_MBATTLOW_F] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 2,
+ },
+ [MAX8907_IRQ_VCHG_MBATTLOW_R] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 3,
+ },
+ [MAX8907_IRQ_VCHG_RST] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 4,
+ },
+ [MAX8907_IRQ_VCHG_DONE] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 5,
+ },
+ [MAX8907_IRQ_VCHG_TOPOFF] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 6,
+ },
+ [MAX8907_IRQ_VCHG_TMR_FAULT] = {
+ .reg = MAX8907_REG_CHG_IRQ2,
+ .mask_reg = MAX8907_REG_CHG_IRQ2_MASK,
+ .offs = 1 << 7,
+ },
+ [MAX8907_IRQ_GPM_RSTIN] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907_IRQ_GPM_MPL] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907_IRQ_GPM_SW_3SEC] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 2,
+ },
+ [MAX8907_IRQ_GPM_EXTON_F] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 3,
+ },
+ [MAX8907_IRQ_GPM_EXTON_R] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 4,
+ },
+ [MAX8907_IRQ_GPM_SW_1SEC] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 5,
+ },
+ [MAX8907_IRQ_GPM_SW_F] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 6,
+ },
+ [MAX8907_IRQ_GPM_SW_R] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .offs = 1 << 7,
+ },
+ [MAX8907_IRQ_GPM_SYSCKEN_F] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ2,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ2_MASK,
+ .offs = 1 << 0,
+ },
+ [MAX8907_IRQ_GPM_SYSCKEN_R] = {
+ .reg = MAX8907_REG_ON_OFF_IRQ2,
+ .mask_reg = MAX8907_REG_ON_OFF_IRQ2_MASK,
+ .offs = 1 << 1,
+ },
+ [MAX8907_IRQ_RTC_ALARM1] = {
+ .reg = MAX8907_REG_RTC_IRQ,
+ .mask_reg = MAX8907_REG_RTC_IRQ_MASK,
+ .offs = 1 << 2,
+ .is_rtc = true,
+ },
+ [MAX8907_IRQ_RTC_ALARM0] = {
+ .reg = MAX8907_REG_RTC_IRQ,
+ .mask_reg = MAX8907_REG_RTC_IRQ_MASK,
+ .offs = 1 << 3,
+ .is_rtc = true,
+ },
+};
+
+static irqreturn_t max8907_irq(int irq, void *data)
+{
+ struct max8907 *chip = data;
+ int i;
+ struct regmap *rm;
+ unsigned int value;
+ struct max8907_irq_data *irq_data;
+ struct irq_data *d;
+ irqreturn_t handled = IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(max8907_irqs); i++) {
+ irq_data = &max8907_irqs[i];
+ d = irq_get_irq_data(irq_find_mapping(chip->domain, i));
+
+ if (irq_data->is_rtc)
+ rm = chip->regmap_rtc;
+ else
+ rm = chip->regmap_gen;
+
+ regmap_read(rm, irq_data->reg, &value);
+ if (!irqd_irq_disabled(d) && (value & irq_data->offs)) {
+ handle_nested_irq(irq_find_mapping(chip->domain, i));
+ handled = IRQ_HANDLED;
+ }
+ }
+
+ return handled;
+}
+
+static void max8907_irq_lock(struct irq_data *data)
+{
+ struct max8907 *chip = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&chip->irq_lock);
+}
+
+static void max8907_irq_set_masks(struct max8907 *chip)
+{
+ unsigned char irq_chg[2], irq_on[2], irq_rtc;
+ int i;
+ struct max8907_irq_data *irq_data;
+ struct irq_data *d;
+
+ irq_chg[0] = irq_chg[1] = irq_on[0] = irq_on[1] = irq_rtc = 0xFF;
+
+ for (i = 0; i < ARRAY_SIZE(max8907_irqs); i++) {
+ irq_data = &max8907_irqs[i];
+ d = irq_get_irq_data(irq_find_mapping(chip->domain, i));
+
+ if (!irqd_irq_disabled(d)) {
+ /* 1 -- disable, 0 -- enable */
+ switch (irq_data->mask_reg) {
+ case MAX8907_REG_CHG_IRQ1_MASK:
+ irq_chg[0] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_CHG_IRQ2_MASK:
+ irq_chg[1] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_ON_OFF_IRQ1_MASK:
+ irq_on[0] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_ON_OFF_IRQ2_MASK:
+ irq_on[1] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_RTC_IRQ_MASK:
+ irq_rtc &= ~irq_data->offs;
+ break;
+ default:
+ dev_err(chip->dev, "invalid mask_reg\n");
+ break;
+ }
+ }
+ }
+
+ regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ1_MASK, irq_chg[0]);
+ regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ2_MASK, irq_chg[1]);
+ regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ1_MASK,
+ irq_on[0]);
+ regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ2_MASK,
+ irq_on[1]);
+ regmap_write(chip->regmap_rtc, MAX8907_REG_RTC_IRQ_MASK, irq_rtc);
+}
+
+static void max8907_irq_sync_unlock(struct irq_data *data)
+{
+ struct max8907 *chip = irq_data_get_irq_chip_data(data);
+
+ max8907_irq_set_masks(chip);
+ mutex_unlock(&chip->irq_lock);
+}
+
+static void max8907_irq_enable(struct irq_data *data)
+{
+ /* Everything happens in max8907_irq_sync_unlock */
+}
+
+static void max8907_irq_disable(struct irq_data *data)
+{
+ /* Everything happens in max8907_irq_sync_unlock */
+}
+
+static int max8907_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ /* Everything happens in max8907_irq_sync_unlock */
+
+ return 0;
+}
+
+static struct irq_chip max8907_irq_chip = {
+ .name = "max8907",
+ .irq_bus_lock = max8907_irq_lock,
+ .irq_bus_sync_unlock = max8907_irq_sync_unlock,
+ .irq_enable = max8907_irq_enable,
+ .irq_disable = max8907_irq_disable,
+ .irq_set_wake = max8907_irq_set_wake,
+};
+
+static int max8907_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct max8907 *chip = h->host_data;
+
+ irq_set_chip_data(virq, chip);
+ irq_set_chip_and_handler(virq, &max8907_irq_chip, handle_edge_irq);
+ irq_set_nested_thread(virq, 1);
+
+ /*
+ * ARM needs us to explicitly flag the IRQ as valid
+ * and will set them noprobe when we do so.
+ */
+#ifdef CONFIG_ARM
+ set_irq_flags(virq, IRQF_VALID);
+#else
+ irq_set_noprobe(virq);
+#endif
+
+ return 0;
+}
+
+static struct irq_domain_ops max8907_domain_ops = {
+ .map = max8907_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+int __devinit max8907_irq_init(struct max8907 *chip)
+{
+ struct max8907_platform_data *pdata = dev_get_platdata(chip->dev);
+ unsigned int val;
+ int ret;
+
+ /* clear all interrupts */
+ regmap_read(chip->regmap_gen, MAX8907_REG_CHG_IRQ1, &val);
+ regmap_read(chip->regmap_gen, MAX8907_REG_CHG_IRQ2, &val);
+ regmap_read(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ1, &val);
+ regmap_read(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ2, &val);
+ regmap_read(chip->regmap_rtc, MAX8907_REG_RTC_IRQ, &val);
+
+ /* mask all interrupts */
+ regmap_write(chip->regmap_rtc, MAX8907_REG_ALARM0_CNTL, 0);
+ regmap_write(chip->regmap_rtc, MAX8907_REG_ALARM1_CNTL, 0);
+ regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ1_MASK, 0xff);
+ regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ2_MASK, 0xff);
+ regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ1_MASK, 0xff);
+ regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ2_MASK, 0xff);
+ regmap_write(chip->regmap_rtc, MAX8907_REG_RTC_IRQ_MASK, 0xff);
+
+ mutex_init(&chip->irq_lock);
+
+ if (pdata)
+ chip->domain = irq_domain_add_legacy(chip->dev->of_node,
+ chip->i2c_gen->irq,
+ pdata->irq_base,
+ 0,
+ &max8907_domain_ops,
+ chip);
+ else
+ chip->domain = irq_domain_add_linear(chip->dev->of_node,
+ chip->i2c_gen->irq,
+ &max8907_domain_ops,
+ chip);
+
+ if (!chip->domain) {
+ dev_err(chip->dev, "Failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ ret = request_threaded_irq(chip->i2c_gen->irq, NULL, max8907_irq,
+ IRQF_ONESHOT, "max8907", chip);
+ if (ret) {
+ dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
+ goto err_free_domain;
+ }
+
+ ret = device_init_wakeup(chip->dev, 1);
+ if (ret) {
+ dev_err(chip->dev, "device_init_wakeup() failed: %d\n", ret);
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_domain:
+ irq_domain_remove(chip->domain);
+err_free_irq:
+ free_irq(chip->i2c_gen->irq, chip);
+ return ret;
+}
+
+void max8907_irq_free(struct max8907 *chip)
+{
+ irq_domain_remove(chip->domain);
+ free_irq(chip->i2c_gen->irq, chip);
+}
+
+int max8907_irq_suspend(struct i2c_client *i2c, pm_message_t state)
+{
+ struct max8907 *chip = i2c_get_clientdata(i2c);
+ unsigned char irq_chg[2], irq_on[2], irq_rtc;
+ int i;
+ struct max8907_irq_data *irq_data;
+ struct irq_data *d;
+
+ irq_chg[0] = irq_chg[1] = irq_on[0] = irq_on[1] = irq_rtc = 0xFF;
+
+ for (i = 0; i < ARRAY_SIZE(max8907_irqs); i++) {
+ irq_data = &max8907_irqs[i];
+ d = irq_get_irq_data(irq_find_mapping(chip->domain, i));
+
+ if (irqd_is_wakeup_set(d)) {
+ /* 1 -- disable, 0 -- enable */
+ switch (irq_data->mask_reg) {
+ case MAX8907_REG_CHG_IRQ1_MASK:
+ irq_chg[0] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_CHG_IRQ2_MASK:
+ irq_chg[1] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_ON_OFF_IRQ1_MASK:
+ irq_on[0] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_ON_OFF_IRQ2_MASK:
+ irq_on[1] &= ~irq_data->offs;
+ break;
+ case MAX8907_REG_RTC_IRQ_MASK:
+ irq_rtc &= ~irq_data->offs;
+ break;
+ default:
+ dev_err(chip->dev, "invalid mask_reg\n");
+ break;
+ }
+ }
+ }
+
+ regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ1_MASK, irq_chg[0]);
+ regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ2_MASK, irq_chg[1]);
+ regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ1_MASK,
+ irq_on[0]);
+ regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ2_MASK,
+ irq_on[1]);
+ regmap_write(chip->regmap_rtc, MAX8907_REG_RTC_IRQ_MASK, irq_rtc);
+
+ if (device_may_wakeup(chip->dev))
+ enable_irq_wake(i2c->irq);
+ else
+ disable_irq(i2c->irq);
+
+ return 0;
+}
+
+int max8907_irq_resume(struct i2c_client *i2c)
+{
+ struct max8907 *chip = i2c_get_clientdata(i2c);
+
+ if (device_may_wakeup(chip->dev))
+ disable_irq_wake(i2c->irq);
+ else
+ enable_irq(i2c->irq);
+
+ max8907_irq_set_masks(chip);
+
+ return 0;
+}
diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c
new file mode 100644
index 0000000..dce17e1
--- /dev/null
+++ b/drivers/mfd/max8907.c
@@ -0,0 +1,213 @@
+/*
+ * max8907.c - mfd driver for MAX8907
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo-4qAbB/aHxuBWk0Htik3J/w@public.gmane.org>
+ * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static struct mfd_cell max8907_cells[] = {
+ { .name = "max8907-regulator", },
+ { .name = "max8907-rtc", },
+};
+
+static bool max8907_gen_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX8907_REG_ON_OFF_IRQ1:
+ case MAX8907_REG_ON_OFF_STAT:
+ case MAX8907_REG_ON_OFF_IRQ2:
+ case MAX8907_REG_CHG_IRQ1:
+ case MAX8907_REG_CHG_IRQ2:
+ case MAX8907_REG_CHG_STAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max8907_gen_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return !max8907_gen_is_volatile_reg(dev, reg);
+}
+
+static const struct regmap_config max8907_regmap_gen_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max8907_gen_is_volatile_reg,
+ .writeable_reg = max8907_gen_is_writeable_reg,
+ .max_register = MAX8907_REG_LDO20VOUT,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static bool max8907_rtc_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg <= MAX8907_REG_RTC_YEAR2)
+ return true;
+
+ switch (reg) {
+ case MAX8907_REG_RTC_STATUS:
+ case MAX8907_REG_RTC_IRQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max8907_rtc_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX8907_REG_RTC_STATUS:
+ case MAX8907_REG_RTC_IRQ:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config max8907_regmap_rtc_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max8907_rtc_is_volatile_reg,
+ .writeable_reg = max8907_rtc_is_writeable_reg,
+ .max_register = MAX8907_REG_MPL_CNTL,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static __devinit int max8907_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max8907 *max8907;
+ int ret;
+
+ max8907 = devm_kzalloc(&i2c->dev, sizeof(struct max8907), GFP_KERNEL);
+ if (!max8907) {
+ ret = -ENOMEM;
+ goto err_alloc_drvdata;
+ }
+
+ max8907->dev = &i2c->dev;
+ dev_set_drvdata(max8907->dev, max8907);
+
+ max8907->i2c_gen = i2c;
+ i2c_set_clientdata(i2c, max8907);
+ max8907->regmap_gen = devm_regmap_init_i2c(i2c,
+ &max8907_regmap_gen_config);
+ if (IS_ERR(max8907->regmap_gen)) {
+ ret = PTR_ERR(max8907->regmap_gen);
+ dev_err(&i2c->dev, "gen regmap init failed: %d\n", ret);
+ goto err_regmap_gen;
+ }
+
+ max8907->i2c_rtc = i2c_new_dummy(i2c->adapter, MAX8907_RTC_I2C_ADDR);
+ if (!max8907->i2c_rtc) {
+ ret = -ENOMEM;
+ goto err_dummy_rtc;
+ }
+ i2c_set_clientdata(max8907->i2c_rtc, max8907);
+ max8907->regmap_rtc = devm_regmap_init_i2c(i2c,
+ &max8907_regmap_rtc_config);
+ if (IS_ERR(max8907->regmap_rtc)) {
+ ret = PTR_ERR(max8907->regmap_rtc);
+ dev_err(&i2c->dev, "rtc regmap init failed: %d\n", ret);
+ goto err_regmap_rtc;
+ }
+
+ ret = max8907_irq_init(max8907);
+ if (ret != 0)
+ goto err_irq_init;
+
+ ret = mfd_add_devices(max8907->dev, -1, max8907_cells,
+ ARRAY_SIZE(max8907_cells), NULL, 0);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "failed to add MFD devices %d\n", ret);
+ goto err_add_devices;
+ }
+
+ return 0;
+
+err_add_devices:
+ max8907_irq_free(max8907);
+err_irq_init:
+err_regmap_rtc:
+ i2c_unregister_device(max8907->i2c_rtc);
+err_dummy_rtc:
+err_regmap_gen:
+err_alloc_drvdata:
+ return ret;
+}
+
+static __devexit int max8907_i2c_remove(struct i2c_client *i2c)
+{
+ struct max8907 *max8907 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max8907->dev);
+
+ max8907_irq_free(max8907);
+
+ i2c_unregister_device(max8907->i2c_rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id max8907_of_match[] = {
+ { .compatible = "maxim,max8907" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max8907_of_match);
+#endif
+
+static const struct i2c_device_id max8907_i2c_id[] = {
+ {"max8907", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, max8907_i2c_id);
+
+static struct i2c_driver max8907_i2c_driver = {
+ .driver = {
+ .name = "max8907",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max8907_of_match),
+ },
+ .probe = max8907_i2c_probe,
+ .remove = max8907_i2c_remove,
+ .suspend = max8907_irq_suspend,
+ .resume = max8907_irq_resume,
+ .id_table = max8907_i2c_id,
+};
+
+static int __init max8907_i2c_init(void)
+{
+ int ret = -ENODEV;
+
+ ret = i2c_add_driver(&max8907_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register I2C driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(max8907_i2c_init);
+
+static void __exit max8907_i2c_exit(void)
+{
+ i2c_del_driver(&max8907_i2c_driver);
+}
+module_exit(max8907_i2c_exit);
+
+MODULE_DESCRIPTION("MAX8907 multi-function core driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo-4qAbB/aHxuBWk0Htik3J/w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/max8907.h b/include/linux/mfd/max8907.h
new file mode 100644
index 0000000..3985904
--- /dev/null
+++ b/include/linux/mfd/max8907.h
@@ -0,0 +1,248 @@
+/*
+ * Functions to access MAX8907 power management chip.
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo-4qAbB/aHxuBWk0Htik3J/w@public.gmane.org>
+ * Copyright (C) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_MAX8907_H
+#define __LINUX_MFD_MAX8907_H
+
+#define MAX8907_GEN_I2C_ADDR (0x78 >> 1)
+#define MAX8907_ADC_I2C_ADDR (0x8e >> 1)
+#define MAX8907_RTC_I2C_ADDR (0xd0 >> 1)
+
+/* MAX8907 register map */
+#define MAX8907_REG_SYSENSEL 0x00
+#define MAX8907_REG_ON_OFF_IRQ1 0x01
+#define MAX8907_REG_ON_OFF_IRQ1_MASK 0x02
+#define MAX8907_REG_ON_OFF_STAT 0x03
+#define MAX8907_REG_SDCTL1 0x04
+#define MAX8907_REG_SDSEQCNT1 0x05
+#define MAX8907_REG_SDV1 0x06
+#define MAX8907_REG_SDCTL2 0x07
+#define MAX8907_REG_SDSEQCNT2 0x08
+#define MAX8907_REG_SDV2 0x09
+#define MAX8907_REG_SDCTL3 0x0A
+#define MAX8907_REG_SDSEQCNT3 0x0B
+#define MAX8907_REG_SDV3 0x0C
+#define MAX8907_REG_ON_OFF_IRQ2 0x0D
+#define MAX8907_REG_ON_OFF_IRQ2_MASK 0x0E
+#define MAX8907_REG_RESET_CNFG 0x0F
+#define MAX8907_REG_LDOCTL16 0x10
+#define MAX8907_REG_LDOSEQCNT16 0x11
+#define MAX8907_REG_LDO16VOUT 0x12
+#define MAX8907_REG_SDBYSEQCNT 0x13
+#define MAX8907_REG_LDOCTL17 0x14
+#define MAX8907_REG_LDOSEQCNT17 0x15
+#define MAX8907_REG_LDO17VOUT 0x16
+#define MAX8907_REG_LDOCTL1 0x18
+#define MAX8907_REG_LDOSEQCNT1 0x19
+#define MAX8907_REG_LDO1VOUT 0x1A
+#define MAX8907_REG_LDOCTL2 0x1C
+#define MAX8907_REG_LDOSEQCNT2 0x1D
+#define MAX8907_REG_LDO2VOUT 0x1E
+#define MAX8907_REG_LDOCTL3 0x20
+#define MAX8907_REG_LDOSEQCNT3 0x21
+#define MAX8907_REG_LDO3VOUT 0x22
+#define MAX8907_REG_LDOCTL4 0x24
+#define MAX8907_REG_LDOSEQCNT4 0x25
+#define MAX8907_REG_LDO4VOUT 0x26
+#define MAX8907_REG_LDOCTL5 0x28
+#define MAX8907_REG_LDOSEQCNT5 0x29
+#define MAX8907_REG_LDO5VOUT 0x2A
+#define MAX8907_REG_LDOCTL6 0x2C
+#define MAX8907_REG_LDOSEQCNT6 0x2D
+#define MAX8907_REG_LDO6VOUT 0x2E
+#define MAX8907_REG_LDOCTL7 0x30
+#define MAX8907_REG_LDOSEQCNT7 0x31
+#define MAX8907_REG_LDO7VOUT 0x32
+#define MAX8907_REG_LDOCTL8 0x34
+#define MAX8907_REG_LDOSEQCNT8 0x35
+#define MAX8907_REG_LDO8VOUT 0x36
+#define MAX8907_REG_LDOCTL9 0x38
+#define MAX8907_REG_LDOSEQCNT9 0x39
+#define MAX8907_REG_LDO9VOUT 0x3A
+#define MAX8907_REG_LDOCTL10 0x3C
+#define MAX8907_REG_LDOSEQCNT10 0x3D
+#define MAX8907_REG_LDO10VOUT 0x3E
+#define MAX8907_REG_LDOCTL11 0x40
+#define MAX8907_REG_LDOSEQCNT11 0x41
+#define MAX8907_REG_LDO11VOUT 0x42
+#define MAX8907_REG_LDOCTL12 0x44
+#define MAX8907_REG_LDOSEQCNT12 0x45
+#define MAX8907_REG_LDO12VOUT 0x46
+#define MAX8907_REG_LDOCTL13 0x48
+#define MAX8907_REG_LDOSEQCNT13 0x49
+#define MAX8907_REG_LDO13VOUT 0x4A
+#define MAX8907_REG_LDOCTL14 0x4C
+#define MAX8907_REG_LDOSEQCNT14 0x4D
+#define MAX8907_REG_LDO14VOUT 0x4E
+#define MAX8907_REG_LDOCTL15 0x50
+#define MAX8907_REG_LDOSEQCNT15 0x51
+#define MAX8907_REG_LDO15VOUT 0x52
+#define MAX8907_REG_OUT5VEN 0x54
+#define MAX8907_REG_OUT5VSEQ 0x55
+#define MAX8907_REG_OUT33VEN 0x58
+#define MAX8907_REG_OUT33VSEQ 0x59
+#define MAX8907_REG_LDOCTL19 0x5C
+#define MAX8907_REG_LDOSEQCNT19 0x5D
+#define MAX8907_REG_LDO19VOUT 0x5E
+#define MAX8907_REG_LBCNFG 0x60
+#define MAX8907_REG_SEQ1CNFG 0x64
+#define MAX8907_REG_SEQ2CNFG 0x65
+#define MAX8907_REG_SEQ3CNFG 0x66
+#define MAX8907_REG_SEQ4CNFG 0x67
+#define MAX8907_REG_SEQ5CNFG 0x68
+#define MAX8907_REG_SEQ6CNFG 0x69
+#define MAX8907_REG_SEQ7CNFG 0x6A
+#define MAX8907_REG_LDOCTL18 0x72
+#define MAX8907_REG_LDOSEQCNT18 0x73
+#define MAX8907_REG_LDO18VOUT 0x74
+#define MAX8907_REG_BBAT_CNFG 0x78
+#define MAX8907_REG_CHG_CNTL1 0x7C
+#define MAX8907_REG_CHG_CNTL2 0x7D
+#define MAX8907_REG_CHG_IRQ1 0x7E
+#define MAX8907_REG_CHG_IRQ2 0x7F
+#define MAX8907_REG_CHG_IRQ1_MASK 0x80
+#define MAX8907_REG_CHG_IRQ2_MASK 0x81
+#define MAX8907_REG_CHG_STAT 0x82
+#define MAX8907_REG_WLED_MODE_CNTL 0x84
+#define MAX8907_REG_ILED_CNTL 0x84
+#define MAX8907_REG_II1RR 0x8E
+#define MAX8907_REG_II2RR 0x8F
+#define MAX8907_REG_LDOCTL20 0x9C
+#define MAX8907_REG_LDOSEQCNT20 0x9D
+#define MAX8907_REG_LDO20VOUT 0x9E
+
+/* RTC register map */
+#define MAX8907_REG_RTC_SEC 0x00
+#define MAX8907_REG_RTC_MIN 0x01
+#define MAX8907_REG_RTC_HOURS 0x02
+#define MAX8907_REG_RTC_WEEKDAY 0x03
+#define MAX8907_REG_RTC_DATE 0x04
+#define MAX8907_REG_RTC_MONTH 0x05
+#define MAX8907_REG_RTC_YEAR1 0x06
+#define MAX8907_REG_RTC_YEAR2 0x07
+#define MAX8907_REG_ALARM0_SEC 0x08
+#define MAX8907_REG_ALARM0_MIN 0x09
+#define MAX8907_REG_ALARM0_HOURS 0x0A
+#define MAX8907_REG_ALARM0_WEEKDAY 0x0B
+#define MAX8907_REG_ALARM0_DATE 0x0C
+#define MAX8907_REG_ALARM0_MONTH 0x0D
+#define MAX8907_REG_ALARM0_YEAR1 0x0E
+#define MAX8907_REG_ALARM0_YEAR2 0x0F
+#define MAX8907_REG_ALARM1_SEC 0x10
+#define MAX8907_REG_ALARM1_MIN 0x11
+#define MAX8907_REG_ALARM1_HOURS 0x12
+#define MAX8907_REG_ALARM1_WEEKDAY 0x13
+#define MAX8907_REG_ALARM1_DATE 0x14
+#define MAX8907_REG_ALARM1_MONTH 0x15
+#define MAX8907_REG_ALARM1_YEAR1 0x16
+#define MAX8907_REG_ALARM1_YEAR2 0x17
+#define MAX8907_REG_ALARM0_CNTL 0x18
+#define MAX8907_REG_ALARM1_CNTL 0x19
+#define MAX8907_REG_RTC_STATUS 0x1A
+#define MAX8907_REG_RTC_CNTL 0x1B
+#define MAX8907_REG_RTC_IRQ 0x1C
+#define MAX8907_REG_RTC_IRQ_MASK 0x1D
+#define MAX8907_REG_MPL_CNTL 0x1E
+
+/* ADC and Touch Screen Controller register map */
+#define MAX8907_CTL 0
+#define MAX8907_SEQCNT 1
+#define MAX8907_VOUT 2
+
+/* mask bit fields */
+#define MAX8907_MASK_LDO_SEQ 0x1C
+#define MAX8907_MASK_LDO_EN 0x01
+#define MAX8907_MASK_VBBATTCV 0x03
+#define MAX8907_MASK_OUT5V_VINEN 0x10
+#define MAX8907_MASK_OUT5V_ENSRC 0x0E
+#define MAX8907_MASK_OUT5V_EN 0x01
+
+/* IRQ definitions */
+enum {
+ MAX8907_IRQ_VCHG_DC_OVP,
+ MAX8907_IRQ_VCHG_DC_F,
+ MAX8907_IRQ_VCHG_DC_R,
+ MAX8907_IRQ_VCHG_THM_OK_R,
+ MAX8907_IRQ_VCHG_THM_OK_F,
+ MAX8907_IRQ_VCHG_MBATTLOW_F,
+ MAX8907_IRQ_VCHG_MBATTLOW_R,
+ MAX8907_IRQ_VCHG_RST,
+ MAX8907_IRQ_VCHG_DONE,
+ MAX8907_IRQ_VCHG_TOPOFF,
+ MAX8907_IRQ_VCHG_TMR_FAULT,
+ MAX8907_IRQ_GPM_RSTIN,
+ MAX8907_IRQ_GPM_MPL,
+ MAX8907_IRQ_GPM_SW_3SEC,
+ MAX8907_IRQ_GPM_EXTON_F,
+ MAX8907_IRQ_GPM_EXTON_R,
+ MAX8907_IRQ_GPM_SW_1SEC,
+ MAX8907_IRQ_GPM_SW_F,
+ MAX8907_IRQ_GPM_SW_R,
+ MAX8907_IRQ_GPM_SYSCKEN_F,
+ MAX8907_IRQ_GPM_SYSCKEN_R,
+ MAX8907_IRQ_RTC_ALARM1,
+ MAX8907_IRQ_RTC_ALARM0,
+ MAX8907_NR_IRQS,
+};
+
+/* Regulator IDs */
+#define MAX8907_SD1 0
+#define MAX8907_SD2 1
+#define MAX8907_SD3 2
+#define MAX8907_LDO1 3
+#define MAX8907_LDO2 4
+#define MAX8907_LDO3 5
+#define MAX8907_LDO4 6
+#define MAX8907_LDO5 7
+#define MAX8907_LDO6 8
+#define MAX8907_LDO7 9
+#define MAX8907_LDO8 10
+#define MAX8907_LDO9 11
+#define MAX8907_LDO10 12
+#define MAX8907_LDO11 13
+#define MAX8907_LDO12 14
+#define MAX8907_LDO13 15
+#define MAX8907_LDO14 16
+#define MAX8907_LDO15 17
+#define MAX8907_LDO16 18
+#define MAX8907_LDO17 19
+#define MAX8907_LDO18 20
+#define MAX8907_LDO19 21
+#define MAX8907_LDO20 22
+#define MAX8907_OUT5V 23
+#define MAX8907_OUT33V 24
+#define MAX8907_BBAT 25
+#define MAX8907_SDBY 26
+#define MAX8907_VRTC 27
+#define MAX8907_WLED 28
+#define MAX8907_NUM_REGULATORS (MAX8907_WLED + 1)
+
+struct max8907_platform_data {
+ int irq_base;
+ struct regulator_init_data *init_data[MAX8907_NUM_REGULATORS];
+};
+
+struct max8907 {
+ struct device *dev;
+ struct mutex irq_lock;
+ struct i2c_client *i2c_gen;
+ struct i2c_client *i2c_rtc;
+ struct regmap *regmap_gen;
+ struct regmap *regmap_rtc;
+ struct irq_domain *domain;
+};
+
+int max8907_irq_init(struct max8907 *chip);
+void max8907_irq_free(struct max8907 *chip);
+int max8907_irq_suspend(struct i2c_client *i2c, pm_message_t state);
+int max8907_irq_resume(struct i2c_client *i2c);
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-07-26 19:40 [PATCH] mfd: add MAX8907 core driver Stephen Warren
@ 2012-07-26 20:35 ` Mark Brown
[not found] ` <20120726203526.GD4560-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2012-07-26 22:07 ` Stephen Warren
2012-07-27 6:36 ` Laxman Dewangan
1 sibling, 2 replies; 11+ messages in thread
From: Mark Brown @ 2012-07-26 20:35 UTC (permalink / raw)
To: Stephen Warren
Cc: Samuel Ortiz, Laxman Dewangan, Liam Girdwood, linux-kernel,
devicetree-discuss, Gyungoh Yoo, Stephen Warren
[-- Attachment #1: Type: text/plain, Size: 2688 bytes --]
On Thu, Jul 26, 2012 at 01:40:30PM -0600, Stephen Warren wrote:
> +struct max8907_irq_data {
> + int reg;
> + int mask_reg;
> + int offs; /* bit offset in mask register */
> + bool is_rtc;
> +};
This (and all the code in here) looks very much like regmap-irq (or one
of the pre-regmap drivers I wrote which were factored out to there)...
why can't we use regmap_irq?
Looking at the code it looks like a very similar pattern to the arizona
chips where you've got two IRQ domains in the chip which can be handled
with a single virtual IRQ to do the demux. We could factor that out
too easily enough, I might just do that...
> + if (!irqd_irq_disabled(d) && (value & irq_data->offs)) {
This looks very suspicious... why do we need to call
irqd_irq_disabled() here?
> + regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ1_MASK, irq_chg[0]);
> + regmap_write(chip->regmap_gen, MAX8907_REG_CHG_IRQ2_MASK, irq_chg[1]);
> + regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ1_MASK,
> + irq_on[0]);
> + regmap_write(chip->regmap_gen, MAX8907_REG_ON_OFF_IRQ2_MASK,
> + irq_on[1]);
> + regmap_write(chip->regmap_rtc, MAX8907_REG_RTC_IRQ_MASK, irq_rtc);
If you have the cache enabled regmap_update_bits() is your friend here,
it'll suppress duplicate I/O.
> +static void max8907_irq_enable(struct irq_data *data)
> +{
> + /* Everything happens in max8907_irq_sync_unlock */
> +}
> +static void max8907_irq_disable(struct irq_data *data)
> +{
> + /* Everything happens in max8907_irq_sync_unlock */
> +}
The fact that these functions are empty is the second part of the above
suspicous check for disabled IRQs. We're just completely ignoring the
caller here. What would idiomatically happen is that we'd update a
variable here then write it out in the unmask.
If these functions really should be empty then they should be omitted.
> +static int max8907_irq_set_wake(struct irq_data *data, unsigned int on)
> +{
> + /* Everything happens in max8907_irq_sync_unlock */
> +
> + return 0;
> +}
Again, this doesn't look clever at all.
> + if (irqd_is_wakeup_set(d)) {
> + /* 1 -- disable, 0 -- enable */
> + switch (irq_data->mask_reg) {
This loop we should just port over into the regmap code.
> +static const struct regmap_config max8907_regmap_gen_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .volatile_reg = max8907_gen_is_volatile_reg,
> + .writeable_reg = max8907_gen_is_writeable_reg,
> + .max_register = MAX8907_REG_LDO20VOUT,
> + .cache_type = REGCACHE_RBTREE,
> +};
Your IRQ registers appear to be clear on read which means you should
have a precious_reg callback too otherwise someone looking at the
register map in debugfs can ack interrupts.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
[not found] ` <20120726203526.GD4560-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
@ 2012-07-26 21:14 ` Stephen Warren
2012-07-26 21:51 ` Mark Brown
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Warren @ 2012-07-26 21:14 UTC (permalink / raw)
To: Mark Brown
Cc: Stephen Warren, Samuel Ortiz,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Gyungoh Yoo, Laxman Dewangan,
Liam Girdwood
On 07/26/2012 02:35 PM, Mark Brown wrote:
> On Thu, Jul 26, 2012 at 01:40:30PM -0600, Stephen Warren wrote:
>> + if (!irqd_irq_disabled(d) && (value & irq_data->offs)) {
>
> This looks very suspicious... why do we need to call
> irqd_irq_disabled() here?
I believe the status register reflects the unmasked status, it's just
the interrupt signal that's affected by the mask.
>> +static void max8907_irq_enable(struct irq_data *data) +{ + /*
>> Everything happens in max8907_irq_sync_unlock */ +}
>
>> +static void max8907_irq_disable(struct irq_data *data) +{ + /*
>> Everything happens in max8907_irq_sync_unlock */ +}
>
> The fact that these functions are empty is the second part of the
> above suspicous check for disabled IRQs. We're just completely
> ignoring the caller here. What would idiomatically happen is that
> we'd update a variable here then write it out in the unmask.
>
> If these functions really should be empty then they should be
> omitted.
>
>> +static int max8907_irq_set_wake(struct irq_data *data, unsigned
>> int on) +{ + /* Everything happens in max8907_irq_sync_unlock */
>> + + return 0; +}
>
> Again, this doesn't look clever at all.
So the idea here was that the IRQ core is already maintaining state
which describes which IRQs are enabled/disabled and wake/not. Rather
than have irq_enable/irq_disable/set_wake do nothing but save the same
state to irq_chip-specific structures, I removed the body of those
functions and instead just call irqd_irq_disabled() etc. wherever I
would have accessed the "local" state. Is that not a legitimate design
then?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-07-26 21:14 ` Stephen Warren
@ 2012-07-26 21:51 ` Mark Brown
0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2012-07-26 21:51 UTC (permalink / raw)
To: Stephen Warren
Cc: Samuel Ortiz, Laxman Dewangan, Liam Girdwood, linux-kernel,
devicetree-discuss, Gyungoh Yoo, Stephen Warren
[-- Attachment #1: Type: text/plain, Size: 1404 bytes --]
On Thu, Jul 26, 2012 at 03:14:21PM -0600, Stephen Warren wrote:
> On 07/26/2012 02:35 PM, Mark Brown wrote:
> > This looks very suspicious... why do we need to call
> > irqd_irq_disabled() here?
> I believe the status register reflects the unmasked status, it's just
> the interrupt signal that's affected by the mask.
This is totally normal, the standard pattern here is to and the status
register with the mask register before parsing.
> So the idea here was that the IRQ core is already maintaining state
> which describes which IRQs are enabled/disabled and wake/not. Rather
> than have irq_enable/irq_disable/set_wake do nothing but save the same
> state to irq_chip-specific structures, I removed the body of those
> functions and instead just call irqd_irq_disabled() etc. wherever I
> would have accessed the "local" state. Is that not a legitimate design
> then?
It seems very smelly, if you were supposed to do this then you wouldn't
have to be defining the empty operations. There's also the counter
argument that it means you've got to go through every interrupt and work
out the state before syncing rather than just being able to blast the
register states in but that's fairly weak.
I think it's a totally sensible idea to reuse the state in the core, but
I do think it's a bad idea to dance around the core's back like this
with the empty operations.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-07-26 20:35 ` Mark Brown
[not found] ` <20120726203526.GD4560-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
@ 2012-07-26 22:07 ` Stephen Warren
2012-07-26 22:16 ` Mark Brown
1 sibling, 1 reply; 11+ messages in thread
From: Stephen Warren @ 2012-07-26 22:07 UTC (permalink / raw)
To: Mark Brown
Cc: Samuel Ortiz, Laxman Dewangan, Liam Girdwood, linux-kernel,
devicetree-discuss, Gyungoh Yoo, Stephen Warren
On 07/26/2012 02:35 PM, Mark Brown wrote:
> On Thu, Jul 26, 2012 at 01:40:30PM -0600, Stephen Warren wrote:
>> + if (irqd_is_wakeup_set(d)) {
>> + /* 1 -- disable, 0 -- enable */
>> + switch (irq_data->mask_reg) {
>
> This loop we should just port over into the regmap code.
I assume the best way of doing this is to add new functions
regmap_irq_suspend()/regmap_irq_resume() (which would mask any enabled
interrupts that were not wake enabled); that way, the regmap_irq code
can loop over each register and just write it once. An alternative might
be to implement struct irq_chip's .irq_suspend/.irq_resume ops, but that
might worst-case end up with an I2C write per interrupt.
I see that the MAX8907 IRQ code does this in suspend:
if (device_may_wakeup(chip->dev))
enable_irq_wake(i2c->irq);
else
disable_irq(i2c->irq);
and this in resume:
if (device_may_wakeup(chip->dev))
disable_irq_wake(i2c->irq);
else
enable_irq(i2c->irq);
neither of which are done in regmap_irq, since it doesn't explicitly do
anything for suspend/resume at the moment. Are those code blocks
necessary? I see that regmap_irq_sync_unlock() is already calling
irq_set_irq_wake(), which implies that suspend/resume may have already
been completely taken care of?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-07-26 22:07 ` Stephen Warren
@ 2012-07-26 22:16 ` Mark Brown
0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2012-07-26 22:16 UTC (permalink / raw)
To: Stephen Warren
Cc: Samuel Ortiz, Laxman Dewangan, Liam Girdwood, linux-kernel,
devicetree-discuss, Gyungoh Yoo, Stephen Warren
[-- Attachment #1: Type: text/plain, Size: 1898 bytes --]
On Thu, Jul 26, 2012 at 04:07:12PM -0600, Stephen Warren wrote:
> On 07/26/2012 02:35 PM, Mark Brown wrote:
> > On Thu, Jul 26, 2012 at 01:40:30PM -0600, Stephen Warren wrote:
> >> + if (irqd_is_wakeup_set(d)) {
> >> + /* 1 -- disable, 0 -- enable */
> >> + switch (irq_data->mask_reg) {
> > This loop we should just port over into the regmap code.
> I assume the best way of doing this is to add new functions
> regmap_irq_suspend()/regmap_irq_resume() (which would mask any enabled
> interrupts that were not wake enabled); that way, the regmap_irq code
> can loop over each register and just write it once. An alternative might
> be to implement struct irq_chip's .irq_suspend/.irq_resume ops, but that
> might worst-case end up with an I2C write per interrupt.
irq_suspend() and irq_resume() are only supposed to be called once per
irq_chip so there should be no concern with using them. Even if they
weren't it's probably not that performance critical really.
> I see that the MAX8907 IRQ code does this in suspend:
> if (device_may_wakeup(chip->dev))
> enable_irq_wake(i2c->irq);
> else
> disable_irq(i2c->irq);
> and this in resume:
> if (device_may_wakeup(chip->dev))
> disable_irq_wake(i2c->irq);
> else
> enable_irq(i2c->irq);
> neither of which are done in regmap_irq, since it doesn't explicitly do
> anything for suspend/resume at the moment. Are those code blocks
> necessary? I see that regmap_irq_sync_unlock() is already calling
> irq_set_irq_wake(), which implies that suspend/resume may have already
> been completely taken care of?
Yes, it should already be taken care of. What the calls here are doing
is mostly allowing userspace to explicitly override the wake state on a
per chip basis. I'm not convinced it's terribly clever to implement
explicit wake support on an interrupt controller, it seems prone to
confusion. We could do that though.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-07-26 19:40 [PATCH] mfd: add MAX8907 core driver Stephen Warren
2012-07-26 20:35 ` Mark Brown
@ 2012-07-27 6:36 ` Laxman Dewangan
1 sibling, 0 replies; 11+ messages in thread
From: Laxman Dewangan @ 2012-07-27 6:36 UTC (permalink / raw)
To: Stephen Warren
Cc: Samuel Ortiz, Mark Brown, Liam Girdwood,
linux-kernel@vger.kernel.org, devicetree-discuss@lists.ozlabs.org,
Gyungoh Yoo, Stephen Warren
On Friday 27 July 2012 01:10 AM, Stephen Warren wrote:
> From: Gyungoh Yoo<jack.yoo@maxim-ic.com>
>
> +static int max8907_irq_set_wake(struct irq_data *data, unsigned int on)
> +{
> + /* Everything happens in max8907_irq_sync_unlock */
> +
Probably you need to call the wake_enable of irq which goes to the cpu here.
> + if (device_may_wakeup(chip->dev))
> + enable_irq_wake(i2c->irq);
> + else
> + disable_irq(i2c->irq);
I think lets sub devices decide the wakeups rather than it is done by
the core.
> + max8907->i2c_rtc = i2c_new_dummy(i2c->adapter, MAX8907_RTC_I2C_ADDR);
> + if (!max8907->i2c_rtc) {
> + ret = -ENOMEM;
> + goto err_dummy_rtc;
> + }
> + i2c_set_clientdata(max8907->i2c_rtc, max8907);
> + max8907->regmap_rtc = devm_regmap_init_i2c(i2c,
> +&max8907_regmap_rtc_config);
the argiment should be max8907->i2c_rtc rather than i2c here.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] mfd: add MAX8907 core driver
@ 2012-08-01 20:48 Stephen Warren
2012-08-02 16:15 ` Mark Brown
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Warren @ 2012-08-01 20:48 UTC (permalink / raw)
To: Samuel Ortiz, Mark Brown
Cc: Liam Girdwood, Laxman Dewangan, Gyungoh Yoo, linux-kernel,
devicetree-discuss, Stephen Warren
From: Gyungoh Yoo <jack.yoo@maxim-ic.com>
The MAX8907 is an I2C-based power-management IC containing voltage
regulators, a reset controller, a real-time clock, and a touch-screen
controller.
The original driver was written by:
* Gyungoh Yoo <jack.yoo@maxim-ic.com>
Various fixes and enhancements by:
* Jin Park <jinyoungp@nvidia.com>
* Tom Cherry <tcherry@nvidia.com>
* Prashant Gaikwad <pgaikwad@nvidia.com>
* Dan Willemsen <dwillemsen@nvidia.com>
* Laxman Dewangan <ldewangan@nvidia.com>
During upstreaming, I (swarren):
* Converted to regmap.
* Converted to regmap-irq.
* Allowed probing from device tree.
* Renamed from max8907c->max8907, since the driver covers at least the
C and B revisions.
* General cleanup.
Signed-off-by: Gyungoh Yoo <jack.yoo@maxim-ic.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
---
v3:
* Remove usage of new (dropped) regmap_add_irq_chips() API; the IRQ core
handles sharing threaded interrupts, so there's no need to do anything
much special to use them.
v2:
* Rework interrupt code to use regmap-irq. NOTE: This depends on new regmap
feature/patches not yet checked in.
* Implement precious regmap callback to prevent debugfs reading clear-on-
read IRQ status registers
* Fix devm_regmap_init_i2c() parameter
* Add *-supply properties to DT binding doc.
---
.../devicetree/bindings/regulator/max8907.txt | 62 ++++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 1 +
drivers/mfd/max8907.c | 336 ++++++++++++++++++++
include/linux/mfd/max8907.h | 221 +++++++++++++
5 files changed, 632 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/regulator/max8907.txt
create mode 100644 drivers/mfd/max8907.c
create mode 100644 include/linux/mfd/max8907.h
diff --git a/Documentation/devicetree/bindings/regulator/max8907.txt b/Documentation/devicetree/bindings/regulator/max8907.txt
new file mode 100644
index 0000000..4ec4877
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/max8907.txt
@@ -0,0 +1,62 @@
+MAX8907 regulator
+
+Required properties:
+- compatible: "maxim,max8907"
+- reg: I2C slave address
+- interrupts: The interrupt output of the controller
+- in-v1-supply: The input supply for SD1.
+- in-v2-supply: The input supply for SD2.
+- in-v3-supply: The input supply for SD3.
+- in1-supply: The input supply for LDO1.
+...
+- in20-supply: The input supply for LDO20.
+- lxw-supply: The input supply for WLED.
+- regulators: A node that houses a sub-node for each regulator within the
+ device. Each sub-node is identified using the regulator-compatible
+ property, with valid values listed below. The content of each sub-node
+ is defined by the standard binding for regulators; see regulator.txt.
+
+Valid regulator-compatible values are:
+
+ sd1, sd2, sd3, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8, ldo9, ldo10,
+ ldo11, ldo12, ldo13, ldo14, ldo15, ldo16, ldo17, ldo18, ldo19, ldo20, out5v,
+ out33v, bbat, sdby, vrtc, wled.
+
+Example:
+
+ max8907@3c {
+ compatible = "maxim,max8907";
+ reg = <0x3c>;
+ interrupts = <0 86 0x4>;
+
+ in-v1-supply = <&some_reg>;
+ ...
+ in1-supply = <&some_reg>;
+ ...
+ lxw-supply = <&some_reg>;
+
+ regulators {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ regulator@0 {
+ reg = <0>;
+ regulator-compatible = "sd1";
+ regulator-name = "nvvdd_sv1,vdd_cpu_pmu";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ };
+
+ regulator@1 {
+ reg = <1>;
+ regulator-compatible = "sd2";
+ regulator-name = "nvvdd_sv2,vdd_core";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+...
+ };
+ };
+ };
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d1facef..856ec00 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -476,6 +476,18 @@ config MFD_MAX77693
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
+ depends on I2C=y && GENERIC_HARDIRQS
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to support for Maxim Semiconductor MAX8907. This is
+ a Power Management IC. 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_MAX8925
bool "Maxim Semiconductor MAX8925 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 79dd22d..2a4108d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
+obj-$(CONFIG_MFD_MAX8907) += max8907.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c
new file mode 100644
index 0000000..70edbcb
--- /dev/null
+++ b/drivers/mfd/max8907.c
@@ -0,0 +1,336 @@
+/*
+ * max8907.c - mfd driver for MAX8907
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
+ * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+static struct mfd_cell max8907_cells[] = {
+ { .name = "max8907-regulator", },
+ { .name = "max8907-rtc", },
+};
+
+static bool max8907_gen_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX8907_REG_ON_OFF_IRQ1:
+ case MAX8907_REG_ON_OFF_STAT:
+ case MAX8907_REG_ON_OFF_IRQ2:
+ case MAX8907_REG_CHG_IRQ1:
+ case MAX8907_REG_CHG_IRQ2:
+ case MAX8907_REG_CHG_STAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max8907_gen_is_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX8907_REG_ON_OFF_IRQ1:
+ case MAX8907_REG_ON_OFF_IRQ2:
+ case MAX8907_REG_CHG_IRQ1:
+ case MAX8907_REG_CHG_IRQ2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max8907_gen_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return !max8907_gen_is_volatile_reg(dev, reg);
+}
+
+static const struct regmap_config max8907_regmap_gen_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max8907_gen_is_volatile_reg,
+ .precious_reg = max8907_gen_is_precious_reg,
+ .writeable_reg = max8907_gen_is_writeable_reg,
+ .max_register = MAX8907_REG_LDO20VOUT,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static bool max8907_rtc_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg <= MAX8907_REG_RTC_YEAR2)
+ return true;
+
+ switch (reg) {
+ case MAX8907_REG_RTC_STATUS:
+ case MAX8907_REG_RTC_IRQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max8907_rtc_is_precious_reg(struct device *dev, unsigned int reg)
+{
+ if (reg <= MAX8907_REG_RTC_YEAR2)
+ return true;
+
+ switch (reg) {
+ case MAX8907_REG_RTC_IRQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max8907_rtc_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX8907_REG_RTC_STATUS:
+ case MAX8907_REG_RTC_IRQ:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config max8907_regmap_rtc_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = max8907_rtc_is_volatile_reg,
+ .precious_reg = max8907_rtc_is_precious_reg,
+ .writeable_reg = max8907_rtc_is_writeable_reg,
+ .max_register = MAX8907_REG_MPL_CNTL,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_irq max8907_chg_irqs[] = {
+ { .reg_offset = 0, .mask = 1 << 0, },
+ { .reg_offset = 0, .mask = 1 << 1, },
+ { .reg_offset = 0, .mask = 1 << 2, },
+ { .reg_offset = 1, .mask = 1 << 0, },
+ { .reg_offset = 1, .mask = 1 << 1, },
+ { .reg_offset = 1, .mask = 1 << 2, },
+ { .reg_offset = 1, .mask = 1 << 3, },
+ { .reg_offset = 1, .mask = 1 << 4, },
+ { .reg_offset = 1, .mask = 1 << 5, },
+ { .reg_offset = 1, .mask = 1 << 6, },
+ { .reg_offset = 1, .mask = 1 << 7, },
+};
+
+static const struct regmap_irq_chip max8907_chg_irq_chip = {
+ .name = "max8907 chg",
+ .status_base = MAX8907_REG_CHG_IRQ1,
+ .mask_base = MAX8907_REG_CHG_IRQ1_MASK,
+ .wake_base = MAX8907_REG_CHG_IRQ1_MASK,
+ .irq_reg_stride = MAX8907_REG_CHG_IRQ2 - MAX8907_REG_CHG_IRQ1,
+ .num_regs = 2,
+ .irqs = max8907_chg_irqs,
+ .num_irqs = ARRAY_SIZE(max8907_chg_irqs),
+};
+
+static const struct regmap_irq max8907_on_off_irqs[] = {
+ { .reg_offset = 0, .mask = 1 << 0, },
+ { .reg_offset = 0, .mask = 1 << 1, },
+ { .reg_offset = 0, .mask = 1 << 2, },
+ { .reg_offset = 0, .mask = 1 << 3, },
+ { .reg_offset = 0, .mask = 1 << 4, },
+ { .reg_offset = 0, .mask = 1 << 5, },
+ { .reg_offset = 0, .mask = 1 << 6, },
+ { .reg_offset = 0, .mask = 1 << 7, },
+ { .reg_offset = 1, .mask = 1 << 0, },
+ { .reg_offset = 1, .mask = 1 << 1, },
+};
+
+static const struct regmap_irq_chip max8907_on_off_irq_chip = {
+ .name = "max8907 on_off",
+ .status_base = MAX8907_REG_ON_OFF_IRQ1,
+ .mask_base = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .wake_base = MAX8907_REG_ON_OFF_IRQ1_MASK,
+ .irq_reg_stride = MAX8907_REG_ON_OFF_IRQ2 - MAX8907_REG_ON_OFF_IRQ1,
+ .num_regs = 2,
+ .irqs = max8907_on_off_irqs,
+ .num_irqs = ARRAY_SIZE(max8907_on_off_irqs),
+};
+
+static const struct regmap_irq max8907_rtc_irqs[] = {
+ { .reg_offset = 0, .mask = 1 << 2, },
+ { .reg_offset = 0, .mask = 1 << 3, },
+};
+
+static const struct regmap_irq_chip max8907_rtc_irq_chip = {
+ .name = "max8907 rtc",
+ .status_base = MAX8907_REG_RTC_IRQ,
+ .mask_base = MAX8907_REG_RTC_IRQ_MASK,
+ .wake_base = MAX8907_REG_RTC_IRQ_MASK,
+ .num_regs = 1,
+ .irqs = max8907_rtc_irqs,
+ .num_irqs = ARRAY_SIZE(max8907_rtc_irqs),
+};
+
+static __devinit int max8907_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max8907 *max8907;
+ int ret;
+
+ max8907 = devm_kzalloc(&i2c->dev, sizeof(struct max8907), GFP_KERNEL);
+ if (!max8907) {
+ ret = -ENOMEM;
+ goto err_alloc_drvdata;
+ }
+
+ max8907->dev = &i2c->dev;
+ dev_set_drvdata(max8907->dev, max8907);
+
+ max8907->i2c_gen = i2c;
+ i2c_set_clientdata(i2c, max8907);
+ max8907->regmap_gen = devm_regmap_init_i2c(i2c,
+ &max8907_regmap_gen_config);
+ if (IS_ERR(max8907->regmap_gen)) {
+ ret = PTR_ERR(max8907->regmap_gen);
+ dev_err(&i2c->dev, "gen regmap init failed: %d\n", ret);
+ goto err_regmap_gen;
+ }
+
+ max8907->i2c_rtc = i2c_new_dummy(i2c->adapter, MAX8907_RTC_I2C_ADDR);
+ if (!max8907->i2c_rtc) {
+ ret = -ENOMEM;
+ goto err_dummy_rtc;
+ }
+ i2c_set_clientdata(max8907->i2c_rtc, max8907);
+ max8907->regmap_rtc = devm_regmap_init_i2c(max8907->i2c_rtc,
+ &max8907_regmap_rtc_config);
+ if (IS_ERR(max8907->regmap_rtc)) {
+ ret = PTR_ERR(max8907->regmap_rtc);
+ dev_err(&i2c->dev, "rtc regmap init failed: %d\n", ret);
+ goto err_regmap_rtc;
+ }
+
+ irq_set_status_flags(max8907->i2c_gen->irq, IRQ_NOAUTOEN);
+
+ ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq,
+ IRQF_ONESHOT | IRQF_SHARED, -1,
+ &max8907_chg_irq_chip,
+ &max8907->irqc_chg);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "failed to add chg irq chip: %d\n", ret);
+ goto err_irqc_chg;
+ }
+ ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq,
+ IRQF_ONESHOT | IRQF_SHARED, -1,
+ &max8907_on_off_irq_chip,
+ &max8907->irqc_on_off);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "failed to add on off irq chip: %d\n", ret);
+ goto err_irqc_on_off;
+ }
+ ret = regmap_add_irq_chip(max8907->regmap_rtc, max8907->i2c_gen->irq,
+ IRQF_ONESHOT | IRQF_SHARED, -1,
+ &max8907_rtc_irq_chip,
+ &max8907->irqc_rtc);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "failed to add rtc irq chip: %d\n", ret);
+ goto err_irqc_rtc;
+ }
+
+ enable_irq(max8907->i2c_gen->irq);
+
+ ret = mfd_add_devices(max8907->dev, -1, max8907_cells,
+ ARRAY_SIZE(max8907_cells), NULL, 0);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "failed to add MFD devices %d\n", ret);
+ goto err_add_devices;
+ }
+
+ return 0;
+
+err_add_devices:
+ regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc);
+err_irqc_rtc:
+ regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off);
+err_irqc_on_off:
+ regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg);
+err_irqc_chg:
+err_regmap_rtc:
+ i2c_unregister_device(max8907->i2c_rtc);
+err_dummy_rtc:
+err_regmap_gen:
+err_alloc_drvdata:
+ return ret;
+}
+
+static __devexit int max8907_i2c_remove(struct i2c_client *i2c)
+{
+ struct max8907 *max8907 = i2c_get_clientdata(i2c);
+
+ mfd_remove_devices(max8907->dev);
+
+ regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc);
+ regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off);
+ regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg);
+
+ i2c_unregister_device(max8907->i2c_rtc);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id max8907_of_match[] = {
+ { .compatible = "maxim,max8907" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max8907_of_match);
+#endif
+
+static const struct i2c_device_id max8907_i2c_id[] = {
+ {"max8907", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, max8907_i2c_id);
+
+static struct i2c_driver max8907_i2c_driver = {
+ .driver = {
+ .name = "max8907",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max8907_of_match),
+ },
+ .probe = max8907_i2c_probe,
+ .remove = max8907_i2c_remove,
+ .id_table = max8907_i2c_id,
+};
+
+static int __init max8907_i2c_init(void)
+{
+ int ret = -ENODEV;
+
+ ret = i2c_add_driver(&max8907_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register I2C driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(max8907_i2c_init);
+
+static void __exit max8907_i2c_exit(void)
+{
+ i2c_del_driver(&max8907_i2c_driver);
+}
+module_exit(max8907_i2c_exit);
+
+MODULE_DESCRIPTION("MAX8907 multi-function core driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/max8907.h b/include/linux/mfd/max8907.h
new file mode 100644
index 0000000..9f04a44
--- /dev/null
+++ b/include/linux/mfd/max8907.h
@@ -0,0 +1,221 @@
+/*
+ * Functions to access MAX8907 power management chip.
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
+ * Copyright (C) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MFD_MAX8907_H
+#define __LINUX_MFD_MAX8907_H
+
+#include <linux/mutex.h>
+#include <linux/pm.h>
+
+#define MAX8907_GEN_I2C_ADDR (0x78 >> 1)
+#define MAX8907_ADC_I2C_ADDR (0x8e >> 1)
+#define MAX8907_RTC_I2C_ADDR (0xd0 >> 1)
+
+/* MAX8907 register map */
+#define MAX8907_REG_SYSENSEL 0x00
+#define MAX8907_REG_ON_OFF_IRQ1 0x01
+#define MAX8907_REG_ON_OFF_IRQ1_MASK 0x02
+#define MAX8907_REG_ON_OFF_STAT 0x03
+#define MAX8907_REG_SDCTL1 0x04
+#define MAX8907_REG_SDSEQCNT1 0x05
+#define MAX8907_REG_SDV1 0x06
+#define MAX8907_REG_SDCTL2 0x07
+#define MAX8907_REG_SDSEQCNT2 0x08
+#define MAX8907_REG_SDV2 0x09
+#define MAX8907_REG_SDCTL3 0x0A
+#define MAX8907_REG_SDSEQCNT3 0x0B
+#define MAX8907_REG_SDV3 0x0C
+#define MAX8907_REG_ON_OFF_IRQ2 0x0D
+#define MAX8907_REG_ON_OFF_IRQ2_MASK 0x0E
+#define MAX8907_REG_RESET_CNFG 0x0F
+#define MAX8907_REG_LDOCTL16 0x10
+#define MAX8907_REG_LDOSEQCNT16 0x11
+#define MAX8907_REG_LDO16VOUT 0x12
+#define MAX8907_REG_SDBYSEQCNT 0x13
+#define MAX8907_REG_LDOCTL17 0x14
+#define MAX8907_REG_LDOSEQCNT17 0x15
+#define MAX8907_REG_LDO17VOUT 0x16
+#define MAX8907_REG_LDOCTL1 0x18
+#define MAX8907_REG_LDOSEQCNT1 0x19
+#define MAX8907_REG_LDO1VOUT 0x1A
+#define MAX8907_REG_LDOCTL2 0x1C
+#define MAX8907_REG_LDOSEQCNT2 0x1D
+#define MAX8907_REG_LDO2VOUT 0x1E
+#define MAX8907_REG_LDOCTL3 0x20
+#define MAX8907_REG_LDOSEQCNT3 0x21
+#define MAX8907_REG_LDO3VOUT 0x22
+#define MAX8907_REG_LDOCTL4 0x24
+#define MAX8907_REG_LDOSEQCNT4 0x25
+#define MAX8907_REG_LDO4VOUT 0x26
+#define MAX8907_REG_LDOCTL5 0x28
+#define MAX8907_REG_LDOSEQCNT5 0x29
+#define MAX8907_REG_LDO5VOUT 0x2A
+#define MAX8907_REG_LDOCTL6 0x2C
+#define MAX8907_REG_LDOSEQCNT6 0x2D
+#define MAX8907_REG_LDO6VOUT 0x2E
+#define MAX8907_REG_LDOCTL7 0x30
+#define MAX8907_REG_LDOSEQCNT7 0x31
+#define MAX8907_REG_LDO7VOUT 0x32
+#define MAX8907_REG_LDOCTL8 0x34
+#define MAX8907_REG_LDOSEQCNT8 0x35
+#define MAX8907_REG_LDO8VOUT 0x36
+#define MAX8907_REG_LDOCTL9 0x38
+#define MAX8907_REG_LDOSEQCNT9 0x39
+#define MAX8907_REG_LDO9VOUT 0x3A
+#define MAX8907_REG_LDOCTL10 0x3C
+#define MAX8907_REG_LDOSEQCNT10 0x3D
+#define MAX8907_REG_LDO10VOUT 0x3E
+#define MAX8907_REG_LDOCTL11 0x40
+#define MAX8907_REG_LDOSEQCNT11 0x41
+#define MAX8907_REG_LDO11VOUT 0x42
+#define MAX8907_REG_LDOCTL12 0x44
+#define MAX8907_REG_LDOSEQCNT12 0x45
+#define MAX8907_REG_LDO12VOUT 0x46
+#define MAX8907_REG_LDOCTL13 0x48
+#define MAX8907_REG_LDOSEQCNT13 0x49
+#define MAX8907_REG_LDO13VOUT 0x4A
+#define MAX8907_REG_LDOCTL14 0x4C
+#define MAX8907_REG_LDOSEQCNT14 0x4D
+#define MAX8907_REG_LDO14VOUT 0x4E
+#define MAX8907_REG_LDOCTL15 0x50
+#define MAX8907_REG_LDOSEQCNT15 0x51
+#define MAX8907_REG_LDO15VOUT 0x52
+#define MAX8907_REG_OUT5VEN 0x54
+#define MAX8907_REG_OUT5VSEQ 0x55
+#define MAX8907_REG_OUT33VEN 0x58
+#define MAX8907_REG_OUT33VSEQ 0x59
+#define MAX8907_REG_LDOCTL19 0x5C
+#define MAX8907_REG_LDOSEQCNT19 0x5D
+#define MAX8907_REG_LDO19VOUT 0x5E
+#define MAX8907_REG_LBCNFG 0x60
+#define MAX8907_REG_SEQ1CNFG 0x64
+#define MAX8907_REG_SEQ2CNFG 0x65
+#define MAX8907_REG_SEQ3CNFG 0x66
+#define MAX8907_REG_SEQ4CNFG 0x67
+#define MAX8907_REG_SEQ5CNFG 0x68
+#define MAX8907_REG_SEQ6CNFG 0x69
+#define MAX8907_REG_SEQ7CNFG 0x6A
+#define MAX8907_REG_LDOCTL18 0x72
+#define MAX8907_REG_LDOSEQCNT18 0x73
+#define MAX8907_REG_LDO18VOUT 0x74
+#define MAX8907_REG_BBAT_CNFG 0x78
+#define MAX8907_REG_CHG_CNTL1 0x7C
+#define MAX8907_REG_CHG_CNTL2 0x7D
+#define MAX8907_REG_CHG_IRQ1 0x7E
+#define MAX8907_REG_CHG_IRQ2 0x7F
+#define MAX8907_REG_CHG_IRQ1_MASK 0x80
+#define MAX8907_REG_CHG_IRQ2_MASK 0x81
+#define MAX8907_REG_CHG_STAT 0x82
+#define MAX8907_REG_WLED_MODE_CNTL 0x84
+#define MAX8907_REG_ILED_CNTL 0x84
+#define MAX8907_REG_II1RR 0x8E
+#define MAX8907_REG_II2RR 0x8F
+#define MAX8907_REG_LDOCTL20 0x9C
+#define MAX8907_REG_LDOSEQCNT20 0x9D
+#define MAX8907_REG_LDO20VOUT 0x9E
+
+/* RTC register map */
+#define MAX8907_REG_RTC_SEC 0x00
+#define MAX8907_REG_RTC_MIN 0x01
+#define MAX8907_REG_RTC_HOURS 0x02
+#define MAX8907_REG_RTC_WEEKDAY 0x03
+#define MAX8907_REG_RTC_DATE 0x04
+#define MAX8907_REG_RTC_MONTH 0x05
+#define MAX8907_REG_RTC_YEAR1 0x06
+#define MAX8907_REG_RTC_YEAR2 0x07
+#define MAX8907_REG_ALARM0_SEC 0x08
+#define MAX8907_REG_ALARM0_MIN 0x09
+#define MAX8907_REG_ALARM0_HOURS 0x0A
+#define MAX8907_REG_ALARM0_WEEKDAY 0x0B
+#define MAX8907_REG_ALARM0_DATE 0x0C
+#define MAX8907_REG_ALARM0_MONTH 0x0D
+#define MAX8907_REG_ALARM0_YEAR1 0x0E
+#define MAX8907_REG_ALARM0_YEAR2 0x0F
+#define MAX8907_REG_ALARM1_SEC 0x10
+#define MAX8907_REG_ALARM1_MIN 0x11
+#define MAX8907_REG_ALARM1_HOURS 0x12
+#define MAX8907_REG_ALARM1_WEEKDAY 0x13
+#define MAX8907_REG_ALARM1_DATE 0x14
+#define MAX8907_REG_ALARM1_MONTH 0x15
+#define MAX8907_REG_ALARM1_YEAR1 0x16
+#define MAX8907_REG_ALARM1_YEAR2 0x17
+#define MAX8907_REG_ALARM0_CNTL 0x18
+#define MAX8907_REG_ALARM1_CNTL 0x19
+#define MAX8907_REG_RTC_STATUS 0x1A
+#define MAX8907_REG_RTC_CNTL 0x1B
+#define MAX8907_REG_RTC_IRQ 0x1C
+#define MAX8907_REG_RTC_IRQ_MASK 0x1D
+#define MAX8907_REG_MPL_CNTL 0x1E
+
+/* ADC and Touch Screen Controller register map */
+#define MAX8907_CTL 0
+#define MAX8907_SEQCNT 1
+#define MAX8907_VOUT 2
+
+/* mask bit fields */
+#define MAX8907_MASK_LDO_SEQ 0x1C
+#define MAX8907_MASK_LDO_EN 0x01
+#define MAX8907_MASK_VBBATTCV 0x03
+#define MAX8907_MASK_OUT5V_VINEN 0x10
+#define MAX8907_MASK_OUT5V_ENSRC 0x0E
+#define MAX8907_MASK_OUT5V_EN 0x01
+
+/* Regulator IDs */
+#define MAX8907_SD1 0
+#define MAX8907_SD2 1
+#define MAX8907_SD3 2
+#define MAX8907_LDO1 3
+#define MAX8907_LDO2 4
+#define MAX8907_LDO3 5
+#define MAX8907_LDO4 6
+#define MAX8907_LDO5 7
+#define MAX8907_LDO6 8
+#define MAX8907_LDO7 9
+#define MAX8907_LDO8 10
+#define MAX8907_LDO9 11
+#define MAX8907_LDO10 12
+#define MAX8907_LDO11 13
+#define MAX8907_LDO12 14
+#define MAX8907_LDO13 15
+#define MAX8907_LDO14 16
+#define MAX8907_LDO15 17
+#define MAX8907_LDO16 18
+#define MAX8907_LDO17 19
+#define MAX8907_LDO18 20
+#define MAX8907_LDO19 21
+#define MAX8907_LDO20 22
+#define MAX8907_OUT5V 23
+#define MAX8907_OUT33V 24
+#define MAX8907_BBAT 25
+#define MAX8907_SDBY 26
+#define MAX8907_VRTC 27
+#define MAX8907_WLED 28
+#define MAX8907_NUM_REGULATORS (MAX8907_WLED + 1)
+
+struct max8907_platform_data {
+ struct regulator_init_data *init_data[MAX8907_NUM_REGULATORS];
+};
+
+struct regmap_irq_chips_data;
+
+struct max8907 {
+ struct device *dev;
+ struct mutex irq_lock;
+ struct i2c_client *i2c_gen;
+ struct i2c_client *i2c_rtc;
+ struct regmap *regmap_gen;
+ struct regmap *regmap_rtc;
+ struct regmap_irq_chip_data *irqc_chg;
+ struct regmap_irq_chip_data *irqc_on_off;
+ struct regmap_irq_chip_data *irqc_rtc;
+};
+
+#endif
--
1.7.0.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-08-01 20:48 Stephen Warren
@ 2012-08-02 16:15 ` Mark Brown
2012-08-02 17:11 ` Stephen Warren
0 siblings, 1 reply; 11+ messages in thread
From: Mark Brown @ 2012-08-02 16:15 UTC (permalink / raw)
To: Stephen Warren
Cc: Samuel Ortiz, Liam Girdwood, Laxman Dewangan, Gyungoh Yoo,
linux-kernel, devicetree-discuss, Stephen Warren
On Wed, Aug 01, 2012 at 02:48:05PM -0600, Stephen Warren wrote:
> From: Gyungoh Yoo <jack.yoo@maxim-ic.com>
>
> The MAX8907 is an I2C-based power-management IC containing voltage
> regulators, a reset controller, a real-time clock, and a touch-screen
> controller.
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-08-02 16:15 ` Mark Brown
@ 2012-08-02 17:11 ` Stephen Warren
2012-08-02 17:56 ` Mark Brown
0 siblings, 1 reply; 11+ messages in thread
From: Stephen Warren @ 2012-08-02 17:11 UTC (permalink / raw)
To: Samuel Ortiz
Cc: Mark Brown, Liam Girdwood, Laxman Dewangan, Gyungoh Yoo,
linux-kernel, devicetree-discuss, Stephen Warren
On 08/02/2012 10:15 AM, Mark Brown wrote:
> On Wed, Aug 01, 2012 at 02:48:05PM -0600, Stephen Warren wrote:
>> From: Gyungoh Yoo <jack.yoo@maxim-ic.com>
>>
>> The MAX8907 is an I2C-based power-management IC containing voltage
>> regulators, a reset controller, a real-time clock, and a touch-screen
>> controller.
>
> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Thanks very much.
Samuel, please don't apply this just yet though - it looks like I need
to make some minor changes to the header file and DT binding
documentation to add in a definition for one more regulator. I'll repost
the amended version along with the regulator driver later.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] mfd: add MAX8907 core driver
2012-08-02 17:11 ` Stephen Warren
@ 2012-08-02 17:56 ` Mark Brown
0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2012-08-02 17:56 UTC (permalink / raw)
To: Stephen Warren
Cc: Samuel Ortiz, Liam Girdwood, Laxman Dewangan, Gyungoh Yoo,
linux-kernel, devicetree-discuss, Stephen Warren
On Thu, Aug 02, 2012 at 11:11:21AM -0600, Stephen Warren wrote:
> Samuel, please don't apply this just yet though - it looks like I need
> to make some minor changes to the header file and DT binding
> documentation to add in a definition for one more regulator. I'll repost
> the amended version along with the regulator driver later.
Please carry my review forward on the next submission.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2012-08-02 17:56 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-26 19:40 [PATCH] mfd: add MAX8907 core driver Stephen Warren
2012-07-26 20:35 ` Mark Brown
[not found] ` <20120726203526.GD4560-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2012-07-26 21:14 ` Stephen Warren
2012-07-26 21:51 ` Mark Brown
2012-07-26 22:07 ` Stephen Warren
2012-07-26 22:16 ` Mark Brown
2012-07-27 6:36 ` Laxman Dewangan
-- strict thread matches above, loose matches on Subject: below --
2012-08-01 20:48 Stephen Warren
2012-08-02 16:15 ` Mark Brown
2012-08-02 17:11 ` Stephen Warren
2012-08-02 17:56 ` Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).