2015-06-12 08:44:02

by Yi Zhang

[permalink] [raw]
Subject: [PATCH V2 0/2] add basic support to Marvell 88pm880/88pm886 PMIC chip

- 88pm880 and 88pm886 are PMIC chips which integrates regulator,
gpadc, charger, fuelgauge, etc;
they share most of the functions and register mapping
- this version removes the redundant EXPORT_SYMBOL_GPL, compared with the
first version


Yi Zhang (2):
mfd: add Marvell 88pm88x description
mfd: 88pm88x: initialize 88pm886/88pm880 base support

Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 ++
drivers/mfd/88pm880-table.c | 173 +++++++
drivers/mfd/88pm886-table.c | 173 +++++++
drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++
drivers/mfd/88pm88x-i2c.c | 167 +++++++
drivers/mfd/88pm88x-irq.c | 171 +++++++
drivers/mfd/88pm88x.h | 51 ++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 3 +
include/linux/mfd/88pm880-reg.h | 98 ++++
include/linux/mfd/88pm880.h | 59 +++
include/linux/mfd/88pm886-reg.h | 59 +++
include/linux/mfd/88pm886.h | 55 ++
include/linux/mfd/88pm88x-reg.h | 118 +++++
include/linux/mfd/88pm88x.h | 202 ++++++++
15 files changed, 1958 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
create mode 100644 drivers/mfd/88pm880-table.c
create mode 100644 drivers/mfd/88pm886-table.c
create mode 100644 drivers/mfd/88pm88x-core.c
create mode 100644 drivers/mfd/88pm88x-i2c.c
create mode 100644 drivers/mfd/88pm88x-irq.c
create mode 100644 drivers/mfd/88pm88x.h
create mode 100644 include/linux/mfd/88pm880-reg.h
create mode 100644 include/linux/mfd/88pm880.h
create mode 100644 include/linux/mfd/88pm886-reg.h
create mode 100644 include/linux/mfd/88pm886.h
create mode 100644 include/linux/mfd/88pm88x-reg.h
create mode 100644 include/linux/mfd/88pm88x.h

--
1.9.1


2015-06-12 08:45:11

by Yi Zhang

[permalink] [raw]
Subject: [PATCH V2 1/2] mfd: add Marvell 88pm88x description

88pm880 and 88pm886 are two combo PMIC chips, most of the function and the
register mapping are the same

Signed-off-by: Yi Zhang <[email protected]>
---
Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 +++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt

diff --git a/Documentation/devicetree/bindings/mfd/88pm88x.txt b/Documentation/devicetree/bindings/mfd/88pm88x.txt
new file mode 100644
index 0000000..72e741c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/88pm88x.txt
@@ -0,0 +1,33 @@
+Marvell 88pm88x combo PMIC chip
+
+This series of chip integrates regulator, rtc, onkey, switch charger,
+fuelgauge, gpadc and a range of I/O pins.
+
+88pm886 and 88pm880 are two very similar chips, most of the registers mapping
+and functions are the same, the main difference is the latter has a separate
+i2c slave address to cover BUCK related setting
+
+Required properties:
+- compatible: one of the strings for a specific chip:
+ "marvell,88pm886"
+ "marvell,88pm880"
+- reg: the i2c address
+- interrupt-controller: it works as an interrupt controller managing its irqs
+- interrupt-cells: this value is 1
+
+Optional properties:
+- marvell,88pm88x-irq-write-clear: the interrupt ack method
+
+
+Example:
+
+ pmic: 88pm886@30 {
+ compatible = "marvell,88pm886";
+ reg = <0x30>;
+ interrupts = <0 4 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ marvell,88pm88x-irq-write-clear;
+ };
--
1.9.1

2015-06-12 08:44:25

by Yi Zhang

[permalink] [raw]
Subject: [PATCH V2 2/2] mfd: 88pm88x: initialize 88pm886/88pm880 base support

88pm886 and 88pm880 are combo PMIC chip, which integrates
regulator, onkey, rtc, gpadc, charger, fuelgauge function;

this patch add the basic support for them, adding related resource, such as
interrupt, preparing for the client-device driver

Signed-off-by: Yi Zhang <[email protected]>
---
drivers/mfd/88pm880-table.c | 173 ++++++++++++
drivers/mfd/88pm886-table.c | 173 ++++++++++++
drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++++++++++++++++++++
drivers/mfd/88pm88x-i2c.c | 167 ++++++++++++
drivers/mfd/88pm88x-irq.c | 171 ++++++++++++
drivers/mfd/88pm88x.h | 51 ++++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 3 +
include/linux/mfd/88pm880-reg.h | 98 +++++++
include/linux/mfd/88pm880.h | 59 ++++
include/linux/mfd/88pm886-reg.h | 59 ++++
include/linux/mfd/88pm886.h | 55 ++++
include/linux/mfd/88pm88x-reg.h | 118 ++++++++
include/linux/mfd/88pm88x.h | 202 ++++++++++++++
14 files changed, 1925 insertions(+)
create mode 100644 drivers/mfd/88pm880-table.c
create mode 100644 drivers/mfd/88pm886-table.c
create mode 100644 drivers/mfd/88pm88x-core.c
create mode 100644 drivers/mfd/88pm88x-i2c.c
create mode 100644 drivers/mfd/88pm88x-irq.c
create mode 100644 drivers/mfd/88pm88x.h
create mode 100644 include/linux/mfd/88pm880-reg.h
create mode 100644 include/linux/mfd/88pm880.h
create mode 100644 include/linux/mfd/88pm886-reg.h
create mode 100644 include/linux/mfd/88pm886.h
create mode 100644 include/linux/mfd/88pm88x-reg.h
create mode 100644 include/linux/mfd/88pm88x.h

diff --git a/drivers/mfd/88pm880-table.c b/drivers/mfd/88pm880-table.c
new file mode 100644
index 0000000..28ca860
--- /dev/null
+++ b/drivers/mfd/88pm880-table.c
@@ -0,0 +1,173 @@
+/*
+ * Marvell 88PM880 specific setting
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <linux/mfd/88pm88x.h>
+#include <linux/mfd/88pm880.h>
+#include <linux/mfd/88pm880-reg.h>
+#include <linux/mfd/88pm88x-reg.h>
+
+#include "88pm88x.h"
+
+#define PM880_BUCK_NAME "88pm880-buck"
+#define PM880_LDO_NAME "88pm880-ldo"
+
+const struct regmap_config pm880_base_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm880_power_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm880_gpadc_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm880_battery_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm880_test_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+static const struct resource buck_resources[] = {
+ {
+ .name = PM880_BUCK_NAME,
+ },
+};
+
+static const struct resource ldo_resources[] = {
+ {
+ .name = PM880_LDO_NAME,
+ },
+};
+
+const struct mfd_cell pm880_cell_devs[] = {
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck1a", 0),
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck2", 1),
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck3", 2),
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck4", 3),
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck5", 4),
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck6", 5),
+ CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck7", 6),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo1", 7),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo2", 8),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo3", 9),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo4", 10),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo5", 11),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo6", 12),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo7", 13),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo8", 14),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo9", 15),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo10", 16),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo11", 17),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo12", 18),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo13", 19),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo14", 20),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo15", 21),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo16", 22),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo17", 23),
+ CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo18", 24),
+};
+
+struct pmic_cell_info pm880_cell_info = {
+ .cells = pm880_cell_devs,
+ .cell_nr = ARRAY_SIZE(pm880_cell_devs),
+};
+
+static const struct reg_default pm880_base_patch[] = {
+ {PM88X_WDOG, 0x1}, /* disable watchdog */
+ {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
+ {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
+ {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
+ /* enable LPM for internal reference group in sleep */
+ {PM88X_LOWPOWER4, 0xc0},
+ {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
+};
+
+static const struct reg_default pm880_power_patch[] = {
+};
+
+static const struct reg_default pm880_gpadc_patch[] = {
+ {PM88X_GPADC_CONFIG6, 0x03}, /* enable non-stop mode */
+};
+
+static const struct reg_default pm880_battery_patch[] = {
+ {PM88X_CHGBK_CONFIG6, 0xe1},
+};
+
+static const struct reg_default pm880_test_patch[] = {
+};
+
+/* 88pm880 chip itself related */
+int pm880_apply_patch(struct pm88x_chip *chip)
+{
+ int ret, size;
+
+ if (!chip || !chip->base_regmap || !chip->power_regmap ||
+ !chip->gpadc_regmap || !chip->battery_regmap ||
+ !chip->test_regmap)
+ return -EINVAL;
+
+ size = ARRAY_SIZE(pm880_base_patch);
+ if (size == 0)
+ goto power;
+ ret = regmap_register_patch(chip->base_regmap, pm880_base_patch, size);
+ if (ret < 0)
+ return ret;
+
+power:
+ size = ARRAY_SIZE(pm880_power_patch);
+ if (size == 0)
+ goto gpadc;
+ ret = regmap_register_patch(chip->power_regmap, pm880_power_patch, size);
+ if (ret < 0)
+ return ret;
+
+gpadc:
+ size = ARRAY_SIZE(pm880_gpadc_patch);
+ if (size == 0)
+ goto battery;
+ ret = regmap_register_patch(chip->gpadc_regmap, pm880_gpadc_patch, size);
+ if (ret < 0)
+ return ret;
+battery:
+ size = ARRAY_SIZE(pm880_battery_patch);
+ if (size == 0)
+ goto test;
+ ret = regmap_register_patch(chip->battery_regmap, pm880_battery_patch, size);
+ if (ret < 0)
+ return ret;
+
+test:
+ size = ARRAY_SIZE(pm880_test_patch);
+ if (size == 0)
+ goto out;
+ ret = regmap_register_patch(chip->test_regmap, pm880_test_patch, size);
+ if (ret < 0)
+ return ret;
+out:
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm880_apply_patch);
diff --git a/drivers/mfd/88pm886-table.c b/drivers/mfd/88pm886-table.c
new file mode 100644
index 0000000..897ee82
--- /dev/null
+++ b/drivers/mfd/88pm886-table.c
@@ -0,0 +1,173 @@
+/*
+ * Marvell 88PM886 specific setting
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <linux/mfd/88pm88x.h>
+#include <linux/mfd/88pm886.h>
+#include <linux/mfd/88pm886-reg.h>
+#include <linux/mfd/88pm88x-reg.h>
+
+#include "88pm88x.h"
+
+#define PM886_BUCK_NAME "88pm886-buck"
+#define PM886_LDO_NAME "88pm886-ldo"
+
+const struct regmap_config pm886_base_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm886_power_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm886_gpadc_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm886_battery_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+const struct regmap_config pm886_test_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xfe,
+};
+
+static const struct resource buck_resources[] = {
+ {
+ .name = PM886_BUCK_NAME,
+ },
+};
+
+static const struct resource ldo_resources[] = {
+ {
+ .name = PM886_LDO_NAME,
+ },
+};
+
+const struct mfd_cell pm886_cell_devs[] = {
+ CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck1", 0),
+ CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck2", 1),
+ CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck3", 2),
+ CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck4", 3),
+ CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck5", 4),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo1", 5),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo2", 6),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo3", 7),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo4", 8),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo5", 9),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo6", 10),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo7", 11),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo8", 12),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo9", 13),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo10", 14),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo11", 15),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo12", 16),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo13", 17),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo14", 18),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo15", 19),
+ CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo16", 20),
+};
+
+struct pmic_cell_info pm886_cell_info = {
+ .cells = pm886_cell_devs,
+ .cell_nr = ARRAY_SIZE(pm886_cell_devs),
+};
+
+static const struct reg_default pm886_base_patch[] = {
+ {PM88X_WDOG, 0x1}, /* disable watchdog */
+ {PM88X_GPIO_CTRL1, 0x40}, /* gpio1: dvc , gpio0: input */
+ {PM88X_GPIO_CTRL2, 0x00}, /* , gpio2: input */
+ {PM88X_GPIO_CTRL3, 0x44}, /* dvc2 , dvc1 */
+ {PM88X_GPIO_CTRL4, 0x00}, /* gpio5v_1:input, gpio5v_2: input*/
+ {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
+ {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
+ {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
+ /* enable LPM for internal reference group in sleep */
+ {PM88X_LOWPOWER4, 0xc0},
+ {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
+};
+
+static const struct reg_default pm886_power_patch[] = {
+};
+
+static const struct reg_default pm886_gpadc_patch[] = {
+ {PM88X_GPADC_CONFIG6, 0x03}, /* enable non-stop mode */
+};
+
+static const struct reg_default pm886_battery_patch[] = {
+ {PM88X_CHGBK_CONFIG6, 0xe1},
+};
+
+static const struct reg_default pm886_test_patch[] = {
+};
+
+/* 88pm886 chip itself related */
+int pm886_apply_patch(struct pm88x_chip *chip)
+{
+ int ret, size;
+
+ if (!chip || !chip->base_regmap || !chip->power_regmap ||
+ !chip->gpadc_regmap || !chip->battery_regmap ||
+ !chip->test_regmap)
+ return -EINVAL;
+
+ size = ARRAY_SIZE(pm886_base_patch);
+ if (size == 0)
+ goto power;
+ ret = regmap_register_patch(chip->base_regmap, pm886_base_patch, size);
+ if (ret < 0)
+ return ret;
+
+power:
+ size = ARRAY_SIZE(pm886_power_patch);
+ if (size == 0)
+ goto gpadc;
+ ret = regmap_register_patch(chip->power_regmap, pm886_power_patch, size);
+ if (ret < 0)
+ return ret;
+
+gpadc:
+ size = ARRAY_SIZE(pm886_gpadc_patch);
+ if (size == 0)
+ goto battery;
+ ret = regmap_register_patch(chip->gpadc_regmap, pm886_gpadc_patch, size);
+ if (ret < 0)
+ return ret;
+battery:
+ size = ARRAY_SIZE(pm886_battery_patch);
+ if (size == 0)
+ goto test;
+ ret = regmap_register_patch(chip->battery_regmap, pm886_battery_patch, size);
+ if (ret < 0)
+ return ret;
+
+test:
+ size = ARRAY_SIZE(pm886_test_patch);
+ if (size == 0)
+ goto out;
+ ret = regmap_register_patch(chip->test_regmap, pm886_test_patch, size);
+ if (ret < 0)
+ return ret;
+out:
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm886_apply_patch);
diff --git a/drivers/mfd/88pm88x-core.c b/drivers/mfd/88pm88x-core.c
new file mode 100644
index 0000000..343e0a0
--- /dev/null
+++ b/drivers/mfd/88pm88x-core.c
@@ -0,0 +1,584 @@
+/*
+ * Base driver for Marvell 88PM886/88PM880 PMIC
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm88x.h>
+#include <linux/mfd/88pm886.h>
+#include <linux/mfd/88pm880.h>
+#include <linux/regulator/machine.h>
+
+#include "88pm88x.h"
+
+#define PM88X_POWER_UP_LOG (0x17)
+#define PM88X_POWER_DOWN_LOG1 (0xe5)
+#define PM88X_POWER_DOWN_LOG2 (0xe6)
+#define PM88X_SW_PDOWN (1 << 5)
+
+static const struct resource onkey_resources[] = {
+ CELL_IRQ_RESOURCE(PM88X_ONKEY_NAME, PM88X_IRQ_ONKEY),
+};
+
+static const struct resource rtc_resources[] = {
+ CELL_IRQ_RESOURCE(PM88X_RTC_NAME, PM88X_IRQ_RTC),
+};
+
+static const struct resource charger_resources[] = {
+ CELL_IRQ_RESOURCE("88pm88x-chg-fail", PM88X_IRQ_CHG_FAIL),
+ CELL_IRQ_RESOURCE("88pm88x-chg-done", PM88X_IRQ_CHG_DONE),
+ CELL_IRQ_RESOURCE("88pm88x-chg-good", PM88X_IRQ_CHG_GOOD),
+};
+
+static const struct resource battery_resources[] = {
+ CELL_IRQ_RESOURCE("88pm88x-bat-cc", PM88X_IRQ_CC),
+ CELL_IRQ_RESOURCE("88pm88x-bat-volt", PM88X_IRQ_VBAT),
+ CELL_IRQ_RESOURCE("88pm88x-bat-detect", PM88X_IRQ_BAT_DET),
+};
+
+static const struct resource headset_resources[] = {
+ CELL_IRQ_RESOURCE("88pm88x-headset-det", PM88X_IRQ_HS_DET),
+ CELL_IRQ_RESOURCE("88pm88x-mic-det", PM88X_IRQ_MIC_DET),
+};
+
+static const struct resource vbus_resources[] = {
+ CELL_IRQ_RESOURCE("88pm88x-vbus-det", PM88X_IRQ_VBUS),
+ CELL_IRQ_RESOURCE("88pm88x-gpadc0", PM88X_IRQ_GPADC0),
+ CELL_IRQ_RESOURCE("88pm88x-gpadc1", PM88X_IRQ_GPADC1),
+ CELL_IRQ_RESOURCE("88pm88x-gpadc2", PM88X_IRQ_GPADC2),
+ CELL_IRQ_RESOURCE("88pm88x-gpadc3", PM88X_IRQ_GPADC3),
+ CELL_IRQ_RESOURCE("88pm88x-otg-fail", PM88X_IRQ_OTG_FAIL),
+};
+
+static const struct resource leds_resources[] = {
+ CELL_IRQ_RESOURCE("88pm88x-cfd-fail", PM88X_IRQ_CFD_FAIL),
+};
+
+static const struct resource dvc_resources[] = {
+ {
+ .name = PM88X_DVC_NAME,
+ },
+};
+
+static const struct resource rgb_resources[] = {
+ {
+ .name = PM88X_RGB_NAME,
+ },
+};
+
+static const struct resource gpadc_resources[] = {
+ {
+ .name = PM88X_GPADC_NAME,
+ },
+};
+
+static const struct mfd_cell common_cell_devs[] = {
+ CELL_DEV(PM88X_RTC_NAME, rtc_resources, "marvell,88pm88x-rtc", -1),
+ CELL_DEV(PM88X_ONKEY_NAME, onkey_resources, "marvell,88pm88x-onkey", -1),
+ CELL_DEV(PM88X_CHARGER_NAME, charger_resources, "marvell,88pm88x-charger", -1),
+ CELL_DEV(PM88X_BATTERY_NAME, battery_resources, "marvell,88pm88x-battery", -1),
+ CELL_DEV(PM88X_HEADSET_NAME, headset_resources, "marvell,88pm88x-headset", -1),
+ CELL_DEV(PM88X_VBUS_NAME, vbus_resources, "marvell,88pm88x-vbus", -1),
+ CELL_DEV(PM88X_CFD_NAME, leds_resources, "marvell,88pm88x-leds", PM88X_FLASH_LED),
+ CELL_DEV(PM88X_CFD_NAME, leds_resources, "marvell,88pm88x-leds", PM88X_TORCH_LED),
+ CELL_DEV(PM88X_DVC_NAME, dvc_resources, "marvell,88pm88x-dvc", -1),
+ CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb0", PM88X_RGB_LED0),
+ CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb1", PM88X_RGB_LED1),
+ CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb2", PM88X_RGB_LED2),
+ CELL_DEV(PM88X_GPADC_NAME, gpadc_resources, "marvell,88pm88x-gpadc", -1),
+};
+
+const struct of_device_id pm88x_of_match[] = {
+ { .compatible = "marvell,88pm886", .data = (void *)PM886 },
+ { .compatible = "marvell,88pm880", .data = (void *)PM880 },
+ {},
+};
+
+struct pm88x_chip *pm88x_init_chip(struct i2c_client *client)
+{
+ struct pm88x_chip *chip;
+
+ chip = devm_kzalloc(&client->dev, sizeof(struct pm88x_chip), GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ chip->client = client;
+ chip->irq = client->irq;
+ chip->dev = &client->dev;
+ chip->ldo_page_addr = client->addr + 1;
+ chip->power_page_addr = client->addr + 1;
+ chip->gpadc_page_addr = client->addr + 2;
+ chip->battery_page_addr = client->addr + 3;
+ chip->buck_page_addr = client->addr + 4;
+ chip->test_page_addr = client->addr + 7;
+
+ dev_set_drvdata(chip->dev, chip);
+ i2c_set_clientdata(chip->client, chip);
+
+ device_init_wakeup(&client->dev, 1);
+
+ return chip;
+}
+
+int pm88x_parse_dt(struct device_node *np, struct pm88x_chip *chip)
+{
+ if (!chip)
+ return -EINVAL;
+
+ chip->irq_mode =
+ !of_property_read_bool(np, "marvell,88pm88x-irq-write-clear");
+
+ return 0;
+}
+
+
+static void parse_powerup_down_log(struct pm88x_chip *chip)
+{
+ int powerup, powerdown1, powerdown2, bit;
+ int powerup_bits, powerdown1_bits, powerdown2_bits;
+ static const char * const powerup_name[] = {
+ "ONKEY_WAKEUP ",
+ "CHG_WAKEUP ",
+ "EXTON_WAKEUP ",
+ "SMPL_WAKEUP ",
+ "ALARM_WAKEUP ",
+ "FAULT_WAKEUP ",
+ "BAT_WAKEUP ",
+ "WLCHG_WAKEUP ",
+ };
+ static const char * const powerdown1_name[] = {
+ "OVER_TEMP ",
+ "UV_VANA5 ",
+ "SW_PDOWN ",
+ "FL_ALARM ",
+ "WD ",
+ "LONG_ONKEY",
+ "OV_VSYS ",
+ "RTC_RESET "
+ };
+ static const char * const powerdown2_name[] = {
+ "HYB_DONE ",
+ "UV_VBAT ",
+ "HW_RESET2 ",
+ "PGOOD_PDOWN",
+ "LONKEY_RTC ",
+ "HW_RESET1 ",
+ };
+
+ regmap_read(chip->base_regmap, PM88X_POWER_UP_LOG, &powerup);
+ regmap_read(chip->base_regmap, PM88X_POWER_DOWN_LOG1, &powerdown1);
+ regmap_read(chip->base_regmap, PM88X_POWER_DOWN_LOG2, &powerdown2);
+
+ /*
+ * mask reserved bits
+ *
+ * note: HYB_DONE and HW_RESET1 are kept,
+ * but should not be considered as power down events
+ */
+ switch (chip->type) {
+ case PM886:
+ powerup &= 0x7f;
+ powerdown2 &= 0x1f;
+ powerup_bits = 7;
+ powerdown1_bits = 8;
+ powerdown2_bits = 5;
+ break;
+ case PM880:
+ powerdown2 &= 0x3f;
+ powerup_bits = 8;
+ powerdown1_bits = 8;
+ powerdown2_bits = 6;
+ break;
+ default:
+ return;
+ }
+
+ /* keep globals for external usage */
+ chip->powerup = powerup;
+ chip->powerdown1 = powerdown1;
+ chip->powerdown2 = powerdown2;
+
+ /* power up log */
+ dev_info(chip->dev, "powerup log 0x%x: 0x%x\n",
+ PM88X_POWER_UP_LOG, powerup);
+ dev_info(chip->dev, " ----------------------------\n");
+ dev_info(chip->dev, "| name(power up) | status |\n");
+ dev_info(chip->dev, "|-----------------|----------|\n");
+ for (bit = 0; bit < powerup_bits; bit++)
+ dev_info(chip->dev, "| %s | %x |\n",
+ powerup_name[bit], (powerup >> bit) & 1);
+ dev_info(chip->dev, " ----------------------------\n");
+
+ /* power down log1 */
+ dev_info(chip->dev, "PowerDW Log1 0x%x: 0x%x\n",
+ PM88X_POWER_DOWN_LOG1, powerdown1);
+ dev_info(chip->dev, " -------------------------------\n");
+ dev_info(chip->dev, "| name(power down1) | status |\n");
+ dev_info(chip->dev, "|--------------------|----------|\n");
+ for (bit = 0; bit < powerdown1_bits; bit++)
+ dev_info(chip->dev, "| %s | %x |\n",
+ powerdown1_name[bit], (powerdown1 >> bit) & 1);
+ dev_info(chip->dev, " -------------------------------\n");
+
+ /* power down log2 */
+ dev_info(chip->dev, "PowerDW Log2 0x%x: 0x%x\n",
+ PM88X_POWER_DOWN_LOG2, powerdown2);
+ dev_info(chip->dev, " -------------------------------\n");
+ dev_info(chip->dev, "| name(power down2) | status |\n");
+ dev_info(chip->dev, "|--------------------|----------|\n");
+ for (bit = 0; bit < powerdown2_bits; bit++)
+ dev_info(chip->dev, "| %s | %x |\n",
+ powerdown2_name[bit], (powerdown2 >> bit) & 1);
+ dev_info(chip->dev, " -------------------------------\n");
+
+ /* write to clear power down log */
+ regmap_write(chip->base_regmap, PM88X_POWER_DOWN_LOG1, 0xff);
+ regmap_write(chip->base_regmap, PM88X_POWER_DOWN_LOG2, 0xff);
+}
+
+static const char *chip_stepping_to_string(unsigned int id)
+{
+ switch (id) {
+ case 0xa1:
+ return "88pm886 A1";
+ case 0xb1:
+ return "88pm880 A1";
+ default:
+ return "Unknown";
+ }
+}
+
+int pm88x_post_init_chip(struct pm88x_chip *chip)
+{
+ int ret;
+ unsigned int val;
+
+ if (!chip || !chip->base_regmap || !chip->power_regmap ||
+ !chip->gpadc_regmap || !chip->battery_regmap)
+ return -EINVAL;
+
+ /* save chip stepping */
+ ret = regmap_read(chip->base_regmap, PM88X_ID_REG, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read chip ID: %d\n", ret);
+ return ret;
+ }
+ chip->chip_id = val;
+
+ dev_info(chip->dev, "PM88X chip ID = 0x%x(%s)\n", val,
+ chip_stepping_to_string(chip->chip_id));
+
+ /* read before alarm wake up bit before initialize interrupt */
+ ret = regmap_read(chip->base_regmap, PM88X_RTC_ALARM_CTRL1, &val);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
+ return ret;
+ }
+ chip->rtc_wakeup = !!(val & PM88X_ALARM_WAKEUP);
+
+ parse_powerup_down_log(chip);
+
+ return 0;
+}
+
+int pm88x_init_pages(struct pm88x_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ const struct regmap_config *base_regmap_config;
+ const struct regmap_config *power_regmap_config;
+ const struct regmap_config *gpadc_regmap_config;
+ const struct regmap_config *battery_regmap_config;
+ const struct regmap_config *test_regmap_config;
+ int ret = 0;
+
+ if (!chip || !chip->power_page_addr ||
+ !chip->gpadc_page_addr || !chip->battery_page_addr)
+ return -ENODEV;
+
+ chip->type = pm88x_of_get_type(&client->dev);
+ switch (chip->type) {
+ case PM886:
+ base_regmap_config = &pm886_base_i2c_regmap;
+ power_regmap_config = &pm886_power_i2c_regmap;
+ gpadc_regmap_config = &pm886_gpadc_i2c_regmap;
+ battery_regmap_config = &pm886_battery_i2c_regmap;
+ test_regmap_config = &pm886_test_i2c_regmap;
+ break;
+ case PM880:
+ base_regmap_config = &pm880_base_i2c_regmap;
+ power_regmap_config = &pm880_power_i2c_regmap;
+ gpadc_regmap_config = &pm880_gpadc_i2c_regmap;
+ battery_regmap_config = &pm880_battery_i2c_regmap;
+ test_regmap_config = &pm880_test_i2c_regmap;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /* base page */
+ chip->base_regmap = devm_regmap_init_i2c(client, base_regmap_config);
+ if (IS_ERR(chip->base_regmap)) {
+ dev_err(chip->dev, "Failed to init base_regmap: %d\n", ret);
+ ret = PTR_ERR(chip->base_regmap);
+ goto out;
+ }
+
+ /* power page */
+ chip->power_page = i2c_new_dummy(client->adapter, chip->power_page_addr);
+ if (!chip->power_page) {
+ dev_err(chip->dev, "Failed to new power_page: %d\n", ret);
+ ret = -ENODEV;
+ goto out;
+ }
+ chip->power_regmap = devm_regmap_init_i2c(chip->power_page,
+ power_regmap_config);
+ if (IS_ERR(chip->power_regmap)) {
+ dev_err(chip->dev, "Failed to init power_regmap: %d\n", ret);
+ ret = PTR_ERR(chip->power_regmap);
+ goto out;
+ }
+
+ /* gpadc page */
+ chip->gpadc_page = i2c_new_dummy(client->adapter, chip->gpadc_page_addr);
+ if (!chip->gpadc_page) {
+ dev_err(chip->dev, "Failed to new gpadc_page: %d\n", ret);
+ ret = -ENODEV;
+ goto out;
+ }
+ chip->gpadc_regmap = devm_regmap_init_i2c(chip->gpadc_page,
+ gpadc_regmap_config);
+ if (IS_ERR(chip->gpadc_regmap)) {
+ dev_err(chip->dev, "Failed to init gpadc_regmap: %d\n", ret);
+ ret = PTR_ERR(chip->gpadc_regmap);
+ goto out;
+ }
+
+ /* battery page */
+ chip->battery_page = i2c_new_dummy(client->adapter, chip->battery_page_addr);
+ if (!chip->battery_page) {
+ dev_err(chip->dev, "Failed to new gpadc_page: %d\n", ret);
+ ret = -ENODEV;
+ goto out;
+ }
+ chip->battery_regmap = devm_regmap_init_i2c(chip->battery_page,
+ battery_regmap_config);
+ if (IS_ERR(chip->battery_regmap)) {
+ dev_err(chip->dev, "Failed to init battery_regmap: %d\n", ret);
+ ret = PTR_ERR(chip->battery_regmap);
+ goto out;
+ }
+
+ /* test page */
+ chip->test_page = i2c_new_dummy(client->adapter, chip->test_page_addr);
+ if (!chip->test_page) {
+ dev_err(chip->dev, "Failed to new test_page: %d\n", ret);
+ ret = -ENODEV;
+ goto out;
+ }
+ chip->test_regmap = devm_regmap_init_i2c(chip->test_page,
+ test_regmap_config);
+ if (IS_ERR(chip->test_regmap)) {
+ dev_err(chip->dev, "Failed to init test_regmap: %d\n", ret);
+ ret = PTR_ERR(chip->test_regmap);
+ goto out;
+ }
+
+ chip->type = pm88x_of_get_type(&client->dev);
+ switch (chip->type) {
+ case PM886:
+ /* ldo page */
+ chip->ldo_page = chip->power_page;
+ chip->ldo_regmap = chip->power_regmap;
+ /* buck page */
+ chip->buck_regmap = chip->power_regmap;
+ break;
+ case PM880:
+ /* ldo page */
+ chip->ldo_page = chip->power_page;
+ chip->ldo_regmap = chip->power_regmap;
+
+ /* buck page */
+ chip->buck_page = i2c_new_dummy(client->adapter,
+ chip->buck_page_addr);
+ if (!chip->buck_page) {
+ dev_err(chip->dev, "Failed to new buck_page: %d\n", ret);
+ ret = -ENODEV;
+ goto out;
+ }
+ chip->buck_regmap = devm_regmap_init_i2c(chip->buck_page,
+ power_regmap_config);
+ if (IS_ERR(chip->buck_regmap)) {
+ dev_err(chip->dev, "Failed to init buck_regmap: %d\n", ret);
+ ret = PTR_ERR(chip->buck_regmap);
+ goto out;
+ }
+
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+out:
+ return ret;
+}
+
+void pm800_exit_pages(struct pm88x_chip *chip)
+{
+ if (!chip)
+ return;
+
+ if (chip->ldo_page)
+ i2c_unregister_device(chip->ldo_page);
+ if (chip->gpadc_page)
+ i2c_unregister_device(chip->gpadc_page);
+ if (chip->test_page)
+ i2c_unregister_device(chip->test_page);
+ /* no need to unregister ldo_page */
+ switch (chip->type) {
+ case PM886:
+ break;
+ case PM880:
+ if (chip->buck_page)
+ i2c_unregister_device(chip->buck_page);
+ break;
+ default:
+ break;
+ }
+}
+
+int pm88x_init_subdev(struct pm88x_chip *chip)
+{
+ int ret;
+ if (!chip)
+ return -EINVAL;
+
+ ret = mfd_add_devices(chip->dev, 0, common_cell_devs,
+ ARRAY_SIZE(common_cell_devs), NULL, 0,
+ regmap_irq_get_domain(chip->irq_data));
+ if (ret < 0)
+ return ret;
+
+ switch (chip->type) {
+ case PM886:
+ ret = mfd_add_devices(chip->dev, 0, pm886_cell_info.cells,
+ pm886_cell_info.cell_nr, NULL, 0,
+ regmap_irq_get_domain(chip->irq_data));
+ break;
+ case PM880:
+ ret = mfd_add_devices(chip->dev, 0, pm880_cell_info.cells,
+ pm880_cell_info.cell_nr, NULL, 0,
+ regmap_irq_get_domain(chip->irq_data));
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int (*apply_to_chip)(struct pm88x_chip *chip);
+/* PMIC chip itself related */
+int pm88x_apply_patch(struct pm88x_chip *chip)
+{
+ if (!chip || !chip->client)
+ return -EINVAL;
+
+ chip->type = pm88x_of_get_type(&chip->client->dev);
+ switch (chip->type) {
+ case PM886:
+ apply_to_chip = pm886_apply_patch;
+ break;
+ case PM880:
+ apply_to_chip = pm880_apply_patch;
+ break;
+ default:
+ break;
+ }
+ if (apply_to_chip)
+ apply_to_chip(chip);
+ return 0;
+}
+
+int pm88x_stepping_fixup(struct pm88x_chip *chip)
+{
+ if (!chip || !chip->client)
+ return -EINVAL;
+
+ chip->type = pm88x_of_get_type(&chip->client->dev);
+ switch (chip->type) {
+ case PM886:
+ case PM880:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int pm88x_apply_board_fixup(struct pm88x_chip *chip, struct device_node *np)
+{
+ /* add board design specific setting, parsed via device tree */
+ return 0;
+}
+
+long pm88x_of_get_type(struct device *dev)
+{
+ const struct of_device_id *id = of_match_device(pm88x_of_match, dev);
+
+ if (id)
+ return (long)id->data;
+ else
+ return 0;
+}
+
+void pm88x_dev_exit(struct pm88x_chip *chip)
+{
+ mfd_remove_devices(chip->dev);
+ pm88x_irq_exit(chip);
+}
+
+void pm88x_power_off(void)
+{
+ pr_info("powers off the system.");
+ /* TODO: implement later */
+
+ for (;;)
+ cpu_relax();
+}
+
+int pm88x_reboot_notifier_callback(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ struct pm88x_chip *chip =
+ container_of(this, struct pm88x_chip, reboot_notifier);
+
+ switch (code) {
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ dev_info(chip->dev, "system is down.\n");
+ break;
+ case SYS_RESTART:
+ default:
+ dev_info(chip->dev, "system will reboot.\n");
+ break;
+ }
+
+ return 0;
+}
diff --git a/drivers/mfd/88pm88x-i2c.c b/drivers/mfd/88pm88x-i2c.c
new file mode 100644
index 0000000..36842ed
--- /dev/null
+++ b/drivers/mfd/88pm88x-i2c.c
@@ -0,0 +1,167 @@
+/*
+ * 88pm88x-i2c.c -- 88pm88x i2c bus interface
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm886.h>
+#include <linux/mfd/88pm880.h>
+#include <linux/mfd/88pm88x.h>
+
+static int pm88x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pm88x_chip *chip;
+ struct device_node *node = client->dev.of_node;
+ int ret = 0;
+
+ chip = pm88x_init_chip(client);
+ if (IS_ERR(chip)) {
+ ret = PTR_ERR(chip);
+ dev_err(chip->dev, "initialize 88pm88x chip fails!\n");
+ goto err;
+ }
+
+ ret = pm88x_parse_dt(node, chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "parse dt fails!\n");
+ goto err;
+ }
+
+ ret = pm88x_init_pages(chip);
+ if (ret) {
+ dev_err(chip->dev, "initialize 88pm88x pages fails!\n");
+ goto err;
+ }
+
+ ret = pm88x_post_init_chip(chip);
+ if (ret) {
+ dev_err(chip->dev, "post initialize 88pm88x fails!\n");
+ goto err;
+ }
+
+ ret = pm88x_irq_init(chip);
+ if (ret) {
+ dev_err(chip->dev, "initialize 88pm88x interrupt fails!\n");
+ goto err_init_irq;
+ }
+
+ ret = pm88x_init_subdev(chip);
+ if (ret) {
+ dev_err(chip->dev, "initialize 88pm88x sub-device fails\n");
+ goto err_init_subdev;
+ }
+
+ /* patch for PMIC chip itself */
+ ret = pm88x_apply_patch(chip);
+ if (ret) {
+ dev_err(chip->dev, "apply 88pm88x register patch fails\n");
+ goto err_apply_patch;
+ }
+
+ /* fixup according PMIC stepping */
+ ret = pm88x_stepping_fixup(chip);
+ if (ret) {
+ dev_err(chip->dev, "fixup according to chip stepping\n");
+ goto err_apply_patch;
+ }
+
+ /* patch for board configuration */
+ ret = pm88x_apply_board_fixup(chip, node);
+ if (ret) {
+ dev_err(chip->dev, "apply 88pm88x register for board fails\n");
+ goto err_apply_patch;
+ }
+
+ pm_power_off = pm88x_power_off;
+
+ chip->reboot_notifier.notifier_call = pm88x_reboot_notifier_callback;
+ register_reboot_notifier(&(chip->reboot_notifier));
+
+ return 0;
+
+err_apply_patch:
+ mfd_remove_devices(chip->dev);
+err_init_subdev:
+ regmap_del_irq_chip(chip->irq, chip->irq_data);
+err_init_irq:
+ pm800_exit_pages(chip);
+err:
+ return ret;
+}
+
+static int pm88x_i2c_remove(struct i2c_client *i2c)
+{
+ struct pm88x_chip *chip = dev_get_drvdata(&i2c->dev);
+ pm88x_dev_exit(chip);
+ return 0;
+}
+
+static const struct i2c_device_id pm88x_i2c_id[] = {
+ { "88pm886", PM886 },
+ { "88pm880", PM880 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pm88x_i2c_id);
+
+static int pm88x_i2c_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int pm88x_i2c_resume(struct device *dev)
+{
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm88x_pm_ops, pm88x_i2c_suspend, pm88x_i2c_resume);
+
+static struct i2c_driver pm88x_i2c_driver = {
+ .driver = {
+ .name = "88pm88x",
+ .owner = THIS_MODULE,
+ .pm = &pm88x_pm_ops,
+ .of_match_table = of_match_ptr(pm88x_of_match),
+ },
+ .probe = pm88x_i2c_probe,
+ .remove = pm88x_i2c_remove,
+ .id_table = pm88x_i2c_id,
+};
+
+static int __init pm88x_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&pm88x_i2c_driver);
+ if (ret != 0) {
+ pr_err("88pm88x I2C registration failed %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+subsys_initcall(pm88x_i2c_init);
+
+static void __exit pm88x_i2c_exit(void)
+{
+ i2c_del_driver(&pm88x_i2c_driver);
+}
+module_exit(pm88x_i2c_exit);
+
+MODULE_DESCRIPTION("88pm88x I2C bus interface");
+MODULE_AUTHOR("Yi Zhang<[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/88pm88x-irq.c b/drivers/mfd/88pm88x-irq.c
new file mode 100644
index 0000000..0126df0
--- /dev/null
+++ b/drivers/mfd/88pm88x-irq.c
@@ -0,0 +1,171 @@
+/*
+ * 88pm886 interrupt support
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/mfd/88pm88x.h>
+#include <linux/mfd/88pm886.h>
+#include <linux/mfd/88pm880.h>
+
+/* interrupt status registers */
+#define PM88X_INT_STATUS1 (0x05)
+
+#define PM88X_INT_ENA_1 (0x0a)
+#define PM88X_ONKEY_INT_ENA1 (1 << 0)
+#define PM88X_EXTON_INT_ENA1 (1 << 1)
+#define PM88X_CHG_INT_ENA1 (1 << 2)
+#define PM88X_BAT_INT_ENA1 (1 << 3)
+#define PM88X_RTC_INT_ENA1 (1 << 4)
+#define PM88X_CLASSD_INT_ENA1 (1 << 5)
+#define PM88X_XO_INT_ENA1 (1 << 6)
+#define PM88X_GPIO_INT_ENA1 (1 << 7)
+
+#define PM88X_INT_ENA_2 (0x0b)
+#define PM88X_VBAT_INT_ENA2 (1 << 0)
+#define PM88X_RSVED1_INT_ENA2 (1 << 1)
+#define PM88X_VBUS_INT_ENA2 (1 << 2)
+#define PM88X_ITEMP_INT_ENA2 (1 << 3)
+#define PM88X_BUCK_PGOOD_INT_ENA2 (1 << 4)
+#define PM88X_LDO_PGOOD_INT_ENA2 (1 << 5)
+#define PM88X_RSVED6_INT_ENA2 (1 << 6)
+#define PM88X_RSVED7_INT_ENA2 (1 << 7)
+
+#define PM88X_INT_ENA_3 (0x0c)
+#define PM88X_GPADC0_INT_ENA3 (1 << 0)
+#define PM88X_GPADC1_INT_ENA3 (1 << 1)
+#define PM88X_GPADC2_INT_ENA3 (1 << 2)
+#define PM88X_GPADC3_INT_ENA3 (1 << 3)
+#define PM88X_MIC_INT_ENA3 (1 << 4)
+#define PM88X_HS_INT_ENA3 (1 << 5)
+#define PM88X_GND_INT_ENA3 (1 << 6)
+#define PM88X_RSVED7_INT_ENA3 (1 << 7)
+
+#define PM88X_INT_ENA_4 (0x0d)
+#define PM88X_CHG_FAIL_INT_ENA4 (1 << 0)
+#define PM88X_CHG_DONE_INT_ENA4 (1 << 1)
+#define PM88X_RSVED2_INT_ENA4 (1 << 2)
+#define PM88X_OTG_FAIL_INT_ENA4 (1 << 3)
+#define PM88X_RSVED4_INT_ENA4 (1 << 4)
+#define PM88X_CHG_ILIM_INT_ENA4 (1 << 5)
+#define PM88X_CC_INT_ENA4 (1 << 6)
+#define PM88X_RSVED7_INT_ENA4 (1 << 7)
+
+#define PM88X_MISC_CONFIG2 (0x15)
+#define PM88X_INV_INT (1 << 0)
+#define PM88X_INT_CLEAR (1 << 1)
+#define PM88X_INT_RC (0 << 1)
+#define PM88X_INT_WC (1 << 1)
+#define PM88X_INT_MASK_MODE (1 << 2)
+
+static const struct regmap_irq pm88x_irqs[] = {
+ /* INT0 */
+ [PM88X_IRQ_ONKEY] = {.reg_offset = 0, .mask = PM88X_ONKEY_INT_ENA1,},
+ [PM88X_IRQ_EXTON] = {.reg_offset = 0, .mask = PM88X_EXTON_INT_ENA1,},
+ [PM88X_IRQ_CHG_GOOD] = {.reg_offset = 0, .mask = PM88X_CHG_INT_ENA1,},
+ [PM88X_IRQ_BAT_DET] = {.reg_offset = 0, .mask = PM88X_BAT_INT_ENA1,},
+ [PM88X_IRQ_RTC] = {.reg_offset = 0, .mask = PM88X_RTC_INT_ENA1,},
+ [PM88X_IRQ_CLASSD] = { .reg_offset = 0, .mask = PM88X_CLASSD_INT_ENA1,},
+ [PM88X_IRQ_XO] = {.reg_offset = 0, .mask = PM88X_XO_INT_ENA1,},
+ [PM88X_IRQ_GPIO] = {.reg_offset = 0, .mask = PM88X_GPIO_INT_ENA1,},
+
+ /* INT1 */
+ [PM88X_IRQ_VBAT] = {.reg_offset = 1, .mask = PM88X_VBAT_INT_ENA2,},
+ [PM88X_IRQ_VBUS] = {.reg_offset = 1, .mask = PM88X_VBUS_INT_ENA2,},
+ [PM88X_IRQ_ITEMP] = {.reg_offset = 1, .mask = PM88X_ITEMP_INT_ENA2,},
+ [PM88X_IRQ_BUCK_PGOOD] = {
+ .reg_offset = 1,
+ .mask = PM88X_BUCK_PGOOD_INT_ENA2,
+ },
+ [PM88X_IRQ_LDO_PGOOD] = {
+ .reg_offset = 1,
+ .mask = PM88X_LDO_PGOOD_INT_ENA2,
+ },
+ /* INT2 */
+ [PM88X_IRQ_GPADC0] = {.reg_offset = 2, .mask = PM88X_GPADC0_INT_ENA3,},
+ [PM88X_IRQ_GPADC1] = {.reg_offset = 2, .mask = PM88X_GPADC1_INT_ENA3,},
+ [PM88X_IRQ_GPADC2] = {.reg_offset = 2, .mask = PM88X_GPADC2_INT_ENA3,},
+ [PM88X_IRQ_GPADC3] = {.reg_offset = 2, .mask = PM88X_GPADC3_INT_ENA3,},
+ [PM88X_IRQ_MIC_DET] = {.reg_offset = 2, .mask = PM88X_MIC_INT_ENA3,},
+ [PM88X_IRQ_HS_DET] = {.reg_offset = 2, .mask = PM88X_HS_INT_ENA3,},
+ [PM88X_IRQ_GND_DET] = {.reg_offset = 2, .mask = PM88X_GND_INT_ENA3,},
+
+ /* INT3 */
+ [PM88X_IRQ_CHG_FAIL] = {
+ .reg_offset = 3,
+ .mask = PM88X_CHG_FAIL_INT_ENA4,
+ },
+ [PM88X_IRQ_CHG_DONE] = {
+ .reg_offset = 3,
+ .mask = PM88X_CHG_DONE_INT_ENA4,
+ },
+ [PM88X_IRQ_OTG_FAIL] = {
+ .reg_offset = 3,
+ .mask = PM88X_OTG_FAIL_INT_ENA4,
+ },
+ [PM88X_IRQ_CHG_ILIM] = {
+ .reg_offset = 3,
+ .mask = PM88X_CHG_ILIM_INT_ENA4,
+ },
+ [PM88X_IRQ_CC] = {.reg_offset = 3, .mask = PM88X_CC_INT_ENA4,},
+};
+
+struct regmap_irq_chip pm88x_irq_chip = {
+ .name = "88pm88x",
+ .irqs = pm88x_irqs,
+ .num_irqs = ARRAY_SIZE(pm88x_irqs),
+
+ .num_regs = 4,
+ .status_base = PM88X_INT_STATUS1,
+ .mask_base = PM88X_INT_ENA_1,
+ .ack_base = PM88X_INT_STATUS1,
+ .mask_invert = 1,
+};
+
+int pm88x_irq_init(struct pm88x_chip *chip)
+{
+ int mask, data, ret;
+ struct regmap *map;
+
+ if (!chip || !chip->base_regmap || !chip->irq) {
+ pr_err("cannot initialize interrupt!\n");
+ return -EINVAL;
+ }
+ map = chip->base_regmap;
+
+ /*
+ * irq_mode defines the way of clearing interrupt.
+ * it's read-clear by default.
+ */
+ mask = PM88X_INV_INT | PM88X_INT_CLEAR | PM88X_INT_MASK_MODE;
+ data = (chip->irq_mode) ? PM88X_INT_WC : PM88X_INT_RC;
+ ret = regmap_update_bits(map, PM88X_MISC_CONFIG2, mask, data);
+ if (ret < 0) {
+ dev_err(chip->dev, "cannot set interrupt mode!\n");
+ return ret;
+ }
+
+ ret = regmap_add_irq_chip(map, chip->irq, IRQF_ONESHOT, -1,
+ &pm88x_irq_chip, &chip->irq_data);
+ return ret;
+}
+
+int pm88x_irq_exit(struct pm88x_chip *chip)
+{
+ regmap_del_irq_chip(chip->irq, chip->irq_data);
+ return 0;
+}
diff --git a/drivers/mfd/88pm88x.h b/drivers/mfd/88pm88x.h
new file mode 100644
index 0000000..a85a486
--- /dev/null
+++ b/drivers/mfd/88pm88x.h
@@ -0,0 +1,51 @@
+#ifndef _MFD_88PM88X_H
+#define _MFD_88PM88X_H
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+
+struct pmic_cell_info {
+ const struct mfd_cell *cells;
+ int cell_nr;
+};
+
+#define CELL_IRQ_RESOURCE(_name, _irq) { \
+ .name = _name, \
+ .start = _irq, .end = _irq, \
+ .flags = IORESOURCE_IRQ, \
+ }
+#define CELL_DEV(_name, _r, _compatible, _id) { \
+ .name = _name, \
+ .of_compatible = _compatible, \
+ .num_resources = ARRAY_SIZE(_r), \
+ .resources = _r, \
+ .id = _id, \
+ }
+
+/* 88pm886 */
+extern const struct regmap_config pm886_base_i2c_regmap;
+extern const struct regmap_config pm886_power_i2c_regmap;
+extern const struct regmap_config pm886_gpadc_i2c_regmap;
+extern const struct regmap_config pm886_battery_i2c_regmap;
+extern const struct regmap_config pm886_test_i2c_regmap;
+
+extern const struct mfd_cell pm886_cell_devs[];
+extern struct pmic_cell_info pm886_cell_info;
+
+int pm886_apply_patch(struct pm88x_chip *chip);
+
+/* 88pm880 */
+extern const struct regmap_config pm880_base_i2c_regmap;
+extern const struct regmap_config pm880_power_i2c_regmap;
+extern const struct regmap_config pm880_gpadc_i2c_regmap;
+extern const struct regmap_config pm880_battery_i2c_regmap;
+extern const struct regmap_config pm880_test_i2c_regmap;
+
+extern const struct mfd_cell pm880_cell_devs[];
+extern struct pmic_cell_info pm880_cell_info;
+
+int pm880_apply_patch(struct pm88x_chip *chip);
+
+#endif
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d5ad04d..bdebca9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -390,6 +390,18 @@ config MFD_KEMPLD
This driver can also be built as a module. If so, the module
will be called kempld-core.

+config MFD_88PM88X
+ tristate "Marvell 88PM886/880 PMIC"
+ depends on I2C=y
+ select REGMAP_I2C
+ select MFD_CORE
+ help
+ This supports for Marvell 88PM88X Series Power Management IC:
+ 88pm886 and 88pm880;
+ This includes the I2C driver, the interrupt resource distribution
+ and the core APIs, for individual sub-device as voltage regulators,
+ RTC, charger, fuelgauge, etc, select under the corresponding menus.
+
config MFD_88PM800
tristate "Marvell 88PM800"
depends on I2C=y
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 0e5cfeb..365d1fa 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -6,6 +6,9 @@
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
+88pm88x-objs := 88pm88x-core.o 88pm88x-i2c.o 88pm88x-irq.o 88pm886-table.o 88pm880-table.o
+obj-$(CONFIG_MFD_88PM88X) += 88pm88x.o
+
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
diff --git a/include/linux/mfd/88pm880-reg.h b/include/linux/mfd/88pm880-reg.h
new file mode 100644
index 0000000..fdf2315
--- /dev/null
+++ b/include/linux/mfd/88pm880-reg.h
@@ -0,0 +1,98 @@
+/*
+ * Marvell 88PM880 registers
+ *
+ * Copyright (C) 2014 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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_88PM880_REG_H
+#define __LINUX_MFD_88PM880_REG_H
+
+#define PM880_BUCK1_VOUT (0x28)
+
+#define PM880_BUCK1A_VOUT (0x28) /* voltage 0 */
+#define PM880_BUCK1A_1_VOUT (0x29)
+#define PM880_BUCK1A_2_VOUT (0x2a)
+#define PM880_BUCK1A_3_VOUT (0x2b)
+#define PM880_BUCK1A_4_VOUT (0x2c)
+#define PM880_BUCK1A_5_VOUT (0x2d)
+#define PM880_BUCK1A_6_VOUT (0x2e)
+#define PM880_BUCK1A_7_VOUT (0x2f)
+#define PM880_BUCK1A_8_VOUT (0x30)
+#define PM880_BUCK1A_9_VOUT (0x31)
+#define PM880_BUCK1A_10_VOUT (0x32)
+#define PM880_BUCK1A_11_VOUT (0x33)
+#define PM880_BUCK1A_12_VOUT (0x34)
+#define PM880_BUCK1A_13_VOUT (0x35)
+#define PM880_BUCK1A_14_VOUT (0x36)
+#define PM880_BUCK1A_15_VOUT (0x37)
+
+#define PM880_BUCK1B_VOUT (0x40)
+#define PM880_BUCK1B_1_VOUT (0x41)
+#define PM880_BUCK1B_2_VOUT (0x42)
+#define PM880_BUCK1B_3_VOUT (0x43)
+#define PM880_BUCK1B_4_VOUT (0x44)
+#define PM880_BUCK1B_5_VOUT (0x45)
+#define PM880_BUCK1B_6_VOUT (0x46)
+#define PM880_BUCK1B_7_VOUT (0x47)
+#define PM880_BUCK1B_8_VOUT (0x48)
+#define PM880_BUCK1B_9_VOUT (0x49)
+#define PM880_BUCK1B_10_VOUT (0x4a)
+#define PM880_BUCK1B_11_VOUT (0x4b)
+#define PM880_BUCK1B_12_VOUT (0x4c)
+#define PM880_BUCK1B_13_VOUT (0x4d)
+#define PM880_BUCK1B_14_VOUT (0x4e)
+#define PM880_BUCK1B_15_VOUT (0x4f)
+
+/* buck7 has dvc function */
+#define PM880_BUCK7_VOUT (0xb8) /* voltage 0 */
+#define PM880_BUCK7_1_VOUT (0xb9)
+#define PM880_BUCK7_2_VOUT (0xba)
+#define PM880_BUCK7_3_VOUT (0xbb)
+
+/*
+ * buck sleep mode control registers:
+ * 00-disable,
+ * 01/10-sleep voltage,
+ * 11-active voltage
+ */
+#define PM880_BUCK1A_SLP_CTRL (0x27)
+#define PM880_BUCK1B_SLP_CTRL (0x3c)
+#define PM880_BUCK2_SLP_CTRL (0x54)
+#define PM880_BUCK3_SLP_CTRL (0x6c)
+/* TODO: there are 7 controls bit for buck4~7 */
+#define PM880_BUCK4_SLP_CTRL (0x84)
+#define PM880_BUCK5_SLP_CTRL (0x94)
+#define PM880_BUCK6_SLP_CTRL (0xa4)
+#define PM880_BUCK7_SLP_CTRL (0xb4)
+
+/*
+ * ldo sleep mode control registers:
+ * 00-disable,
+ * 01/10-sleep voltage,
+ * 11-active voltage
+ */
+#define PM880_LDO1_SLP_CTRL (0x21)
+#define PM880_LDO2_SLP_CTRL (0x27)
+#define PM880_LDO3_SLP_CTRL (0x2d)
+#define PM880_LDO4_SLP_CTRL (0x33)
+#define PM880_LDO5_SLP_CTRL (0x39)
+#define PM880_LDO6_SLP_CTRL (0x3f)
+#define PM880_LDO7_SLP_CTRL (0x45)
+#define PM880_LDO8_SLP_CTRL (0x4b)
+#define PM880_LDO9_SLP_CTRL (0x51)
+#define PM880_LDO10_SLP_CTRL (0x57)
+#define PM880_LDO11_SLP_CTRL (0x5d)
+#define PM880_LDO12_SLP_CTRL (0x63)
+#define PM880_LDO13_SLP_CTRL (0x69)
+#define PM880_LDO14_SLP_CTRL (0x6f)
+#define PM880_LDO15_SLP_CTRL (0x75)
+#define PM880_LDO16_SLP_CTRL (0x7b)
+#define PM880_LDO17_SLP_CTRL (0x81)
+#define PM880_LDO18_SLP_CTRL (0x87)
+
+#endif /*__LINUX_MFD_88PM880_REG_H */
diff --git a/include/linux/mfd/88pm880.h b/include/linux/mfd/88pm880.h
new file mode 100644
index 0000000..94b9e063
--- /dev/null
+++ b/include/linux/mfd/88pm880.h
@@ -0,0 +1,59 @@
+/*
+ * Marvell 88PM880 Interface
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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.
+ *
+ * 88pm880 specific configuration: at present it's regulators and dvc part
+ */
+
+#ifndef __LINUX_MFD_88PM880_H
+#define __LINUX_MFD_88PM880_H
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/atomic.h>
+#include <linux/reboot.h>
+#include "88pm880-reg.h"
+
+enum {
+ PM880_ID_BUCK1A = 0,
+ PM880_ID_BUCK2,
+ PM880_ID_BUCK3,
+ PM880_ID_BUCK4,
+ PM880_ID_BUCK5,
+ PM880_ID_BUCK6,
+ PM880_ID_BUCK7,
+
+ PM880_ID_BUCK_MAX = 7,
+};
+
+enum {
+ PM880_ID_LDO1 = 0,
+ PM880_ID_LDO2,
+ PM880_ID_LDO3,
+ PM880_ID_LDO4,
+ PM880_ID_LDO5,
+ PM880_ID_LDO6,
+ PM880_ID_LDO7,
+ PM880_ID_LDO8,
+ PM880_ID_LDO9,
+ PM880_ID_LDO10,
+ PM880_ID_LDO11,
+ PM880_ID_LDO12,
+ PM880_ID_LDO13,
+ PM880_ID_LDO14 = 13,
+ PM880_ID_LDO15,
+ PM880_ID_LDO16 = 15,
+
+ PM880_ID_LDO17 = 16,
+ PM880_ID_LDO18 = 17,
+
+ PM880_ID_LDO_MAX = 18,
+};
+#endif /* __LINUX_MFD_88PM880_H */
diff --git a/include/linux/mfd/88pm886-reg.h b/include/linux/mfd/88pm886-reg.h
new file mode 100644
index 0000000..38a7ecd
--- /dev/null
+++ b/include/linux/mfd/88pm886-reg.h
@@ -0,0 +1,59 @@
+/*
+ * Marvell 88PM886 registers
+ *
+ * Copyright (C) 2014 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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_88PM886_REG_H
+#define __LINUX_MFD_88PM886_REG_H
+
+#define PM886_BUCK1_VOUT (0xa5)
+#define PM886_BUCK1_1_VOUT (0xa6)
+#define PM886_BUCK1_2_VOUT (0xa7)
+#define PM886_BUCK1_3_VOUT (0xa8)
+#define PM886_BUCK1_4_VOUT (0x9a)
+#define PM886_BUCK1_5_VOUT (0x9b)
+#define PM886_BUCK1_6_VOUT (0x9c)
+#define PM886_BUCK1_7_VOUT (0x9d)
+
+/*
+ * buck sleep mode control registers:
+ * 00-disable,
+ * 01/10-sleep voltage,
+ * 11-active voltage
+ */
+#define PM886_BUCK1_SLP_CTRL (0xa2)
+#define PM886_BUCK2_SLP_CTRL (0xb0)
+#define PM886_BUCK3_SLP_CTRL (0xbe)
+#define PM886_BUCK4_SLP_CTRL (0xcc)
+#define PM886_BUCK5_SLP_CTRL (0xda)
+
+/*
+ * ldo sleep mode control registers:
+ * 00-disable,
+ * 01/10-sleep voltage,
+ * 11-active voltage
+ */
+#define PM886_LDO1_SLP_CTRL (0x21)
+#define PM886_LDO2_SLP_CTRL (0x27)
+#define PM886_LDO3_SLP_CTRL (0x2d)
+#define PM886_LDO4_SLP_CTRL (0x33)
+#define PM886_LDO5_SLP_CTRL (0x39)
+#define PM886_LDO6_SLP_CTRL (0x3f)
+#define PM886_LDO7_SLP_CTRL (0x45)
+#define PM886_LDO8_SLP_CTRL (0x4b)
+#define PM886_LDO9_SLP_CTRL (0x51)
+#define PM886_LDO10_SLP_CTRL (0x57)
+#define PM886_LDO11_SLP_CTRL (0x5d)
+#define PM886_LDO12_SLP_CTRL (0x63)
+#define PM886_LDO13_SLP_CTRL (0x69)
+#define PM886_LDO14_SLP_CTRL (0x6f)
+#define PM886_LDO15_SLP_CTRL (0x75)
+#define PM886_LDO16_SLP_CTRL (0x7b)
+
+#endif
diff --git a/include/linux/mfd/88pm886.h b/include/linux/mfd/88pm886.h
new file mode 100644
index 0000000..9390406
--- /dev/null
+++ b/include/linux/mfd/88pm886.h
@@ -0,0 +1,55 @@
+/*
+ * Marvell 88PM886 Interface
+ *
+ * Copyright (C) 2015 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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.
+ *
+ * 88pm886 specific configuration: at present it's regulators and dvc part
+ */
+
+#ifndef __LINUX_MFD_88PM886_H
+#define __LINUX_MFD_88PM886_H
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/atomic.h>
+#include <linux/reboot.h>
+#include "88pm886-reg.h"
+
+enum {
+ PM886_ID_BUCK1 = 0,
+ PM886_ID_BUCK2,
+ PM886_ID_BUCK3,
+ PM886_ID_BUCK4,
+ PM886_ID_BUCK5,
+
+ PM886_ID_BUCK_MAX = 5,
+};
+
+enum {
+ PM886_ID_LDO1 = 0,
+ PM886_ID_LDO2,
+ PM886_ID_LDO3,
+ PM886_ID_LDO4,
+ PM886_ID_LDO5,
+ PM886_ID_LDO6,
+ PM886_ID_LDO7,
+ PM886_ID_LDO8,
+ PM886_ID_LDO9,
+ PM886_ID_LDO10,
+ PM886_ID_LDO11,
+ PM886_ID_LDO12,
+ PM886_ID_LDO13,
+ PM886_ID_LDO14,
+ PM886_ID_LDO15,
+ PM886_ID_LDO16 = 15,
+
+ PM886_ID_LDO_MAX = 16,
+};
+
+#endif /* __LINUX_MFD_88PM886_H */
diff --git a/include/linux/mfd/88pm88x-reg.h b/include/linux/mfd/88pm88x-reg.h
new file mode 100644
index 0000000..d767b31
--- /dev/null
+++ b/include/linux/mfd/88pm88x-reg.h
@@ -0,0 +1,118 @@
+/*
+ * Marvell 88PM88X registers
+ *
+ * Copyright (C) 2014 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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_88PM88X_REG_H
+#define __LINUX_MFD_88PM88X_REG_H
+/*
+ * This file is just used for the common registers,
+ * which are shared by sub-clients
+ */
+
+/*--base page:--------------------------------------------------------------*/
+#define PM88X_ID_REG (0x0)
+
+#define PM88X_STATUS1 (0x1)
+#define PM88X_CHG_DET (1 << 2)
+#define PM88X_BAT_DET (1 << 3)
+
+#define PM88X_MISC_CONFIG1 (0x14)
+#define PM88X_LONKEY_RST (1 << 3)
+
+#define PM88X_WDOG (0x1d)
+
+#define PM88X_LOWPOWER2 (0x21)
+#define PM88X_LOWPOWER4 (0x23)
+
+/* clk control register */
+#define PM88X_CLK_CTRL1 (0x25)
+
+/* gpio */
+#define PM88X_GPIO_CTRL1 (0x30)
+#define PM88X_GPIO0_VAL_MSK (0x1 << 0)
+#define PM88X_GPIO0_MODE_MSK (0x7 << 1)
+#define PM88X_GPIO1_VAL_MSK (0x1 << 4)
+#define PM88X_GPIO1_MODE_MSK (0x7 << 5)
+#define PM88X_GPIO1_SET_DVC (0x2 << 5)
+
+#define PM88X_GPIO_CTRL2 (0x31)
+#define PM88X_GPIO2_VAL_MSK (0x1 << 0)
+#define PM88X_GPIO2_MODE_MSK (0x7 << 1)
+
+#define PM88X_GPIO_CTRL3 (0x32)
+
+#define PM88X_GPIO_CTRL4 (0x33)
+#define PM88X_GPIO5V_1_VAL_MSK (0x1 << 0)
+#define PM88X_GPIO5V_1_MODE_MSK (0x7 << 1)
+#define PM88X_GPIO5V_2_VAL_MSK (0x1 << 4)
+#define PM88X_GPIO5V_2_MODE_MSK (0x7 << 5)
+
+#define PM88X_BK_OSC_CTRL1 (0x50)
+#define PM88X_BK_OSC_CTRL3 (0x52)
+
+#define PM88X_RTC_ALARM_CTRL1 (0xd0)
+#define PM88X_ALARM_WAKEUP (1 << 4)
+#define PM88X_USE_XO (1 << 7)
+
+#define PM88X_AON_CTRL2 (0xe2)
+#define PM88X_AON_CTRL3 (0xe3)
+#define PM88X_AON_CTRL4 (0xe4)
+#define PM88X_AON_CTRL7 (0xe7)
+
+/* 0xea, 0xeb, 0xec, 0xed are reserved by RTC */
+#define PM88X_RTC_SPARE5 (0xee)
+#define PM88X_RTC_SPARE6 (0xef)
+/*-------------------------------------------------------------------------*/
+
+/*--power page:------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+/*--gpadc page:------------------------------------------------------------*/
+
+#define PM88X_GPADC_CONFIG1 (0x1)
+
+#define PM88X_GPADC_CONFIG2 (0x2)
+#define PM88X_GPADC0_MEAS_EN (1 << 2)
+#define PM88X_GPADC1_MEAS_EN (1 << 3)
+#define PM88X_GPADC2_MEAS_EN (1 << 4)
+#define PM88X_GPADC3_MEAS_EN (1 << 5)
+
+#define PM88X_GPADC_CONFIG3 (0x3)
+
+#define PM88X_GPADC_CONFIG6 (0x6)
+#define PM88X_GPADC_CONFIG8 (0x8)
+
+#define PM88X_GPADC0_LOW_TH (0x20)
+#define PM88X_GPADC1_LOW_TH (0x21)
+#define PM88X_GPADC2_LOW_TH (0x22)
+#define PM88X_GPADC3_LOW_TH (0x23)
+
+#define PM88X_GPADC0_UPP_TH (0x30)
+#define PM88X_GPADC1_UPP_TH (0x31)
+#define PM88X_GPADC2_UPP_TH (0x32)
+#define PM88X_GPADC3_UPP_TH (0x33)
+
+#define PM88X_VBUS_MEAS1 (0x4A)
+#define PM88X_GPADC0_MEAS1 (0x54)
+#define PM88X_GPADC1_MEAS1 (0x56)
+#define PM88X_GPADC2_MEAS1 (0x58)
+#define PM88X_GPADC3_MEAS1 (0x5A)
+
+
+/*--charger page:------------------------------------------------------------*/
+#define PM88X_CHG_CONFIG1 (0x28)
+#define PM88X_CHGBK_CONFIG6 (0x50)
+/*-------------------------------------------------------------------------*/
+
+/*--test page:-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+#endif
diff --git a/include/linux/mfd/88pm88x.h b/include/linux/mfd/88pm88x.h
new file mode 100644
index 0000000..efa2fe6
--- /dev/null
+++ b/include/linux/mfd/88pm88x.h
@@ -0,0 +1,202 @@
+/*
+ * Marvell 88PM88X PMIC Common Interface
+ *
+ * Copyright (C) 2014 Marvell International Ltd.
+ * Yi Zhang <[email protected]>
+ *
+ * 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.
+ *
+ *
+ * This file configures the common part of the 88pm88x series PMIC
+ */
+
+#ifndef __LINUX_MFD_88PM88X_H
+#define __LINUX_MFD_88PM88X_H
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/atomic.h>
+#include <linux/reboot.h>
+#include "88pm88x-reg.h"
+#include "88pm886-reg.h"
+
+#define PM88X_RTC_NAME "88pm88x-rtc"
+#define PM88X_ONKEY_NAME "88pm88x-onkey"
+#define PM88X_CHARGER_NAME "88pm88x-charger"
+#define PM88X_BATTERY_NAME "88pm88x-battery"
+#define PM88X_HEADSET_NAME "88pm88x-headset"
+#define PM88X_VBUS_NAME "88pm88x-vbus"
+#define PM88X_CFD_NAME "88pm88x-leds"
+#define PM88X_RGB_NAME "88pm88x-rgb"
+#define PM88X_GPADC_NAME "88pm88x-gpadc"
+#define PM88X_DVC_NAME "88pm88x-dvc"
+
+enum pm88x_type {
+ PM886 = 1,
+ PM880 = 2,
+};
+
+enum pm88x_pages {
+ PM88X_BASE_PAGE = 0,
+ PM88X_LDO_PAGE,
+ PM88X_GPADC_PAGE,
+ PM88X_BATTERY_PAGE,
+ PM88X_BUCK_PAGE = 4,
+ PM88X_TEST_PAGE = 7,
+};
+
+enum pm88x_gpadc {
+ PM88X_NO_GPADC = -1,
+ PM88X_GPADC0 = 0,
+ PM88X_GPADC1,
+ PM88X_GPADC2,
+ PM88X_GPADC3,
+};
+
+/* interrupt number */
+enum pm88x_irq_number {
+ PM88X_IRQ_ONKEY, /* EN1b0 *//* 0 */
+ PM88X_IRQ_EXTON, /* EN1b1 */
+ PM88X_IRQ_CHG_GOOD, /* EN1b2 */
+ PM88X_IRQ_BAT_DET, /* EN1b3 */
+ PM88X_IRQ_RTC, /* EN1b4 */
+ PM88X_IRQ_CLASSD, /* EN1b5 *//* 5 */
+ PM88X_IRQ_XO, /* EN1b6 */
+ PM88X_IRQ_GPIO, /* EN1b7 */
+
+ PM88X_IRQ_VBAT, /* EN2b0 *//* 8 */
+ /* EN2b1 */
+ PM88X_IRQ_VBUS, /* EN2b2 */
+ PM88X_IRQ_ITEMP, /* EN2b3 *//* 10 */
+ PM88X_IRQ_BUCK_PGOOD, /* EN2b4 */
+ PM88X_IRQ_LDO_PGOOD, /* EN2b5 */
+
+ PM88X_IRQ_GPADC0, /* EN3b0 */
+ PM88X_IRQ_GPADC1, /* EN3b1 */
+ PM88X_IRQ_GPADC2, /* EN3b2 *//* 15 */
+ PM88X_IRQ_GPADC3, /* EN3b3 */
+ PM88X_IRQ_MIC_DET, /* EN3b4 */
+ PM88X_IRQ_HS_DET, /* EN3b5 */
+ PM88X_IRQ_GND_DET, /* EN3b6 */
+
+ PM88X_IRQ_CHG_FAIL, /* EN4b0 *//* 20 */
+ PM88X_IRQ_CHG_DONE, /* EN4b1 */
+ /* EN4b2 */
+ PM88X_IRQ_CFD_FAIL, /* EN4b3 */
+ PM88X_IRQ_OTG_FAIL, /* EN4b4 */
+ PM88X_IRQ_CHG_ILIM, /* EN4b5 *//* 25 */
+ /* EN4b6 */
+ PM88X_IRQ_CC, /* EN4b7 *//* 27 */
+
+ PM88X_MAX_IRQ, /* 28 */
+};
+
+/* 3 rgb led indicators */
+enum {
+ PM88X_RGB_LED0,
+ PM88X_RGB_LED1,
+ PM88X_RGB_LED2,
+};
+
+/* camera flash/torch */
+enum {
+ PM88X_NO_LED = -1,
+ PM88X_FLASH_LED = 0,
+ PM88X_TORCH_LED,
+};
+
+struct pm88x_dvc_ops {
+ void (*level_to_reg)(u8 level);
+};
+
+struct pm88x_buck1_dvc_desc {
+ u8 current_reg;
+ int max_level;
+ int uV_step1;
+ int uV_step2;
+ int min_uV;
+ int mid_uV;
+ int max_uV;
+ int mid_reg_val;
+};
+
+struct pm88x_dvc {
+ struct device *dev;
+ struct pm88x_chip *chip;
+ struct pm88x_dvc_ops ops;
+ struct pm88x_buck1_dvc_desc desc;
+};
+
+struct pm88x_chip {
+ struct i2c_client *client;
+ struct device *dev;
+
+ struct i2c_client *ldo_page; /* chip client for ldo page */
+ struct i2c_client *power_page; /* chip client for power page */
+ struct i2c_client *gpadc_page; /* chip client for gpadc page */
+ struct i2c_client *battery_page;/* chip client for battery page */
+ struct i2c_client *buck_page; /* chip client for buck page */
+ struct i2c_client *test_page; /* chip client for test page */
+
+ struct regmap *base_regmap;
+ struct regmap *ldo_regmap;
+ struct regmap *power_regmap;
+ struct regmap *gpadc_regmap;
+ struct regmap *battery_regmap;
+ struct regmap *buck_regmap;
+ struct regmap *test_regmap;
+ struct regmap *codec_regmap;
+
+ unsigned short ldo_page_addr; /* ldo page I2C address */
+ unsigned short power_page_addr; /* power page I2C address */
+ unsigned short gpadc_page_addr; /* gpadc page I2C address */
+ unsigned short battery_page_addr;/* battery page I2C address */
+ unsigned short buck_page_addr; /* buck page I2C address */
+ unsigned short test_page_addr; /* test page I2C address */
+
+ unsigned int chip_id;
+ long type; /* specific chip */
+ int irq;
+
+ int irq_mode; /* write/read clear */
+ struct regmap_irq_chip_data *irq_data;
+
+ bool rtc_wakeup; /* is it powered up by expired alarm? */
+ u8 powerdown1; /* save power down reason */
+ u8 powerdown2;
+ u8 powerup; /* the reason of power on */
+
+ struct notifier_block reboot_notifier;
+ struct pm88x_dvc *dvc;
+};
+
+extern struct regmap_irq_chip pm88x_irq_chip;
+extern const struct of_device_id pm88x_of_match[];
+
+struct pm88x_chip *pm88x_init_chip(struct i2c_client *client);
+int pm88x_parse_dt(struct device_node *np, struct pm88x_chip *chip);
+
+int pm88x_init_pages(struct pm88x_chip *chip);
+int pm88x_post_init_chip(struct pm88x_chip *chip);
+void pm800_exit_pages(struct pm88x_chip *chip);
+
+int pm88x_init_subdev(struct pm88x_chip *chip);
+long pm88x_of_get_type(struct device *dev);
+void pm88x_dev_exit(struct pm88x_chip *chip);
+
+int pm88x_irq_init(struct pm88x_chip *chip);
+int pm88x_irq_exit(struct pm88x_chip *chip);
+int pm88x_apply_patch(struct pm88x_chip *chip);
+int pm88x_stepping_fixup(struct pm88x_chip *chip);
+int pm88x_apply_board_fixup(struct pm88x_chip *chip, struct device_node *np);
+
+struct pm88x_chip *pm88x_get_chip(void);
+void pm88x_set_chip(struct pm88x_chip *chip);
+void pm88x_power_off(void);
+int pm88x_reboot_notifier_callback(struct notifier_block *nb,
+ unsigned long code, void *unused);
+
+#endif /* __LINUX_MFD_88PM88X_H */
--
1.9.1

2015-06-19 08:34:11

by Yi Zhang

[permalink] [raw]
Subject: Re: [PATCH V2 0/2] add basic support to Marvell 88pm880/88pm886 PMIC chip

On Fri, Jun 12, 2015 at 04:43:19PM +0800, Yi Zhang wrote:

Hi, Samuel and Lee:

Could you please share your comments on this patch?
then I can modify accordingly;

thanks very much;
> - 88pm880 and 88pm886 are PMIC chips which integrates regulator,
> gpadc, charger, fuelgauge, etc;
> they share most of the functions and register mapping
> - this version removes the redundant EXPORT_SYMBOL_GPL, compared with the
> first version
>
>
> Yi Zhang (2):
> mfd: add Marvell 88pm88x description
> mfd: 88pm88x: initialize 88pm886/88pm880 base support
>
> Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 ++
> drivers/mfd/88pm880-table.c | 173 +++++++
> drivers/mfd/88pm886-table.c | 173 +++++++
> drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++
> drivers/mfd/88pm88x-i2c.c | 167 +++++++
> drivers/mfd/88pm88x-irq.c | 171 +++++++
> drivers/mfd/88pm88x.h | 51 ++
> drivers/mfd/Kconfig | 12 +
> drivers/mfd/Makefile | 3 +
> include/linux/mfd/88pm880-reg.h | 98 ++++
> include/linux/mfd/88pm880.h | 59 +++
> include/linux/mfd/88pm886-reg.h | 59 +++
> include/linux/mfd/88pm886.h | 55 ++
> include/linux/mfd/88pm88x-reg.h | 118 +++++
> include/linux/mfd/88pm88x.h | 202 ++++++++
> 15 files changed, 1958 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
> create mode 100644 drivers/mfd/88pm880-table.c
> create mode 100644 drivers/mfd/88pm886-table.c
> create mode 100644 drivers/mfd/88pm88x-core.c
> create mode 100644 drivers/mfd/88pm88x-i2c.c
> create mode 100644 drivers/mfd/88pm88x-irq.c
> create mode 100644 drivers/mfd/88pm88x.h
> create mode 100644 include/linux/mfd/88pm880-reg.h
> create mode 100644 include/linux/mfd/88pm880.h
> create mode 100644 include/linux/mfd/88pm886-reg.h
> create mode 100644 include/linux/mfd/88pm886.h
> create mode 100644 include/linux/mfd/88pm88x-reg.h
> create mode 100644 include/linux/mfd/88pm88x.h
>
> --
> 1.9.1
>

2015-06-22 08:36:24

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH V2 0/2] add basic support to Marvell 88pm880/88pm886 PMIC chip

On Fri, 19 Jun 2015, Yi Zhang wrote:

> On Fri, Jun 12, 2015 at 04:43:19PM +0800, Yi Zhang wrote:
>
> Hi, Samuel and Lee:
>
> Could you please share your comments on this patch?
> then I can modify accordingly;

Please don't ping.

There are two reasons why you haven't received a reply. Firstly, we
are very late in the cycle. Many Maintainers turn their hand from
reviewing to maintaining/testing during the very late stages. Please
follow the cycles. Secondly, I have been on vacation for a little
over a week and no amount of pinging will hurry me back to work.

If you think your set has been missed, please wait a while, think
about why that might be and if you can't think of a good reason then
resend the set.

> > - 88pm880 and 88pm886 are PMIC chips which integrates regulator,
> > gpadc, charger, fuelgauge, etc;
> > they share most of the functions and register mapping
> > - this version removes the redundant EXPORT_SYMBOL_GPL, compared with the
> > first version
> >
> >
> > Yi Zhang (2):
> > mfd: add Marvell 88pm88x description
> > mfd: 88pm88x: initialize 88pm886/88pm880 base support
> >
> > Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 ++
> > drivers/mfd/88pm880-table.c | 173 +++++++
> > drivers/mfd/88pm886-table.c | 173 +++++++
> > drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++
> > drivers/mfd/88pm88x-i2c.c | 167 +++++++
> > drivers/mfd/88pm88x-irq.c | 171 +++++++
> > drivers/mfd/88pm88x.h | 51 ++
> > drivers/mfd/Kconfig | 12 +
> > drivers/mfd/Makefile | 3 +
> > include/linux/mfd/88pm880-reg.h | 98 ++++
> > include/linux/mfd/88pm880.h | 59 +++
> > include/linux/mfd/88pm886-reg.h | 59 +++
> > include/linux/mfd/88pm886.h | 55 ++
> > include/linux/mfd/88pm88x-reg.h | 118 +++++
> > include/linux/mfd/88pm88x.h | 202 ++++++++
> > 15 files changed, 1958 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
> > create mode 100644 drivers/mfd/88pm880-table.c
> > create mode 100644 drivers/mfd/88pm886-table.c
> > create mode 100644 drivers/mfd/88pm88x-core.c
> > create mode 100644 drivers/mfd/88pm88x-i2c.c
> > create mode 100644 drivers/mfd/88pm88x-irq.c
> > create mode 100644 drivers/mfd/88pm88x.h
> > create mode 100644 include/linux/mfd/88pm880-reg.h
> > create mode 100644 include/linux/mfd/88pm880.h
> > create mode 100644 include/linux/mfd/88pm886-reg.h
> > create mode 100644 include/linux/mfd/88pm886.h
> > create mode 100644 include/linux/mfd/88pm88x-reg.h
> > create mode 100644 include/linux/mfd/88pm88x.h
> >

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

2015-06-23 14:31:28

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH V2 1/2] mfd: add Marvell 88pm88x description

On Fri, Jun 12, 2015 at 3:43 AM, Yi Zhang <[email protected]> wrote:
> 88pm880 and 88pm886 are two combo PMIC chips, most of the function and the
> register mapping are the same

How do they compare to 80x/822/860 PMICs?

>
> Signed-off-by: Yi Zhang <[email protected]>
> ---
> Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 +++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/88pm88x.txt b/Documentation/devicetree/bindings/mfd/88pm88x.txt
> new file mode 100644
> index 0000000..72e741c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/88pm88x.txt
> @@ -0,0 +1,33 @@
> +Marvell 88pm88x combo PMIC chip
> +
> +This series of chip integrates regulator, rtc, onkey, switch charger,
> +fuelgauge, gpadc and a range of I/O pins.
> +
> +88pm886 and 88pm880 are two very similar chips, most of the registers mapping
> +and functions are the same, the main difference is the latter has a separate
> +i2c slave address to cover BUCK related setting
> +
> +Required properties:
> +- compatible: one of the strings for a specific chip:
> + "marvell,88pm886"
> + "marvell,88pm880"
> +- reg: the i2c address
> +- interrupt-controller: it works as an interrupt controller managing its irqs
> +- interrupt-cells: this value is 1
> +
> +Optional properties:
> +- marvell,88pm88x-irq-write-clear: the interrupt ack method

The 80x/860 binding needs the same property. Please coordinate this
with Vaibhav.

Rob

2015-06-23 14:46:12

by Vaibhav Hiremath

[permalink] [raw]
Subject: Re: [PATCH V2 1/2] mfd: add Marvell 88pm88x description



On Tuesday 23 June 2015 08:01 PM, Rob Herring wrote:
> On Fri, Jun 12, 2015 at 3:43 AM, Yi Zhang <[email protected]> wrote:
>> 88pm880 and 88pm886 are two combo PMIC chips, most of the function and the
>> register mapping are the same
>
> How do they compare to 80x/822/860 PMICs?
>

Zhang,

Sorry, I missed this patch-series.

Is there any way we can get access to datahsheets of all these devices?

>>
>> Signed-off-by: Yi Zhang <[email protected]>
>> ---
>> Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 +++++++++++++++++++++++
>> 1 file changed, 33 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/88pm88x.txt b/Documentation/devicetree/bindings/mfd/88pm88x.txt
>> new file mode 100644
>> index 0000000..72e741c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/88pm88x.txt
>> @@ -0,0 +1,33 @@
>> +Marvell 88pm88x combo PMIC chip
>> +
>> +This series of chip integrates regulator, rtc, onkey, switch charger,
>> +fuelgauge, gpadc and a range of I/O pins.
>> +
>> +88pm886 and 88pm880 are two very similar chips, most of the registers mapping
>> +and functions are the same, the main difference is the latter has a separate
>> +i2c slave address to cover BUCK related setting
>> +
>> +Required properties:
>> +- compatible: one of the strings for a specific chip:
>> + "marvell,88pm886"
>> + "marvell,88pm880"
>> +- reg: the i2c address
>> +- interrupt-controller: it works as an interrupt controller managing its irqs
>> +- interrupt-cells: this value is 1
>> +
>> +Optional properties:
>> +- marvell,88pm88x-irq-write-clear: the interrupt ack method
>
> The 80x/860 binding needs the same property. Please coordinate this
> with Vaibhav.
>

Please refer to the patch

https://lkml.org/lkml/2015/6/17/545

Thanks,
Vaibhav

2015-06-25 08:33:06

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH V2 2/2] mfd: 88pm88x: initialize 88pm886/88pm880 base support

On Fri, 12 Jun 2015, Yi Zhang wrote:

> 88pm886 and 88pm880 are combo PMIC chip, which integrates
> regulator, onkey, rtc, gpadc, charger, fuelgauge function;
>
> this patch add the basic support for them, adding related resource, such as
> interrupt, preparing for the client-device driver
>
> Signed-off-by: Yi Zhang <[email protected]>
> ---
> drivers/mfd/88pm880-table.c | 173 ++++++++++++
> drivers/mfd/88pm886-table.c | 173 ++++++++++++
> drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++++++++++++++++++++
> drivers/mfd/88pm88x-i2c.c | 167 ++++++++++++
> drivers/mfd/88pm88x-irq.c | 171 ++++++++++++
> drivers/mfd/88pm88x.h | 51 ++++

I'm initially concerned that you aren't (re)using _any_ of the 88pm*
files already in the subsystem.

> drivers/mfd/Kconfig | 12 +
> drivers/mfd/Makefile | 3 +
> include/linux/mfd/88pm880-reg.h | 98 +++++++
> include/linux/mfd/88pm880.h | 59 ++++
> include/linux/mfd/88pm886-reg.h | 59 ++++
> include/linux/mfd/88pm886.h | 55 ++++
> include/linux/mfd/88pm88x-reg.h | 118 ++++++++
> include/linux/mfd/88pm88x.h | 202 ++++++++++++++
> 14 files changed, 1925 insertions(+)

Sending 2k line patches doesn't make reviewing a particularly pleasant
experience. Please divide it up into a properly separated set. If
you feel as though the set can't be split up, then you're probably
coding it wrongly.

> create mode 100644 drivers/mfd/88pm880-table.c
> create mode 100644 drivers/mfd/88pm886-table.c
> create mode 100644 drivers/mfd/88pm88x-core.c
> create mode 100644 drivers/mfd/88pm88x-i2c.c
> create mode 100644 drivers/mfd/88pm88x-irq.c
> create mode 100644 drivers/mfd/88pm88x.h
> create mode 100644 include/linux/mfd/88pm880-reg.h
> create mode 100644 include/linux/mfd/88pm880.h
> create mode 100644 include/linux/mfd/88pm886-reg.h
> create mode 100644 include/linux/mfd/88pm886.h
> create mode 100644 include/linux/mfd/88pm88x-reg.h
> create mode 100644 include/linux/mfd/88pm88x.h
>
> diff --git a/drivers/mfd/88pm880-table.c b/drivers/mfd/88pm880-table.c
> new file mode 100644
> index 0000000..28ca860
> --- /dev/null
> +++ b/drivers/mfd/88pm880-table.c
> @@ -0,0 +1,173 @@
> +/*
> + * Marvell 88PM880 specific setting
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>

Tea Maker, Project Manger, CEO, Janitor?

Or "Author: "

> + * 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/kernel.h>
> +#include <linux/device.h>
> +#include <linux/mfd/88pm88x.h>
> +#include <linux/mfd/88pm880.h>
> +#include <linux/mfd/88pm880-reg.h>
> +#include <linux/mfd/88pm88x-reg.h>
> +
> +#include "88pm88x.h"
> +
> +#define PM880_BUCK_NAME "88pm880-buck"
> +#define PM880_LDO_NAME "88pm880-ldo"
> +
> +const struct regmap_config pm880_base_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm880_power_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm880_gpadc_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm880_battery_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm880_test_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +static const struct resource buck_resources[] = {
> + {
> + .name = PM880_BUCK_NAME,

Tabbing.

> + },
> +};
> +
> +static const struct resource ldo_resources[] = {
> + {
> + .name = PM880_LDO_NAME,

Tabbing.

> + },
> +};
> +
> +const struct mfd_cell pm880_cell_devs[] = {
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck1a", 0),
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck2", 1),
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck3", 2),
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck4", 3),
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck5", 4),
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck6", 5),
> + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck7", 6),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo1", 7),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo2", 8),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo3", 9),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo4", 10),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo5", 11),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo6", 12),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo7", 13),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo8", 14),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo9", 15),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo10", 16),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo11", 17),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo12", 18),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo13", 19),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo14", 20),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo15", 21),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo16", 22),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo17", 23),
> + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo18", 24),
> +};

You don't want an MFD for each regulator. See the DT documentation
for regulators.

> +struct pmic_cell_info pm880_cell_info = {
> + .cells = pm880_cell_devs,
> + .cell_nr = ARRAY_SIZE(pm880_cell_devs),
> +};

No need for this, please remove it.

> +static const struct reg_default pm880_base_patch[] = {
> + {PM88X_WDOG, 0x1}, /* disable watchdog */

Pad it out like you did the others "0x01".

> + {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */

kHz

> + {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */

Don't put bit names in comments -- you should define them instead.

> + {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */

Remove the bit name -- second part is fine.

> + /* enable LPM for internal reference group in sleep */

I see you're not a fan of using capital letter to start a sentence?

> + {PM88X_LOWPOWER4, 0xc0},

No comment?

> + {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
> +};

This would be easier to read if you lined it up:

{PM88X_WDOG, 0x01}, /* disable watchdog */
{PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
{PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
{PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
/* enable LPM for internal reference group in sleep */
{PM88X_LOWPOWER4, 0xc0},
{PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */

... don't you think?

I also think the values should be defined. If you disagree, at the
_very least_ please sharpen up the comments.

> +static const struct reg_default pm880_power_patch[] = {
> +};

What's the point of this?

> +static const struct reg_default pm880_gpadc_patch[] = {
> + {PM88X_GPADC_CONFIG6, 0x03}, /* enable non-stop mode */
> +};

I personally prefer spaces after the first and before the second curly
bracket.

> +static const struct reg_default pm880_battery_patch[] = {
> + {PM88X_CHGBK_CONFIG6, 0xe1},
> +};

As above.

> +static const struct reg_default pm880_test_patch[] = {
> +};

Why do you have an empty struct const?

> +/* 88pm880 chip itself related */

I have no idea what this means?

> +int pm880_apply_patch(struct pm88x_chip *chip)
> +{
> + int ret, size;
> +
> + if (!chip || !chip->base_regmap || !chip->power_regmap ||
> + !chip->gpadc_regmap || !chip->battery_regmap ||
> + !chip->test_regmap)
> + return -EINVAL;

You already checked for this in pm88x_post_init_chip().

> + size = ARRAY_SIZE(pm880_base_patch);
> + if (size == 0)
> + goto power;
> + ret = regmap_register_patch(chip->base_regmap, pm880_base_patch, size);
> + if (ret < 0)

I assume any non-zero return value is bad. If so, please change all
of these too "if (ret)".

> + return ret;
> +
> +power:
> + size = ARRAY_SIZE(pm880_power_patch);
> + if (size == 0)
> + goto gpadc;
> + ret = regmap_register_patch(chip->power_regmap, pm880_power_patch, size);
> + if (ret < 0)
> + return ret;
> +
> +gpadc:
> + size = ARRAY_SIZE(pm880_gpadc_patch);
> + if (size == 0)
> + goto battery;
> + ret = regmap_register_patch(chip->gpadc_regmap, pm880_gpadc_patch, size);
> + if (ret < 0)
> + return ret;

'\n'

> +battery:
> + size = ARRAY_SIZE(pm880_battery_patch);
> + if (size == 0)
> + goto test;
> + ret = regmap_register_patch(chip->battery_regmap, pm880_battery_patch, size);
> + if (ret < 0)
> + return ret;
> +
> +test:
> + size = ARRAY_SIZE(pm880_test_patch);
> + if (size == 0)
> + goto out;
> + ret = regmap_register_patch(chip->test_regmap, pm880_test_patch, size);
> + if (ret < 0)
> + return ret;

'\n'

> +out:
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(pm880_apply_patch);
> diff --git a/drivers/mfd/88pm886-table.c b/drivers/mfd/88pm886-table.c
> new file mode 100644
> index 0000000..897ee82
> --- /dev/null
> +++ b/drivers/mfd/88pm886-table.c
> @@ -0,0 +1,173 @@
> +/*
> + * Marvell 88PM886 specific setting
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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/kernel.h>
> +#include <linux/device.h>
> +#include <linux/mfd/88pm88x.h>
> +#include <linux/mfd/88pm886.h>
> +#include <linux/mfd/88pm886-reg.h>
> +#include <linux/mfd/88pm88x-reg.h>
> +
> +#include "88pm88x.h"
> +
> +#define PM886_BUCK_NAME "88pm886-buck"
> +#define PM886_LDO_NAME "88pm886-ldo"
> +
> +const struct regmap_config pm886_base_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm886_power_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm886_gpadc_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm886_battery_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +const struct regmap_config pm886_test_i2c_regmap = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xfe,
> +};
> +
> +static const struct resource buck_resources[] = {
> + {
> + .name = PM886_BUCK_NAME,
> + },
> +};
> +
> +static const struct resource ldo_resources[] = {
> + {
> + .name = PM886_LDO_NAME,
> + },
> +};
> +
> +const struct mfd_cell pm886_cell_devs[] = {
> + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck1", 0),
> + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck2", 1),
> + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck3", 2),
> + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck4", 3),
> + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck5", 4),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo1", 5),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo2", 6),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo3", 7),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo4", 8),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo5", 9),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo6", 10),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo7", 11),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo8", 12),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo9", 13),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo10", 14),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo11", 15),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo12", 16),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo13", 17),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo14", 18),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo15", 19),
> + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo16", 20),
> +};

You don't want an MFD for each regulator. See the DT documentation
for regulators.

> +struct pmic_cell_info pm886_cell_info = {
> + .cells = pm886_cell_devs,
> + .cell_nr = ARRAY_SIZE(pm886_cell_devs),
> +};

No need for this, please remove it.

> +static const struct reg_default pm886_base_patch[] = {
> + {PM88X_WDOG, 0x1}, /* disable watchdog */
> + {PM88X_GPIO_CTRL1, 0x40}, /* gpio1: dvc , gpio0: input */
> + {PM88X_GPIO_CTRL2, 0x00}, /* , gpio2: input */
> + {PM88X_GPIO_CTRL3, 0x44}, /* dvc2 , dvc1 */
> + {PM88X_GPIO_CTRL4, 0x00}, /* gpio5v_1:input, gpio5v_2: input*/
> + {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
> + {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
> + {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
> + /* enable LPM for internal reference group in sleep */
> + {PM88X_LOWPOWER4, 0xc0},
> + {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
> +};
> +
> +static const struct reg_default pm886_power_patch[] = {
> +};
> +
> +static const struct reg_default pm886_gpadc_patch[] = {
> + {PM88X_GPADC_CONFIG6, 0x03}, /* enable non-stop mode */
> +};
> +
> +static const struct reg_default pm886_battery_patch[] = {
> + {PM88X_CHGBK_CONFIG6, 0xe1},
> +};
> +
> +static const struct reg_default pm886_test_patch[] = {
> +};
> +
> +/* 88pm886 chip itself related */
> +int pm886_apply_patch(struct pm88x_chip *chip)
> +{
> + int ret, size;
> +
> + if (!chip || !chip->base_regmap || !chip->power_regmap ||
> + !chip->gpadc_regmap || !chip->battery_regmap ||
> + !chip->test_regmap)
> + return -EINVAL;

You already checked for this in pm88x_post_init_chip().

> + size = ARRAY_SIZE(pm886_base_patch);
> + if (size == 0)
> + goto power;
> + ret = regmap_register_patch(chip->base_regmap, pm886_base_patch, size);
> + if (ret < 0)
> + return ret;
> +
> +power:
> + size = ARRAY_SIZE(pm886_power_patch);
> + if (size == 0)
> + goto gpadc;
> + ret = regmap_register_patch(chip->power_regmap, pm886_power_patch, size);
> + if (ret < 0)
> + return ret;
> +
> +gpadc:
> + size = ARRAY_SIZE(pm886_gpadc_patch);
> + if (size == 0)
> + goto battery;
> + ret = regmap_register_patch(chip->gpadc_regmap, pm886_gpadc_patch, size);
> + if (ret < 0)
> + return ret;
> +battery:
> + size = ARRAY_SIZE(pm886_battery_patch);
> + if (size == 0)
> + goto test;
> + ret = regmap_register_patch(chip->battery_regmap, pm886_battery_patch, size);
> + if (ret < 0)
> + return ret;
> +
> +test:
> + size = ARRAY_SIZE(pm886_test_patch);
> + if (size == 0)
> + goto out;
> + ret = regmap_register_patch(chip->test_regmap, pm886_test_patch, size);
> + if (ret < 0)
> + return ret;
> +out:
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(pm886_apply_patch);

A lot of this code looks identical to the previous *-table.c file and
a bunch of it is duplicated line from line. Please amalgamate them
and find a way to reduce the amount of lines. I can think of several
ways this can be done.

> diff --git a/drivers/mfd/88pm88x-core.c b/drivers/mfd/88pm88x-core.c
> new file mode 100644
> index 0000000..343e0a0
> --- /dev/null
> +++ b/drivers/mfd/88pm88x-core.c
> @@ -0,0 +1,584 @@
> +/*
> + * Base driver for Marvell 88PM886/88PM880 PMIC
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqdomain.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/88pm88x.h>
> +#include <linux/mfd/88pm886.h>
> +#include <linux/mfd/88pm880.h>
> +#include <linux/regulator/machine.h>
> +
> +#include "88pm88x.h"
> +
> +#define PM88X_POWER_UP_LOG (0x17)
> +#define PM88X_POWER_DOWN_LOG1 (0xe5)
> +#define PM88X_POWER_DOWN_LOG2 (0xe6)
> +#define PM88X_SW_PDOWN (1 << 5)
> +
> +static const struct resource onkey_resources[] = {
> + CELL_IRQ_RESOURCE(PM88X_ONKEY_NAME, PM88X_IRQ_ONKEY),
> +};
> +
> +static const struct resource rtc_resources[] = {
> + CELL_IRQ_RESOURCE(PM88X_RTC_NAME, PM88X_IRQ_RTC),
> +};
> +
> +static const struct resource charger_resources[] = {
> + CELL_IRQ_RESOURCE("88pm88x-chg-fail", PM88X_IRQ_CHG_FAIL),
> + CELL_IRQ_RESOURCE("88pm88x-chg-done", PM88X_IRQ_CHG_DONE),
> + CELL_IRQ_RESOURCE("88pm88x-chg-good", PM88X_IRQ_CHG_GOOD),
> +};
> +
> +static const struct resource battery_resources[] = {
> + CELL_IRQ_RESOURCE("88pm88x-bat-cc", PM88X_IRQ_CC),
> + CELL_IRQ_RESOURCE("88pm88x-bat-volt", PM88X_IRQ_VBAT),
> + CELL_IRQ_RESOURCE("88pm88x-bat-detect", PM88X_IRQ_BAT_DET),
> +};
> +
> +static const struct resource headset_resources[] = {
> + CELL_IRQ_RESOURCE("88pm88x-headset-det", PM88X_IRQ_HS_DET),
> + CELL_IRQ_RESOURCE("88pm88x-mic-det", PM88X_IRQ_MIC_DET),
> +};
> +
> +static const struct resource vbus_resources[] = {
> + CELL_IRQ_RESOURCE("88pm88x-vbus-det", PM88X_IRQ_VBUS),
> + CELL_IRQ_RESOURCE("88pm88x-gpadc0", PM88X_IRQ_GPADC0),
> + CELL_IRQ_RESOURCE("88pm88x-gpadc1", PM88X_IRQ_GPADC1),
> + CELL_IRQ_RESOURCE("88pm88x-gpadc2", PM88X_IRQ_GPADC2),
> + CELL_IRQ_RESOURCE("88pm88x-gpadc3", PM88X_IRQ_GPADC3),
> + CELL_IRQ_RESOURCE("88pm88x-otg-fail", PM88X_IRQ_OTG_FAIL),
> +};
> +
> +static const struct resource leds_resources[] = {
> + CELL_IRQ_RESOURCE("88pm88x-cfd-fail", PM88X_IRQ_CFD_FAIL),
> +};
> +
> +static const struct resource dvc_resources[] = {
> + {
> + .name = PM88X_DVC_NAME,
> + },
> +};
> +
> +static const struct resource rgb_resources[] = {
> + {
> + .name = PM88X_RGB_NAME,
> + },
> +};
> +
> +static const struct resource gpadc_resources[] = {
> + {
> + .name = PM88X_GPADC_NAME,
> + },
> +};

Tabbing for all of the above.

In fact, for one line struct entries, I would prefer:

{ .name = PM88X_GPADC_NAME },

> +static const struct mfd_cell common_cell_devs[] = {
> + CELL_DEV(PM88X_RTC_NAME, rtc_resources, "marvell,88pm88x-rtc", -1),
> + CELL_DEV(PM88X_ONKEY_NAME, onkey_resources, "marvell,88pm88x-onkey", -1),
> + CELL_DEV(PM88X_CHARGER_NAME, charger_resources, "marvell,88pm88x-charger", -1),
> + CELL_DEV(PM88X_BATTERY_NAME, battery_resources, "marvell,88pm88x-battery", -1),
> + CELL_DEV(PM88X_HEADSET_NAME, headset_resources, "marvell,88pm88x-headset", -1),
> + CELL_DEV(PM88X_VBUS_NAME, vbus_resources, "marvell,88pm88x-vbus", -1),
> + CELL_DEV(PM88X_CFD_NAME, leds_resources, "marvell,88pm88x-leds", PM88X_FLASH_LED),
> + CELL_DEV(PM88X_CFD_NAME, leds_resources, "marvell,88pm88x-leds", PM88X_TORCH_LED),
> + CELL_DEV(PM88X_DVC_NAME, dvc_resources, "marvell,88pm88x-dvc", -1),
> + CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb0", PM88X_RGB_LED0),
> + CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb1", PM88X_RGB_LED1),
> + CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb2", PM88X_RGB_LED2),

This isn't how it works. You should have one LED entry here, then let
the LED driver take care of all this stuff.

> + CELL_DEV(PM88X_GPADC_NAME, gpadc_resources, "marvell,88pm88x-gpadc", -1),
> +};


> +const struct of_device_id pm88x_of_match[] = {
> + { .compatible = "marvell,88pm886", .data = (void *)PM886 },
> + { .compatible = "marvell,88pm880", .data = (void *)PM880 },
> + {},
> +};
> +
> +struct pm88x_chip *pm88x_init_chip(struct i2c_client *client)

Why is this in a seperate file to the one it's called from? I
suggest you move it into the *-i2c.c file.

> +{
> + struct pm88x_chip *chip;
> +
> + chip = devm_kzalloc(&client->dev, sizeof(struct pm88x_chip), GFP_KERNEL);

Calling this 'chip' is confusing, as it has different connotations
depending on the subsystem.

ddata (for device data) or priv for (private) is more common.

> + if (!chip)
> + return ERR_PTR(-ENOMEM);
> +
> + chip->client = client;
> + chip->irq = client->irq;
> + chip->dev = &client->dev;

Why are you storing 'client' AND 'dev'? Do you use 'client' directly?
If not, ditch it. If you do, then just store 'client' and extract
'client->dev' when you require it.

> + chip->ldo_page_addr = client->addr + 1;
> + chip->power_page_addr = client->addr + 1;
> + chip->gpadc_page_addr = client->addr + 2;
> + chip->battery_page_addr = client->addr + 3;
> + chip->buck_page_addr = client->addr + 4;
> + chip->test_page_addr = client->addr + 7;

I have no idea what's going on here, but it's almost certainly not
correct. Instead of separating these out per device have a base
address and create DEFINES for each of them.

> + dev_set_drvdata(chip->dev, chip);
> + i2c_set_clientdata(chip->client, chip);

Why do you have both of these?

> + device_init_wakeup(&client->dev, 1);
> +
> + return chip;

You're wearing a belt and 2 pairs of braces here. You just stored
'chip' into the device's private data area, why are you returning it
too?

> +}
> +
> +int pm88x_parse_dt(struct device_node *np, struct pm88x_chip *chip)
> +{
> + if (!chip)
> + return -EINVAL;
> +
> + chip->irq_mode =
> + !of_property_read_bool(np, "marvell,88pm88x-irq-write-clear");

A new function for just one line? Just move it into probe()

> + return 0;
> +}
> +
> +

Superfluous '\n'.

> +static void parse_powerup_down_log(struct pm88x_chip *chip)
> +{
> + int powerup, powerdown1, powerdown2, bit;
> + int powerup_bits, powerdown1_bits, powerdown2_bits;
> + static const char * const powerup_name[] = {
> + "ONKEY_WAKEUP ",
> + "CHG_WAKEUP ",
> + "EXTON_WAKEUP ",
> + "SMPL_WAKEUP ",
> + "ALARM_WAKEUP ",
> + "FAULT_WAKEUP ",
> + "BAT_WAKEUP ",
> + "WLCHG_WAKEUP ",
> + };
> + static const char * const powerdown1_name[] = {
> + "OVER_TEMP ",
> + "UV_VANA5 ",
> + "SW_PDOWN ",
> + "FL_ALARM ",
> + "WD ",
> + "LONG_ONKEY",
> + "OV_VSYS ",
> + "RTC_RESET "
> + };
> + static const char * const powerdown2_name[] = {
> + "HYB_DONE ",
> + "UV_VBAT ",
> + "HW_RESET2 ",
> + "PGOOD_PDOWN",
> + "LONKEY_RTC ",
> + "HW_RESET1 ",
> + };
> +
> + regmap_read(chip->base_regmap, PM88X_POWER_UP_LOG, &powerup);
> + regmap_read(chip->base_regmap, PM88X_POWER_DOWN_LOG1, &powerdown1);
> + regmap_read(chip->base_regmap, PM88X_POWER_DOWN_LOG2, &powerdown2);
> +
> + /*
> + * mask reserved bits
> + *
> + * note: HYB_DONE and HW_RESET1 are kept,
> + * but should not be considered as power down events
> + */
> + switch (chip->type) {
> + case PM886:
> + powerup &= 0x7f;
> + powerdown2 &= 0x1f;
> + powerup_bits = 7;
> + powerdown1_bits = 8;
> + powerdown2_bits = 5;
> + break;
> + case PM880:
> + powerdown2 &= 0x3f;
> + powerup_bits = 8;
> + powerdown1_bits = 8;
> + powerdown2_bits = 6;
> + break;
> + default:
> + return;
> + }
> +
> + /* keep globals for external usage */
> + chip->powerup = powerup;
> + chip->powerdown1 = powerdown1;
> + chip->powerdown2 = powerdown2;
> +
> + /* power up log */
> + dev_info(chip->dev, "powerup log 0x%x: 0x%x\n",
> + PM88X_POWER_UP_LOG, powerup);
> + dev_info(chip->dev, " ----------------------------\n");
> + dev_info(chip->dev, "| name(power up) | status |\n");
> + dev_info(chip->dev, "|-----------------|----------|\n");
> + for (bit = 0; bit < powerup_bits; bit++)
> + dev_info(chip->dev, "| %s | %x |\n",
> + powerup_name[bit], (powerup >> bit) & 1);
> + dev_info(chip->dev, " ----------------------------\n");
> +
> + /* power down log1 */
> + dev_info(chip->dev, "PowerDW Log1 0x%x: 0x%x\n",
> + PM88X_POWER_DOWN_LOG1, powerdown1);
> + dev_info(chip->dev, " -------------------------------\n");
> + dev_info(chip->dev, "| name(power down1) | status |\n");
> + dev_info(chip->dev, "|--------------------|----------|\n");
> + for (bit = 0; bit < powerdown1_bits; bit++)
> + dev_info(chip->dev, "| %s | %x |\n",
> + powerdown1_name[bit], (powerdown1 >> bit) & 1);
> + dev_info(chip->dev, " -------------------------------\n");
> +
> + /* power down log2 */
> + dev_info(chip->dev, "PowerDW Log2 0x%x: 0x%x\n",
> + PM88X_POWER_DOWN_LOG2, powerdown2);
> + dev_info(chip->dev, " -------------------------------\n");
> + dev_info(chip->dev, "| name(power down2) | status |\n");
> + dev_info(chip->dev, "|--------------------|----------|\n");
> + for (bit = 0; bit < powerdown2_bits; bit++)
> + dev_info(chip->dev, "| %s | %x |\n",
> + powerdown2_name[bit], (powerdown2 >> bit) & 1);
> + dev_info(chip->dev, " -------------------------------\n");
> +
> + /* write to clear power down log */
> + regmap_write(chip->base_regmap, PM88X_POWER_DOWN_LOG1, 0xff);
> + regmap_write(chip->base_regmap, PM88X_POWER_DOWN_LOG2, 0xff);
> +}
> +
> +static const char *chip_stepping_to_string(unsigned int id)
> +{
> + switch (id) {
> + case 0xa1:
> + return "88pm886 A1";
> + case 0xb1:
> + return "88pm880 A1";
> + default:
> + return "Unknown";
> + }
> +}
> +
> +int pm88x_post_init_chip(struct pm88x_chip *chip)
> +{
> + int ret;
> + unsigned int val;
> +
> + if (!chip || !chip->base_regmap || !chip->power_regmap ||
> + !chip->gpadc_regmap || !chip->battery_regmap)
> + return -EINVAL;
> +
> + /* save chip stepping */
> + ret = regmap_read(chip->base_regmap, PM88X_ID_REG, &val);
> + if (ret < 0) {
> + dev_err(chip->dev, "Failed to read chip ID: %d\n", ret);
> + return ret;
> + }
> + chip->chip_id = val;
> +
> + dev_info(chip->dev, "PM88X chip ID = 0x%x(%s)\n", val,
> + chip_stepping_to_string(chip->chip_id));
> +
> + /* read before alarm wake up bit before initialize interrupt */
> + ret = regmap_read(chip->base_regmap, PM88X_RTC_ALARM_CTRL1, &val);
> + if (ret < 0) {
> + dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
> + return ret;
> + }
> + chip->rtc_wakeup = !!(val & PM88X_ALARM_WAKEUP);
> +
> + parse_powerup_down_log(chip);
> +
> + return 0;
> +}
> +
> +int pm88x_init_pages(struct pm88x_chip *chip)
> +{
> + struct i2c_client *client = chip->client;
> + const struct regmap_config *base_regmap_config;
> + const struct regmap_config *power_regmap_config;
> + const struct regmap_config *gpadc_regmap_config;
> + const struct regmap_config *battery_regmap_config;
> + const struct regmap_config *test_regmap_config;
> + int ret = 0;
> +
> + if (!chip || !chip->power_page_addr ||
> + !chip->gpadc_page_addr || !chip->battery_page_addr)
> + return -ENODEV;
> +
> + chip->type = pm88x_of_get_type(&client->dev);
> + switch (chip->type) {
> + case PM886:
> + base_regmap_config = &pm886_base_i2c_regmap;
> + power_regmap_config = &pm886_power_i2c_regmap;
> + gpadc_regmap_config = &pm886_gpadc_i2c_regmap;
> + battery_regmap_config = &pm886_battery_i2c_regmap;
> + test_regmap_config = &pm886_test_i2c_regmap;
> + break;
> + case PM880:
> + base_regmap_config = &pm880_base_i2c_regmap;
> + power_regmap_config = &pm880_power_i2c_regmap;
> + gpadc_regmap_config = &pm880_gpadc_i2c_regmap;
> + battery_regmap_config = &pm880_battery_i2c_regmap;
> + test_regmap_config = &pm880_test_i2c_regmap;
> + break;
> + default:
> + return -ENODEV;

You could probably do with an error message here.

> + }
> +
> + /* base page */
> + chip->base_regmap = devm_regmap_init_i2c(client, base_regmap_config);
> + if (IS_ERR(chip->base_regmap)) {
> + dev_err(chip->dev, "Failed to init base_regmap: %d\n", ret);
> + ret = PTR_ERR(chip->base_regmap);
> + goto out;
> + }
> +
> + /* power page */
> + chip->power_page = i2c_new_dummy(client->adapter, chip->power_page_addr);
> + if (!chip->power_page) {
> + dev_err(chip->dev, "Failed to new power_page: %d\n", ret);
> + ret = -ENODEV;
> + goto out;
> + }
> + chip->power_regmap = devm_regmap_init_i2c(chip->power_page,
> + power_regmap_config);
> + if (IS_ERR(chip->power_regmap)) {
> + dev_err(chip->dev, "Failed to init power_regmap: %d\n", ret);
> + ret = PTR_ERR(chip->power_regmap);
> + goto out;
> + }
> +
> + /* gpadc page */
> + chip->gpadc_page = i2c_new_dummy(client->adapter, chip->gpadc_page_addr);
> + if (!chip->gpadc_page) {
> + dev_err(chip->dev, "Failed to new gpadc_page: %d\n", ret);
> + ret = -ENODEV;
> + goto out;
> + }
> + chip->gpadc_regmap = devm_regmap_init_i2c(chip->gpadc_page,
> + gpadc_regmap_config);
> + if (IS_ERR(chip->gpadc_regmap)) {
> + dev_err(chip->dev, "Failed to init gpadc_regmap: %d\n", ret);
> + ret = PTR_ERR(chip->gpadc_regmap);
> + goto out;
> + }
> +
> + /* battery page */
> + chip->battery_page = i2c_new_dummy(client->adapter, chip->battery_page_addr);
> + if (!chip->battery_page) {
> + dev_err(chip->dev, "Failed to new gpadc_page: %d\n", ret);
> + ret = -ENODEV;
> + goto out;
> + }
> + chip->battery_regmap = devm_regmap_init_i2c(chip->battery_page,
> + battery_regmap_config);
> + if (IS_ERR(chip->battery_regmap)) {
> + dev_err(chip->dev, "Failed to init battery_regmap: %d\n", ret);
> + ret = PTR_ERR(chip->battery_regmap);
> + goto out;
> + }
> +
> + /* test page */
> + chip->test_page = i2c_new_dummy(client->adapter, chip->test_page_addr);
> + if (!chip->test_page) {
> + dev_err(chip->dev, "Failed to new test_page: %d\n", ret);
> + ret = -ENODEV;
> + goto out;
> + }
> + chip->test_regmap = devm_regmap_init_i2c(chip->test_page,
> + test_regmap_config);
> + if (IS_ERR(chip->test_regmap)) {
> + dev_err(chip->dev, "Failed to init test_regmap: %d\n", ret);
> + ret = PTR_ERR(chip->test_regmap);
> + goto out;
> + }

There's a lot of duplicated code here. You can save quite a few lines
if you thought about it I'm sure.

> + chip->type = pm88x_of_get_type(&client->dev);
> + switch (chip->type) {
> + case PM886:
> + /* ldo page */
> + chip->ldo_page = chip->power_page;
> + chip->ldo_regmap = chip->power_regmap;
> + /* buck page */
> + chip->buck_regmap = chip->power_regmap;
> + break;
> + case PM880:
> + /* ldo page */
> + chip->ldo_page = chip->power_page;
> + chip->ldo_regmap = chip->power_regmap;
> +
> + /* buck page */
> + chip->buck_page = i2c_new_dummy(client->adapter,
> + chip->buck_page_addr);
> + if (!chip->buck_page) {
> + dev_err(chip->dev, "Failed to new buck_page: %d\n", ret);
> + ret = -ENODEV;
> + goto out;
> + }
> + chip->buck_regmap = devm_regmap_init_i2c(chip->buck_page,
> + power_regmap_config);
> + if (IS_ERR(chip->buck_regmap)) {
> + dev_err(chip->dev, "Failed to init buck_regmap: %d\n", ret);
> + ret = PTR_ERR(chip->buck_regmap);
> + goto out;
> + }
> +
> + break;
> + default:
> + ret = -EINVAL;

Firstly, this probably won't happen, as the first switch() succeeded.
Secondly, you are returning a different error value, why? Thirdly,
you probably want an error message here.

> + break;
> + }
> +out:
> + return ret;
> +}
> +
> +void pm800_exit_pages(struct pm88x_chip *chip)
> +{
> + if (!chip)
> + return;
> +
> + if (chip->ldo_page)
> + i2c_unregister_device(chip->ldo_page);
> + if (chip->gpadc_page)
> + i2c_unregister_device(chip->gpadc_page);
> + if (chip->test_page)
> + i2c_unregister_device(chip->test_page);

'\n'

> + /* no need to unregister ldo_page */
> + switch (chip->type) {
> + case PM886:
> + break;
> + case PM880:
> + if (chip->buck_page)
> + i2c_unregister_device(chip->buck_page);
> + break;
> + default:
> + break;
> + }

No need for this big switch(). Just do:

if (chip->buck_page)
i2c_unregister_device(chip->buck_page);

> +}
> +
> +int pm88x_init_subdev(struct pm88x_chip *chip)
> +{
> + int ret;

'\n'

This will raise a Checkpatch error. Did you run this set though
checkpatch.pl?

> + if (!chip)
> + return -EINVAL;

I'm pretty sure you can't get here if !chip.

> + ret = mfd_add_devices(chip->dev, 0, common_cell_devs,

What does 0 mean?

> + ARRAY_SIZE(common_cell_devs), NULL, 0,
> + regmap_irq_get_domain(chip->irq_data));
> + if (ret < 0)

if (ret)

> + return ret;
> +
> + switch (chip->type) {
> + case PM886:
> + ret = mfd_add_devices(chip->dev, 0, pm886_cell_info.cells,
> + pm886_cell_info.cell_nr, NULL, 0,
> + regmap_irq_get_domain(chip->irq_data));
> + break;
> + case PM880:
> + ret = mfd_add_devices(chip->dev, 0, pm880_cell_info.cells,
> + pm880_cell_info.cell_nr, NULL, 0,
> + regmap_irq_get_domain(chip->irq_data));

switch() on {pm880_cell_info|pm886_cell_info.cells}.cells etc and just
call mfd_add_devices() once.

> + break;
> + default:
> + break;
> + }
> + return ret;
> +}
> +
> +static int (*apply_to_chip)(struct pm88x_chip *chip);
> +/* PMIC chip itself related */
> +int pm88x_apply_patch(struct pm88x_chip *chip)
> +{
> + if (!chip || !chip->client)
> + return -EINVAL;
> +
> + chip->type = pm88x_of_get_type(&chip->client->dev);
> + switch (chip->type) {
> + case PM886:
> + apply_to_chip = pm886_apply_patch;
> + break;
> + case PM880:
> + apply_to_chip = pm880_apply_patch;
> + break;
> + default:
> + break;
> + }
> + if (apply_to_chip)
> + apply_to_chip(chip);
> + return 0;
> +}

What on earth is going on here?

Why bother assigning the *fn()?

> +int pm88x_stepping_fixup(struct pm88x_chip *chip)
> +{
> + if (!chip || !chip->client)
> + return -EINVAL;

I don't think this can happen.

> + chip->type = pm88x_of_get_type(&chip->client->dev);
> + switch (chip->type) {
> + case PM886:
> + case PM880:
> + default:
> + break;
> + }
> +
> + return 0;
> +}

This function appears to do nothing. Why is it here?

> +int pm88x_apply_board_fixup(struct pm88x_chip *chip, struct device_node *np)
> +{
> + /* add board design specific setting, parsed via device tree */
> + return 0;
> +}

What's this? Another empty function.

> +long pm88x_of_get_type(struct device *dev)
> +{
> + const struct of_device_id *id = of_match_device(pm88x_of_match, dev);
> +
> + if (id)
> + return (long)id->data;
> + else
> + return 0;
> +}

Just do this once, in probe and store the value in driver data. No
need for a function to do this.

> +void pm88x_dev_exit(struct pm88x_chip *chip)
> +{
> + mfd_remove_devices(chip->dev);
> + pm88x_irq_exit(chip);
> +}
> +
> +void pm88x_power_off(void)
> +{
> + pr_info("powers off the system.");
> + /* TODO: implement later */

Why can't you implement this now?

> + for (;;)
> + cpu_relax();

What's the point of this?

> +}
> +
> +int pm88x_reboot_notifier_callback(struct notifier_block *this,
> + unsigned long code, void *unused)
> +{
> + struct pm88x_chip *chip =
> + container_of(this, struct pm88x_chip, reboot_notifier);
> +
> + switch (code) {
> + case SYS_HALT:
> + case SYS_POWER_OFF:
> + dev_info(chip->dev, "system is down.\n");
> + break;
> + case SYS_RESTART:
> + default:
> + dev_info(chip->dev, "system will reboot.\n");
> + break;
> + }
> +
> + return 0;
> +}

This function is pointless.

> diff --git a/drivers/mfd/88pm88x-i2c.c b/drivers/mfd/88pm88x-i2c.c
> new file mode 100644
> index 0000000..36842ed
> --- /dev/null
> +++ b/drivers/mfd/88pm88x-i2c.c
> @@ -0,0 +1,167 @@
> +/*
> + * 88pm88x-i2c.c -- 88pm88x i2c bus interface
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/88pm886.h>
> +#include <linux/mfd/88pm880.h>
> +#include <linux/mfd/88pm88x.h>
> +
> +static int pm88x_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct pm88x_chip *chip;
> + struct device_node *node = client->dev.of_node;
> + int ret = 0;
> +
> + chip = pm88x_init_chip(client);

This function doesn't actually do any chip initialisation.

... and the code should be moved into here.

> + if (IS_ERR(chip)) {
> + ret = PTR_ERR(chip);
> + dev_err(chip->dev, "initialize 88pm88x chip fails!\n");

"Failed to initialise chip"

> + goto err;

Just return.

> + }
> +
> + ret = pm88x_parse_dt(node, chip);
> + if (ret < 0) {
> + dev_err(chip->dev, "parse dt fails!\n");

"Failed to parse DT"

> + goto err;

Just return.

> + }
> +
> + ret = pm88x_init_pages(chip);
> + if (ret) {
> + dev_err(chip->dev, "initialize 88pm88x pages fails!\n");

Etc ...

> + goto err;

Just return.

> + }
> +
> + ret = pm88x_post_init_chip(chip);
> + if (ret) {
> + dev_err(chip->dev, "post initialize 88pm88x fails!\n");
> + goto err;

Just return.

> + }
> +
> + ret = pm88x_irq_init(chip);
> + if (ret) {
> + dev_err(chip->dev, "initialize 88pm88x interrupt fails!\n");
> + goto err_init_irq;
> + }
> +
> + ret = pm88x_init_subdev(chip);
> + if (ret) {
> + dev_err(chip->dev, "initialize 88pm88x sub-device fails\n");
> + goto err_init_subdev;
> + }
> +
> + /* patch for PMIC chip itself */
> + ret = pm88x_apply_patch(chip);
> + if (ret) {
> + dev_err(chip->dev, "apply 88pm88x register patch fails\n");
> + goto err_apply_patch;
> + }
> +
> + /* fixup according PMIC stepping */

This comment doesn't make any sense.

> + ret = pm88x_stepping_fixup(chip);
> + if (ret) {
> + dev_err(chip->dev, "fixup according to chip stepping\n");
> + goto err_apply_patch;
> + }
> +
> + /* patch for board configuration */
> + ret = pm88x_apply_board_fixup(chip, node);
> + if (ret) {
> + dev_err(chip->dev, "apply 88pm88x register for board fails\n");
> + goto err_apply_patch;
> + }
> +
> + pm_power_off = pm88x_power_off;
> +
> + chip->reboot_notifier.notifier_call = pm88x_reboot_notifier_callback;
> + register_reboot_notifier(&(chip->reboot_notifier));
> +
> + return 0;
> +
> +err_apply_patch:
> + mfd_remove_devices(chip->dev);
> +err_init_subdev:
> + regmap_del_irq_chip(chip->irq, chip->irq_data);
> +err_init_irq:
> + pm800_exit_pages(chip);
> +err:

Remove this label.

> + return ret;
> +}
> +
> +static int pm88x_i2c_remove(struct i2c_client *i2c)
> +{
> + struct pm88x_chip *chip = dev_get_drvdata(&i2c->dev);
> + pm88x_dev_exit(chip);
> + return 0;
> +}

Checkpatch will warn you about this function.

> +static const struct i2c_device_id pm88x_i2c_id[] = {
> + { "88pm886", PM886 },
> + { "88pm880", PM880 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, pm88x_i2c_id);
> +
> +static int pm88x_i2c_suspend(struct device *dev)
> +{
> + return 0;
> +}
> +
> +static int pm88x_i2c_resume(struct device *dev)
> +{
> + return 0;
> +}

Why even supply them?

> +static SIMPLE_DEV_PM_OPS(pm88x_pm_ops, pm88x_i2c_suspend, pm88x_i2c_resume);
> +
> +static struct i2c_driver pm88x_i2c_driver = {
> + .driver = {
> + .name = "88pm88x",
> + .owner = THIS_MODULE,

Remove this line.

> + .pm = &pm88x_pm_ops,
> + .of_match_table = of_match_ptr(pm88x_of_match),
> + },
> + .probe = pm88x_i2c_probe,
> + .remove = pm88x_i2c_remove,
> + .id_table = pm88x_i2c_id,
> +};
> +
> +static int __init pm88x_i2c_init(void)
> +{
> + int ret;
> +
> + ret = i2c_add_driver(&pm88x_i2c_driver);
> + if (ret != 0) {
> + pr_err("88pm88x I2C registration failed %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +subsys_initcall(pm88x_i2c_init);
> +
> +static void __exit pm88x_i2c_exit(void)
> +{
> + i2c_del_driver(&pm88x_i2c_driver);
> +}
> +module_exit(pm88x_i2c_exit);

I think you want to remove all of this and replace with
module_i2c_driver().

> +MODULE_DESCRIPTION("88pm88x I2C bus interface");
> +MODULE_AUTHOR("Yi Zhang<[email protected]>");

Missing a space.

> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mfd/88pm88x-irq.c b/drivers/mfd/88pm88x-irq.c
> new file mode 100644
> index 0000000..0126df0
> --- /dev/null
> +++ b/drivers/mfd/88pm88x-irq.c
> @@ -0,0 +1,171 @@
> +/*
> + * 88pm886 interrupt support
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/mfd/88pm88x.h>
> +#include <linux/mfd/88pm886.h>
> +#include <linux/mfd/88pm880.h>
> +
> +/* interrupt status registers */
> +#define PM88X_INT_STATUS1 (0x05)
> +
> +#define PM88X_INT_ENA_1 (0x0a)
> +#define PM88X_ONKEY_INT_ENA1 (1 << 0)
> +#define PM88X_EXTON_INT_ENA1 (1 << 1)
> +#define PM88X_CHG_INT_ENA1 (1 << 2)
> +#define PM88X_BAT_INT_ENA1 (1 << 3)
> +#define PM88X_RTC_INT_ENA1 (1 << 4)
> +#define PM88X_CLASSD_INT_ENA1 (1 << 5)
> +#define PM88X_XO_INT_ENA1 (1 << 6)
> +#define PM88X_GPIO_INT_ENA1 (1 << 7)
> +
> +#define PM88X_INT_ENA_2 (0x0b)
> +#define PM88X_VBAT_INT_ENA2 (1 << 0)
> +#define PM88X_RSVED1_INT_ENA2 (1 << 1)
> +#define PM88X_VBUS_INT_ENA2 (1 << 2)
> +#define PM88X_ITEMP_INT_ENA2 (1 << 3)
> +#define PM88X_BUCK_PGOOD_INT_ENA2 (1 << 4)
> +#define PM88X_LDO_PGOOD_INT_ENA2 (1 << 5)
> +#define PM88X_RSVED6_INT_ENA2 (1 << 6)
> +#define PM88X_RSVED7_INT_ENA2 (1 << 7)
> +
> +#define PM88X_INT_ENA_3 (0x0c)
> +#define PM88X_GPADC0_INT_ENA3 (1 << 0)
> +#define PM88X_GPADC1_INT_ENA3 (1 << 1)
> +#define PM88X_GPADC2_INT_ENA3 (1 << 2)
> +#define PM88X_GPADC3_INT_ENA3 (1 << 3)
> +#define PM88X_MIC_INT_ENA3 (1 << 4)
> +#define PM88X_HS_INT_ENA3 (1 << 5)
> +#define PM88X_GND_INT_ENA3 (1 << 6)
> +#define PM88X_RSVED7_INT_ENA3 (1 << 7)
> +
> +#define PM88X_INT_ENA_4 (0x0d)
> +#define PM88X_CHG_FAIL_INT_ENA4 (1 << 0)
> +#define PM88X_CHG_DONE_INT_ENA4 (1 << 1)
> +#define PM88X_RSVED2_INT_ENA4 (1 << 2)
> +#define PM88X_OTG_FAIL_INT_ENA4 (1 << 3)
> +#define PM88X_RSVED4_INT_ENA4 (1 << 4)
> +#define PM88X_CHG_ILIM_INT_ENA4 (1 << 5)
> +#define PM88X_CC_INT_ENA4 (1 << 6)
> +#define PM88X_RSVED7_INT_ENA4 (1 << 7)
> +
> +#define PM88X_MISC_CONFIG2 (0x15)
> +#define PM88X_INV_INT (1 << 0)
> +#define PM88X_INT_CLEAR (1 << 1)
> +#define PM88X_INT_RC (0 << 1)
> +#define PM88X_INT_WC (1 << 1)
> +#define PM88X_INT_MASK_MODE (1 << 2)

Replace all "1 <<" with BIT()

> +static const struct regmap_irq pm88x_irqs[] = {
> + /* INT0 */
> + [PM88X_IRQ_ONKEY] = {.reg_offset = 0, .mask = PM88X_ONKEY_INT_ENA1,},
> + [PM88X_IRQ_EXTON] = {.reg_offset = 0, .mask = PM88X_EXTON_INT_ENA1,},
> + [PM88X_IRQ_CHG_GOOD] = {.reg_offset = 0, .mask = PM88X_CHG_INT_ENA1,},
> + [PM88X_IRQ_BAT_DET] = {.reg_offset = 0, .mask = PM88X_BAT_INT_ENA1,},
> + [PM88X_IRQ_RTC] = {.reg_offset = 0, .mask = PM88X_RTC_INT_ENA1,},
> + [PM88X_IRQ_CLASSD] = { .reg_offset = 0, .mask = PM88X_CLASSD_INT_ENA1,},
> + [PM88X_IRQ_XO] = {.reg_offset = 0, .mask = PM88X_XO_INT_ENA1,},
> + [PM88X_IRQ_GPIO] = {.reg_offset = 0, .mask = PM88X_GPIO_INT_ENA1,},
> +
> + /* INT1 */
> + [PM88X_IRQ_VBAT] = {.reg_offset = 1, .mask = PM88X_VBAT_INT_ENA2,},
> + [PM88X_IRQ_VBUS] = {.reg_offset = 1, .mask = PM88X_VBUS_INT_ENA2,},
> + [PM88X_IRQ_ITEMP] = {.reg_offset = 1, .mask = PM88X_ITEMP_INT_ENA2,},
> + [PM88X_IRQ_BUCK_PGOOD] = {
> + .reg_offset = 1,
> + .mask = PM88X_BUCK_PGOOD_INT_ENA2,
> + },
> + [PM88X_IRQ_LDO_PGOOD] = {
> + .reg_offset = 1,
> + .mask = PM88X_LDO_PGOOD_INT_ENA2,
> + },
> + /* INT2 */
> + [PM88X_IRQ_GPADC0] = {.reg_offset = 2, .mask = PM88X_GPADC0_INT_ENA3,},
> + [PM88X_IRQ_GPADC1] = {.reg_offset = 2, .mask = PM88X_GPADC1_INT_ENA3,},
> + [PM88X_IRQ_GPADC2] = {.reg_offset = 2, .mask = PM88X_GPADC2_INT_ENA3,},
> + [PM88X_IRQ_GPADC3] = {.reg_offset = 2, .mask = PM88X_GPADC3_INT_ENA3,},
> + [PM88X_IRQ_MIC_DET] = {.reg_offset = 2, .mask = PM88X_MIC_INT_ENA3,},
> + [PM88X_IRQ_HS_DET] = {.reg_offset = 2, .mask = PM88X_HS_INT_ENA3,},
> + [PM88X_IRQ_GND_DET] = {.reg_offset = 2, .mask = PM88X_GND_INT_ENA3,},

This is not how we lay out structures (your code below is correct).

> + /* INT3 */
> + [PM88X_IRQ_CHG_FAIL] = {
> + .reg_offset = 3,
> + .mask = PM88X_CHG_FAIL_INT_ENA4,
> + },
> + [PM88X_IRQ_CHG_DONE] = {
> + .reg_offset = 3,
> + .mask = PM88X_CHG_DONE_INT_ENA4,
> + },
> + [PM88X_IRQ_OTG_FAIL] = {
> + .reg_offset = 3,
> + .mask = PM88X_OTG_FAIL_INT_ENA4,
> + },
> + [PM88X_IRQ_CHG_ILIM] = {
> + .reg_offset = 3,
> + .mask = PM88X_CHG_ILIM_INT_ENA4,
> + },
> + [PM88X_IRQ_CC] = {.reg_offset = 3, .mask = PM88X_CC_INT_ENA4,},
> +};
> +
> +struct regmap_irq_chip pm88x_irq_chip = {
> + .name = "88pm88x",
> + .irqs = pm88x_irqs,
> + .num_irqs = ARRAY_SIZE(pm88x_irqs),
> +

No need for the '\n'.

> + .num_regs = 4,
> + .status_base = PM88X_INT_STATUS1,
> + .mask_base = PM88X_INT_ENA_1,
> + .ack_base = PM88X_INT_STATUS1,
> + .mask_invert = 1,
> +};
> +
> +int pm88x_irq_init(struct pm88x_chip *chip)
> +{
> + int mask, data, ret;
> + struct regmap *map;

Not sure this variable is even needed, but if you want to keep it,
please rename to regmap.

> + if (!chip || !chip->base_regmap || !chip->irq) {
> + pr_err("cannot initialize interrupt!\n");
> + return -EINVAL;
> + }
> + map = chip->base_regmap;
> +
> + /*
> + * irq_mode defines the way of clearing interrupt.
> + * it's read-clear by default.
> + */
> + mask = PM88X_INV_INT | PM88X_INT_CLEAR | PM88X_INT_MASK_MODE;
> + data = (chip->irq_mode) ? PM88X_INT_WC : PM88X_INT_RC;
> + ret = regmap_update_bits(map, PM88X_MISC_CONFIG2, mask, data);
> + if (ret < 0) {
> + dev_err(chip->dev, "cannot set interrupt mode!\n");
> + return ret;
> + }
> +
> + ret = regmap_add_irq_chip(map, chip->irq, IRQF_ONESHOT, -1,
> + &pm88x_irq_chip, &chip->irq_data);
> + return ret;
> +}
> +
> +int pm88x_irq_exit(struct pm88x_chip *chip)
> +{
> + regmap_del_irq_chip(chip->irq, chip->irq_data);
> + return 0;
> +}
> diff --git a/drivers/mfd/88pm88x.h b/drivers/mfd/88pm88x.h
> new file mode 100644
> index 0000000..a85a486
> --- /dev/null
> +++ b/drivers/mfd/88pm88x.h
> @@ -0,0 +1,51 @@
> +#ifndef _MFD_88PM88X_H
> +#define _MFD_88PM88X_H
> +
> +#include <linux/kernel.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/core.h>
> +
> +struct pmic_cell_info {
> + const struct mfd_cell *cells;
> + int cell_nr;
> +};

No need for this, please move it.

> +#define CELL_IRQ_RESOURCE(_name, _irq) { \
> + .name = _name, \
> + .start = _irq, .end = _irq, \
> + .flags = IORESOURCE_IRQ, \
> + }

This already exists, DEFINE_RES*.

> +#define CELL_DEV(_name, _r, _compatible, _id) { \
> + .name = _name, \
> + .of_compatible = _compatible, \
> + .num_resources = ARRAY_SIZE(_r), \
> + .resources = _r, \
> + .id = _id, \
> + }

This is not required.

If you feel the need for an MFD Cell macro, you probably have too many
MFD cells and you have done something wrong..

> +/* 88pm886 */
> +extern const struct regmap_config pm886_base_i2c_regmap;
> +extern const struct regmap_config pm886_power_i2c_regmap;
> +extern const struct regmap_config pm886_gpadc_i2c_regmap;
> +extern const struct regmap_config pm886_battery_i2c_regmap;
> +extern const struct regmap_config pm886_test_i2c_regmap;
> +
> +extern const struct mfd_cell pm886_cell_devs[];
> +extern struct pmic_cell_info pm886_cell_info;
> +
> +int pm886_apply_patch(struct pm88x_chip *chip);
> +
> +/* 88pm880 */
> +extern const struct regmap_config pm880_base_i2c_regmap;
> +extern const struct regmap_config pm880_power_i2c_regmap;
> +extern const struct regmap_config pm880_gpadc_i2c_regmap;
> +extern const struct regmap_config pm880_battery_i2c_regmap;
> +extern const struct regmap_config pm880_test_i2c_regmap;
> +
> +extern const struct mfd_cell pm880_cell_devs[];
> +extern struct pmic_cell_info pm880_cell_info;
> +
> +int pm880_apply_patch(struct pm88x_chip *chip);

Place all of your code in the correct files and all these horrible
global externs should vanish.

> +#endif
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index d5ad04d..bdebca9 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -390,6 +390,18 @@ config MFD_KEMPLD
> This driver can also be built as a module. If so, the module
> will be called kempld-core.
>
> +config MFD_88PM88X
> + tristate "Marvell 88PM886/880 PMIC"
> + depends on I2C=y
> + select REGMAP_I2C
> + select MFD_CORE
> + help
> + This supports for Marvell 88PM88X Series Power Management IC:
> + 88pm886 and 88pm880;
> + This includes the I2C driver, the interrupt resource distribution
> + and the core APIs, for individual sub-device as voltage regulators,
> + RTC, charger, fuelgauge, etc, select under the corresponding menus.
> +
> config MFD_88PM800
> tristate "Marvell 88PM800"
> depends on I2C=y
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 0e5cfeb..365d1fa 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -6,6 +6,9 @@
> obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
> obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
> obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
> +88pm88x-objs := 88pm88x-core.o 88pm88x-i2c.o 88pm88x-irq.o 88pm886-table.o 88pm880-table.o
> +obj-$(CONFIG_MFD_88PM88X) += 88pm88x.o
> +
> obj-$(CONFIG_MFD_SM501) += sm501.o
> obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
> obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
> diff --git a/include/linux/mfd/88pm880-reg.h b/include/linux/mfd/88pm880-reg.h
> new file mode 100644
> index 0000000..fdf2315
> --- /dev/null
> +++ b/include/linux/mfd/88pm880-reg.h

You don't need a seperate header file for register values. Just put
them in include/linux/mfd/88pm880.h.

> @@ -0,0 +1,98 @@
> +/*
> + * Marvell 88PM880 registers
> + *
> + * Copyright (C) 2014 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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_88PM880_REG_H
> +#define __LINUX_MFD_88PM880_REG_H
> +
> +#define PM880_BUCK1_VOUT (0x28)
> +
> +#define PM880_BUCK1A_VOUT (0x28) /* voltage 0 */
> +#define PM880_BUCK1A_1_VOUT (0x29)
> +#define PM880_BUCK1A_2_VOUT (0x2a)
> +#define PM880_BUCK1A_3_VOUT (0x2b)
> +#define PM880_BUCK1A_4_VOUT (0x2c)
> +#define PM880_BUCK1A_5_VOUT (0x2d)
> +#define PM880_BUCK1A_6_VOUT (0x2e)
> +#define PM880_BUCK1A_7_VOUT (0x2f)
> +#define PM880_BUCK1A_8_VOUT (0x30)
> +#define PM880_BUCK1A_9_VOUT (0x31)
> +#define PM880_BUCK1A_10_VOUT (0x32)
> +#define PM880_BUCK1A_11_VOUT (0x33)
> +#define PM880_BUCK1A_12_VOUT (0x34)
> +#define PM880_BUCK1A_13_VOUT (0x35)
> +#define PM880_BUCK1A_14_VOUT (0x36)
> +#define PM880_BUCK1A_15_VOUT (0x37)
> +
> +#define PM880_BUCK1B_VOUT (0x40)
> +#define PM880_BUCK1B_1_VOUT (0x41)
> +#define PM880_BUCK1B_2_VOUT (0x42)
> +#define PM880_BUCK1B_3_VOUT (0x43)
> +#define PM880_BUCK1B_4_VOUT (0x44)
> +#define PM880_BUCK1B_5_VOUT (0x45)
> +#define PM880_BUCK1B_6_VOUT (0x46)
> +#define PM880_BUCK1B_7_VOUT (0x47)
> +#define PM880_BUCK1B_8_VOUT (0x48)
> +#define PM880_BUCK1B_9_VOUT (0x49)
> +#define PM880_BUCK1B_10_VOUT (0x4a)
> +#define PM880_BUCK1B_11_VOUT (0x4b)
> +#define PM880_BUCK1B_12_VOUT (0x4c)
> +#define PM880_BUCK1B_13_VOUT (0x4d)
> +#define PM880_BUCK1B_14_VOUT (0x4e)
> +#define PM880_BUCK1B_15_VOUT (0x4f)
> +
> +/* buck7 has dvc function */
> +#define PM880_BUCK7_VOUT (0xb8) /* voltage 0 */
> +#define PM880_BUCK7_1_VOUT (0xb9)
> +#define PM880_BUCK7_2_VOUT (0xba)
> +#define PM880_BUCK7_3_VOUT (0xbb)
> +
> +/*
> + * buck sleep mode control registers:
> + * 00-disable,
> + * 01/10-sleep voltage,
> + * 11-active voltage
> + */
> +#define PM880_BUCK1A_SLP_CTRL (0x27)
> +#define PM880_BUCK1B_SLP_CTRL (0x3c)
> +#define PM880_BUCK2_SLP_CTRL (0x54)
> +#define PM880_BUCK3_SLP_CTRL (0x6c)
> +/* TODO: there are 7 controls bit for buck4~7 */
> +#define PM880_BUCK4_SLP_CTRL (0x84)
> +#define PM880_BUCK5_SLP_CTRL (0x94)
> +#define PM880_BUCK6_SLP_CTRL (0xa4)
> +#define PM880_BUCK7_SLP_CTRL (0xb4)
> +
> +/*
> + * ldo sleep mode control registers:
> + * 00-disable,
> + * 01/10-sleep voltage,
> + * 11-active voltage
> + */
> +#define PM880_LDO1_SLP_CTRL (0x21)
> +#define PM880_LDO2_SLP_CTRL (0x27)
> +#define PM880_LDO3_SLP_CTRL (0x2d)
> +#define PM880_LDO4_SLP_CTRL (0x33)
> +#define PM880_LDO5_SLP_CTRL (0x39)
> +#define PM880_LDO6_SLP_CTRL (0x3f)
> +#define PM880_LDO7_SLP_CTRL (0x45)
> +#define PM880_LDO8_SLP_CTRL (0x4b)
> +#define PM880_LDO9_SLP_CTRL (0x51)
> +#define PM880_LDO10_SLP_CTRL (0x57)
> +#define PM880_LDO11_SLP_CTRL (0x5d)
> +#define PM880_LDO12_SLP_CTRL (0x63)
> +#define PM880_LDO13_SLP_CTRL (0x69)
> +#define PM880_LDO14_SLP_CTRL (0x6f)
> +#define PM880_LDO15_SLP_CTRL (0x75)
> +#define PM880_LDO16_SLP_CTRL (0x7b)
> +#define PM880_LDO17_SLP_CTRL (0x81)
> +#define PM880_LDO18_SLP_CTRL (0x87)

Remove the brackets around all of the above.

> +#endif /*__LINUX_MFD_88PM880_REG_H */
> diff --git a/include/linux/mfd/88pm880.h b/include/linux/mfd/88pm880.h
> new file mode 100644
> index 0000000..94b9e063
> --- /dev/null
> +++ b/include/linux/mfd/88pm880.h
> @@ -0,0 +1,59 @@
> +/*
> + * Marvell 88PM880 Interface
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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.
> + *
> + * 88pm880 specific configuration: at present it's regulators and dvc part
> + */
> +
> +#ifndef __LINUX_MFD_88PM880_H
> +#define __LINUX_MFD_88PM880_H
> +
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +#include <linux/atomic.h>
> +#include <linux/reboot.h>

Why are these required?

> +#include "88pm880-reg.h"

<linux/mfd/88pm880.h>

> +enum {
> + PM880_ID_BUCK1A = 0,
> + PM880_ID_BUCK2,
> + PM880_ID_BUCK3,
> + PM880_ID_BUCK4,
> + PM880_ID_BUCK5,
> + PM880_ID_BUCK6,
> + PM880_ID_BUCK7,
> +
> + PM880_ID_BUCK_MAX = 7,
> +};
> +
> +enum {
> + PM880_ID_LDO1 = 0,
> + PM880_ID_LDO2,
> + PM880_ID_LDO3,
> + PM880_ID_LDO4,
> + PM880_ID_LDO5,
> + PM880_ID_LDO6,
> + PM880_ID_LDO7,
> + PM880_ID_LDO8,
> + PM880_ID_LDO9,
> + PM880_ID_LDO10,
> + PM880_ID_LDO11,
> + PM880_ID_LDO12,
> + PM880_ID_LDO13,
> + PM880_ID_LDO14 = 13,
> + PM880_ID_LDO15,
> + PM880_ID_LDO16 = 15,
> +
> + PM880_ID_LDO17 = 16,
> + PM880_ID_LDO18 = 17,
> +
> + PM880_ID_LDO_MAX = 18,
> +};
> +#endif /* __LINUX_MFD_88PM880_H */
> diff --git a/include/linux/mfd/88pm886-reg.h b/include/linux/mfd/88pm886-reg.h
> new file mode 100644
> index 0000000..38a7ecd
> --- /dev/null
> +++ b/include/linux/mfd/88pm886-reg.h
> @@ -0,0 +1,59 @@
> +/*
> + * Marvell 88PM886 registers
> + *
> + * Copyright (C) 2014 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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_88PM886_REG_H
> +#define __LINUX_MFD_88PM886_REG_H
> +
> +#define PM886_BUCK1_VOUT (0xa5)
> +#define PM886_BUCK1_1_VOUT (0xa6)
> +#define PM886_BUCK1_2_VOUT (0xa7)
> +#define PM886_BUCK1_3_VOUT (0xa8)
> +#define PM886_BUCK1_4_VOUT (0x9a)
> +#define PM886_BUCK1_5_VOUT (0x9b)
> +#define PM886_BUCK1_6_VOUT (0x9c)
> +#define PM886_BUCK1_7_VOUT (0x9d)
> +
> +/*
> + * buck sleep mode control registers:
> + * 00-disable,
> + * 01/10-sleep voltage,
> + * 11-active voltage
> + */
> +#define PM886_BUCK1_SLP_CTRL (0xa2)
> +#define PM886_BUCK2_SLP_CTRL (0xb0)
> +#define PM886_BUCK3_SLP_CTRL (0xbe)
> +#define PM886_BUCK4_SLP_CTRL (0xcc)
> +#define PM886_BUCK5_SLP_CTRL (0xda)
> +
> +/*
> + * ldo sleep mode control registers:
> + * 00-disable,
> + * 01/10-sleep voltage,
> + * 11-active voltage
> + */
> +#define PM886_LDO1_SLP_CTRL (0x21)
> +#define PM886_LDO2_SLP_CTRL (0x27)
> +#define PM886_LDO3_SLP_CTRL (0x2d)
> +#define PM886_LDO4_SLP_CTRL (0x33)
> +#define PM886_LDO5_SLP_CTRL (0x39)
> +#define PM886_LDO6_SLP_CTRL (0x3f)
> +#define PM886_LDO7_SLP_CTRL (0x45)
> +#define PM886_LDO8_SLP_CTRL (0x4b)
> +#define PM886_LDO9_SLP_CTRL (0x51)
> +#define PM886_LDO10_SLP_CTRL (0x57)
> +#define PM886_LDO11_SLP_CTRL (0x5d)
> +#define PM886_LDO12_SLP_CTRL (0x63)
> +#define PM886_LDO13_SLP_CTRL (0x69)
> +#define PM886_LDO14_SLP_CTRL (0x6f)
> +#define PM886_LDO15_SLP_CTRL (0x75)
> +#define PM886_LDO16_SLP_CTRL (0x7b)
> +
> +#endif
> diff --git a/include/linux/mfd/88pm886.h b/include/linux/mfd/88pm886.h
> new file mode 100644
> index 0000000..9390406
> --- /dev/null
> +++ b/include/linux/mfd/88pm886.h
> @@ -0,0 +1,55 @@
> +/*
> + * Marvell 88PM886 Interface
> + *
> + * Copyright (C) 2015 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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.
> + *
> + * 88pm886 specific configuration: at present it's regulators and dvc part
> + */
> +
> +#ifndef __LINUX_MFD_88PM886_H
> +#define __LINUX_MFD_88PM886_H
> +
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +#include <linux/atomic.h>
> +#include <linux/reboot.h>
> +#include "88pm886-reg.h"
> +
> +enum {
> + PM886_ID_BUCK1 = 0,
> + PM886_ID_BUCK2,
> + PM886_ID_BUCK3,
> + PM886_ID_BUCK4,
> + PM886_ID_BUCK5,
> +
> + PM886_ID_BUCK_MAX = 5,
> +};
> +
> +enum {
> + PM886_ID_LDO1 = 0,
> + PM886_ID_LDO2,
> + PM886_ID_LDO3,
> + PM886_ID_LDO4,
> + PM886_ID_LDO5,
> + PM886_ID_LDO6,
> + PM886_ID_LDO7,
> + PM886_ID_LDO8,
> + PM886_ID_LDO9,
> + PM886_ID_LDO10,
> + PM886_ID_LDO11,
> + PM886_ID_LDO12,
> + PM886_ID_LDO13,
> + PM886_ID_LDO14,
> + PM886_ID_LDO15,
> + PM886_ID_LDO16 = 15,
> +
> + PM886_ID_LDO_MAX = 16,
> +};
> +
> +#endif /* __LINUX_MFD_88PM886_H */
> diff --git a/include/linux/mfd/88pm88x-reg.h b/include/linux/mfd/88pm88x-reg.h
> new file mode 100644
> index 0000000..d767b31
> --- /dev/null
> +++ b/include/linux/mfd/88pm88x-reg.h
> @@ -0,0 +1,118 @@
> +/*
> + * Marvell 88PM88X registers
> + *
> + * Copyright (C) 2014 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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_88PM88X_REG_H
> +#define __LINUX_MFD_88PM88X_REG_H
> +/*
> + * This file is just used for the common registers,
> + * which are shared by sub-clients
> + */
> +
> +/*--base page:--------------------------------------------------------------*/

All of the commenting above is superfluous.

> +#define PM88X_ID_REG (0x0)
> +
> +#define PM88X_STATUS1 (0x1)
> +#define PM88X_CHG_DET (1 << 2)
> +#define PM88X_BAT_DET (1 << 3)
> +
> +#define PM88X_MISC_CONFIG1 (0x14)
> +#define PM88X_LONKEY_RST (1 << 3)
> +
> +#define PM88X_WDOG (0x1d)
> +
> +#define PM88X_LOWPOWER2 (0x21)
> +#define PM88X_LOWPOWER4 (0x23)
> +
> +/* clk control register */
> +#define PM88X_CLK_CTRL1 (0x25)
> +
> +/* gpio */
> +#define PM88X_GPIO_CTRL1 (0x30)
> +#define PM88X_GPIO0_VAL_MSK (0x1 << 0)
> +#define PM88X_GPIO0_MODE_MSK (0x7 << 1)
> +#define PM88X_GPIO1_VAL_MSK (0x1 << 4)
> +#define PM88X_GPIO1_MODE_MSK (0x7 << 5)
> +#define PM88X_GPIO1_SET_DVC (0x2 << 5)
> +
> +#define PM88X_GPIO_CTRL2 (0x31)
> +#define PM88X_GPIO2_VAL_MSK (0x1 << 0)
> +#define PM88X_GPIO2_MODE_MSK (0x7 << 1)
> +
> +#define PM88X_GPIO_CTRL3 (0x32)
> +
> +#define PM88X_GPIO_CTRL4 (0x33)
> +#define PM88X_GPIO5V_1_VAL_MSK (0x1 << 0)
> +#define PM88X_GPIO5V_1_MODE_MSK (0x7 << 1)
> +#define PM88X_GPIO5V_2_VAL_MSK (0x1 << 4)
> +#define PM88X_GPIO5V_2_MODE_MSK (0x7 << 5)
> +
> +#define PM88X_BK_OSC_CTRL1 (0x50)
> +#define PM88X_BK_OSC_CTRL3 (0x52)
> +
> +#define PM88X_RTC_ALARM_CTRL1 (0xd0)
> +#define PM88X_ALARM_WAKEUP (1 << 4)
> +#define PM88X_USE_XO (1 << 7)
> +
> +#define PM88X_AON_CTRL2 (0xe2)
> +#define PM88X_AON_CTRL3 (0xe3)
> +#define PM88X_AON_CTRL4 (0xe4)
> +#define PM88X_AON_CTRL7 (0xe7)
> +
> +/* 0xea, 0xeb, 0xec, 0xed are reserved by RTC */
> +#define PM88X_RTC_SPARE5 (0xee)
> +#define PM88X_RTC_SPARE6 (0xef)
> +/*-------------------------------------------------------------------------*/
> +
> +/*--power page:------------------------------------------------------------*/
> +
> +/*-------------------------------------------------------------------------*/
> +
> +/*--gpadc page:------------------------------------------------------------*/

This type of commenting is messy and unwanted.

> +#define PM88X_GPADC_CONFIG1 (0x1)
> +
> +#define PM88X_GPADC_CONFIG2 (0x2)
> +#define PM88X_GPADC0_MEAS_EN (1 << 2)
> +#define PM88X_GPADC1_MEAS_EN (1 << 3)
> +#define PM88X_GPADC2_MEAS_EN (1 << 4)
> +#define PM88X_GPADC3_MEAS_EN (1 << 5)
> +
> +#define PM88X_GPADC_CONFIG3 (0x3)
> +
> +#define PM88X_GPADC_CONFIG6 (0x6)
> +#define PM88X_GPADC_CONFIG8 (0x8)
> +
> +#define PM88X_GPADC0_LOW_TH (0x20)
> +#define PM88X_GPADC1_LOW_TH (0x21)
> +#define PM88X_GPADC2_LOW_TH (0x22)
> +#define PM88X_GPADC3_LOW_TH (0x23)
> +
> +#define PM88X_GPADC0_UPP_TH (0x30)
> +#define PM88X_GPADC1_UPP_TH (0x31)
> +#define PM88X_GPADC2_UPP_TH (0x32)
> +#define PM88X_GPADC3_UPP_TH (0x33)
> +
> +#define PM88X_VBUS_MEAS1 (0x4A)
> +#define PM88X_GPADC0_MEAS1 (0x54)
> +#define PM88X_GPADC1_MEAS1 (0x56)
> +#define PM88X_GPADC2_MEAS1 (0x58)
> +#define PM88X_GPADC3_MEAS1 (0x5A)
> +
> +
> +/*--charger page:------------------------------------------------------------*/
> +#define PM88X_CHG_CONFIG1 (0x28)
> +#define PM88X_CHGBK_CONFIG6 (0x50)
> +/*-------------------------------------------------------------------------*/
> +
> +/*--test page:-------------------------------------------------------------*/
> +
> +/*-------------------------------------------------------------------------*/
> +#endif
> diff --git a/include/linux/mfd/88pm88x.h b/include/linux/mfd/88pm88x.h
> new file mode 100644
> index 0000000..efa2fe6
> --- /dev/null
> +++ b/include/linux/mfd/88pm88x.h
> @@ -0,0 +1,202 @@
> +/*
> + * Marvell 88PM88X PMIC Common Interface
> + *
> + * Copyright (C) 2014 Marvell International Ltd.
> + * Yi Zhang <[email protected]>
> + *
> + * 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.
> + *
> + *
> + * This file configures the common part of the 88pm88x series PMIC
> + */
> +
> +#ifndef __LINUX_MFD_88PM88X_H
> +#define __LINUX_MFD_88PM88X_H
> +
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +#include <linux/atomic.h>
> +#include <linux/reboot.h>
> +#include "88pm88x-reg.h"
> +#include "88pm886-reg.h"

Why are all of these required?

> +#define PM88X_RTC_NAME "88pm88x-rtc"
> +#define PM88X_ONKEY_NAME "88pm88x-onkey"
> +#define PM88X_CHARGER_NAME "88pm88x-charger"
> +#define PM88X_BATTERY_NAME "88pm88x-battery"
> +#define PM88X_HEADSET_NAME "88pm88x-headset"
> +#define PM88X_VBUS_NAME "88pm88x-vbus"
> +#define PM88X_CFD_NAME "88pm88x-leds"
> +#define PM88X_RGB_NAME "88pm88x-rgb"
> +#define PM88X_GPADC_NAME "88pm88x-gpadc"
> +#define PM88X_DVC_NAME "88pm88x-dvc"

I would prefer that you just used the name directly.

> +enum pm88x_type {
> + PM886 = 1,
> + PM880 = 2,
> +};
> +
> +enum pm88x_pages {
> + PM88X_BASE_PAGE = 0,
> + PM88X_LDO_PAGE,
> + PM88X_GPADC_PAGE,
> + PM88X_BATTERY_PAGE,
> + PM88X_BUCK_PAGE = 4,

This is 4 anyway.

> + PM88X_TEST_PAGE = 7,
> +};
> +
> +enum pm88x_gpadc {
> + PM88X_NO_GPADC = -1,
> + PM88X_GPADC0 = 0,
> + PM88X_GPADC1,
> + PM88X_GPADC2,
> + PM88X_GPADC3,
> +};
> +
> +/* interrupt number */
> +enum pm88x_irq_number {
> + PM88X_IRQ_ONKEY, /* EN1b0 *//* 0 */

At least put a space between the comments.

> + PM88X_IRQ_EXTON, /* EN1b1 */
> + PM88X_IRQ_CHG_GOOD, /* EN1b2 */
> + PM88X_IRQ_BAT_DET, /* EN1b3 */
> + PM88X_IRQ_RTC, /* EN1b4 */
> + PM88X_IRQ_CLASSD, /* EN1b5 *//* 5 */
> + PM88X_IRQ_XO, /* EN1b6 */
> + PM88X_IRQ_GPIO, /* EN1b7 */
> +
> + PM88X_IRQ_VBAT, /* EN2b0 *//* 8 */
> + /* EN2b1 */
> + PM88X_IRQ_VBUS, /* EN2b2 */
> + PM88X_IRQ_ITEMP, /* EN2b3 *//* 10 */
> + PM88X_IRQ_BUCK_PGOOD, /* EN2b4 */
> + PM88X_IRQ_LDO_PGOOD, /* EN2b5 */
> +
> + PM88X_IRQ_GPADC0, /* EN3b0 */
> + PM88X_IRQ_GPADC1, /* EN3b1 */
> + PM88X_IRQ_GPADC2, /* EN3b2 *//* 15 */
> + PM88X_IRQ_GPADC3, /* EN3b3 */
> + PM88X_IRQ_MIC_DET, /* EN3b4 */
> + PM88X_IRQ_HS_DET, /* EN3b5 */
> + PM88X_IRQ_GND_DET, /* EN3b6 */
> +
> + PM88X_IRQ_CHG_FAIL, /* EN4b0 *//* 20 */
> + PM88X_IRQ_CHG_DONE, /* EN4b1 */
> + /* EN4b2 */
> + PM88X_IRQ_CFD_FAIL, /* EN4b3 */
> + PM88X_IRQ_OTG_FAIL, /* EN4b4 */
> + PM88X_IRQ_CHG_ILIM, /* EN4b5 *//* 25 */
> + /* EN4b6 */
> + PM88X_IRQ_CC, /* EN4b7 *//* 27 */
> +
> + PM88X_MAX_IRQ, /* 28 */
> +};
> +
> +/* 3 rgb led indicators */
> +enum {
> + PM88X_RGB_LED0,
> + PM88X_RGB_LED1,
> + PM88X_RGB_LED2,
> +};
> +
> +/* camera flash/torch */
> +enum {
> + PM88X_NO_LED = -1,
> + PM88X_FLASH_LED = 0,
> + PM88X_TORCH_LED,
> +};
> +
> +struct pm88x_dvc_ops {
> + void (*level_to_reg)(u8 level);
> +};

This appears to be unused.

> +struct pm88x_buck1_dvc_desc {
> + u8 current_reg;
> + int max_level;
> + int uV_step1;
> + int uV_step2;
> + int min_uV;
> + int mid_uV;
> + int max_uV;
> + int mid_reg_val;
> +};
>
> +struct pm88x_dvc {
> + struct device *dev;
> + struct pm88x_chip *chip;
> + struct pm88x_dvc_ops ops;
> + struct pm88x_buck1_dvc_desc desc;
> +};
> +
> +struct pm88x_chip {
> + struct i2c_client *client;
> + struct device *dev;
> +
> + struct i2c_client *ldo_page; /* chip client for ldo page */
> + struct i2c_client *power_page; /* chip client for power page */
> + struct i2c_client *gpadc_page; /* chip client for gpadc page */
> + struct i2c_client *battery_page;/* chip client for battery page */
> + struct i2c_client *buck_page; /* chip client for buck page */
> + struct i2c_client *test_page; /* chip client for test page */
> +
> + struct regmap *base_regmap;
> + struct regmap *ldo_regmap;
> + struct regmap *power_regmap;
> + struct regmap *gpadc_regmap;
> + struct regmap *battery_regmap;
> + struct regmap *buck_regmap;
> + struct regmap *test_regmap;
> + struct regmap *codec_regmap;
> +
> + unsigned short ldo_page_addr; /* ldo page I2C address */
> + unsigned short power_page_addr; /* power page I2C address */
> + unsigned short gpadc_page_addr; /* gpadc page I2C address */
> + unsigned short battery_page_addr;/* battery page I2C address */
> + unsigned short buck_page_addr; /* buck page I2C address */
> + unsigned short test_page_addr; /* test page I2C address */
> +
> + unsigned int chip_id;
> + long type; /* specific chip */
> + int irq;
> +
> + int irq_mode; /* write/read clear */
> + struct regmap_irq_chip_data *irq_data;
> +
> + bool rtc_wakeup; /* is it powered up by expired alarm? */
> + u8 powerdown1; /* save power down reason */
> + u8 powerdown2;
> + u8 powerup; /* the reason of power on */
> +
> + struct notifier_block reboot_notifier;
> + struct pm88x_dvc *dvc;
> +};

This is an awful lot of junk to be held in a device data structure.
Try to remove as much as you can.

> +extern struct regmap_irq_chip pm88x_irq_chip;
> +extern const struct of_device_id pm88x_of_match[];
> +
> +struct pm88x_chip *pm88x_init_chip(struct i2c_client *client);
> +int pm88x_parse_dt(struct device_node *np, struct pm88x_chip *chip);
> +
> +int pm88x_init_pages(struct pm88x_chip *chip);
> +int pm88x_post_init_chip(struct pm88x_chip *chip);
> +void pm800_exit_pages(struct pm88x_chip *chip);
> +
> +int pm88x_init_subdev(struct pm88x_chip *chip);
> +long pm88x_of_get_type(struct device *dev);
> +void pm88x_dev_exit(struct pm88x_chip *chip);
> +
> +int pm88x_irq_init(struct pm88x_chip *chip);
> +int pm88x_irq_exit(struct pm88x_chip *chip);
> +int pm88x_apply_patch(struct pm88x_chip *chip);
> +int pm88x_stepping_fixup(struct pm88x_chip *chip);
> +int pm88x_apply_board_fixup(struct pm88x_chip *chip, struct device_node *np);
> +
> +struct pm88x_chip *pm88x_get_chip(void);
> +void pm88x_set_chip(struct pm88x_chip *chip);
> +void pm88x_power_off(void);
> +int pm88x_reboot_notifier_callback(struct notifier_block *nb,
> + unsigned long code, void *unused);

All of these should probably be moved into a single file, unless you
have a good reason to spread them out?

> +#endif /* __LINUX_MFD_88PM88X_H */

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

2015-06-26 03:35:32

by Yi Zhang

[permalink] [raw]
Subject: Re: [PATCH V2 1/2] mfd: add Marvell 88pm88x description

On Tue, Jun 23, 2015 at 09:31:00AM -0500, Rob Herring wrote:
> On Fri, Jun 12, 2015 at 3:43 AM, Yi Zhang <[email protected]> wrote:
> > 88pm880 and 88pm886 are two combo PMIC chips, most of the function and the
> > register mapping are the same
>
> How do they compare to 80x/822/860 PMICs?

Hi, Rob:

88pm80x/822/860 and 88pm886/88pm880 are two different series:
- the register mapping is totally different, it's hard to reuse the
existed code for 88pm80x
- the latter is a chip which integrates the charger/fuelgauge/camera
flash which doesn't exist in 88pm80x
>
> >
> > Signed-off-by: Yi Zhang <[email protected]>
> > ---
> > Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 +++++++++++++++++++++++
> > 1 file changed, 33 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
> >
> > diff --git a/Documentation/devicetree/bindings/mfd/88pm88x.txt b/Documentation/devicetree/bindings/mfd/88pm88x.txt
> > new file mode 100644
> > index 0000000..72e741c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mfd/88pm88x.txt
> > @@ -0,0 +1,33 @@
> > +Marvell 88pm88x combo PMIC chip
> > +
> > +This series of chip integrates regulator, rtc, onkey, switch charger,
> > +fuelgauge, gpadc and a range of I/O pins.
> > +
> > +88pm886 and 88pm880 are two very similar chips, most of the registers mapping
> > +and functions are the same, the main difference is the latter has a separate
> > +i2c slave address to cover BUCK related setting
> > +
> > +Required properties:
> > +- compatible: one of the strings for a specific chip:
> > + "marvell,88pm886"
> > + "marvell,88pm880"
> > +- reg: the i2c address
> > +- interrupt-controller: it works as an interrupt controller managing its irqs
> > +- interrupt-cells: this value is 1
> > +
> > +Optional properties:
> > +- marvell,88pm88x-irq-write-clear: the interrupt ack method
>
> The 80x/860 binding needs the same property. Please coordinate this
> with Vaibhav.

Thanks for your reminder, got it
>
> Rob

2015-06-26 03:43:23

by Yi Zhang

[permalink] [raw]
Subject: Re: [PATCH V2 1/2] mfd: add Marvell 88pm88x description

On Tue, Jun 23, 2015 at 08:16:01PM +0530, Vaibhav Hiremath wrote:
>
>
> On Tuesday 23 June 2015 08:01 PM, Rob Herring wrote:
> >On Fri, Jun 12, 2015 at 3:43 AM, Yi Zhang <[email protected]> wrote:
> >>88pm880 and 88pm886 are two combo PMIC chips, most of the function and the
> >>register mapping are the same
> >
> >How do they compare to 80x/822/860 PMICs?
> >
>
> Zhang,
>
> Sorry, I missed this patch-series.
>
> Is there any way we can get access to datahsheets of all these devices?

Hi, Vaibhav:

https://extranet.marvell.com/ is a useful place to access to them
>
> >>
> >>Signed-off-by: Yi Zhang <[email protected]>
> >>---
> >> Documentation/devicetree/bindings/mfd/88pm88x.txt | 33 +++++++++++++++++++++++
> >> 1 file changed, 33 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/mfd/88pm88x.txt
> >>
> >>diff --git a/Documentation/devicetree/bindings/mfd/88pm88x.txt b/Documentation/devicetree/bindings/mfd/88pm88x.txt
> >>new file mode 100644
> >>index 0000000..72e741c
> >>--- /dev/null
> >>+++ b/Documentation/devicetree/bindings/mfd/88pm88x.txt
> >>@@ -0,0 +1,33 @@
> >>+Marvell 88pm88x combo PMIC chip
> >>+
> >>+This series of chip integrates regulator, rtc, onkey, switch charger,
> >>+fuelgauge, gpadc and a range of I/O pins.
> >>+
> >>+88pm886 and 88pm880 are two very similar chips, most of the registers mapping
> >>+and functions are the same, the main difference is the latter has a separate
> >>+i2c slave address to cover BUCK related setting
> >>+
> >>+Required properties:
> >>+- compatible: one of the strings for a specific chip:
> >>+ "marvell,88pm886"
> >>+ "marvell,88pm880"
> >>+- reg: the i2c address
> >>+- interrupt-controller: it works as an interrupt controller managing its irqs
> >>+- interrupt-cells: this value is 1
> >>+
> >>+Optional properties:
> >>+- marvell,88pm88x-irq-write-clear: the interrupt ack method
> >
> >The 80x/860 binding needs the same property. Please coordinate this
> >with Vaibhav.
> >
>
> Please refer to the patch
>
> https://lkml.org/lkml/2015/6/17/545

Thanks;
>
> Thanks,
> Vaibhav

2015-06-26 12:49:48

by Yi Zhang

[permalink] [raw]
Subject: Re: [PATCH V2 2/2] mfd: 88pm88x: initialize 88pm886/88pm880 base support

On Thu, Jun 25, 2015 at 09:32:48AM +0100, Lee Jones wrote:
> On Fri, 12 Jun 2015, Yi Zhang wrote:
>
> > 88pm886 and 88pm880 are combo PMIC chip, which integrates
> > regulator, onkey, rtc, gpadc, charger, fuelgauge function;
> >
> > this patch add the basic support for them, adding related resource, such as
> > interrupt, preparing for the client-device driver
> >
> > Signed-off-by: Yi Zhang <[email protected]>
> > ---
> > drivers/mfd/88pm880-table.c | 173 ++++++++++++
> > drivers/mfd/88pm886-table.c | 173 ++++++++++++
> > drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++++++++++++++++++++
> > drivers/mfd/88pm88x-i2c.c | 167 ++++++++++++
> > drivers/mfd/88pm88x-irq.c | 171 ++++++++++++
> > drivers/mfd/88pm88x.h | 51 ++++
>
Hi, Lee:

Thanks very much for your patience on such a long patch, it is my
fault, I will modify according to your precious comments and split
them in the next version, thanks very much;

> I'm initially concerned that you aren't (re)using _any_ of the 88pm*
> files already in the subsystem.

sorry for confusing you, this patch is for the 88pm886/88pm880, a new
series PMIC which is very different from the previous chips(88pm8*) in
register mapping and function;
after investigation, we tried to add this patch.
>
> > drivers/mfd/Kconfig | 12 +
> > drivers/mfd/Makefile | 3 +
> > include/linux/mfd/88pm880-reg.h | 98 +++++++
> > include/linux/mfd/88pm880.h | 59 ++++
> > include/linux/mfd/88pm886-reg.h | 59 ++++
> > include/linux/mfd/88pm886.h | 55 ++++
> > include/linux/mfd/88pm88x-reg.h | 118 ++++++++
> > include/linux/mfd/88pm88x.h | 202 ++++++++++++++
> > 14 files changed, 1925 insertions(+)
>
> Sending 2k line patches doesn't make reviewing a particularly pleasant
> experience. Please divide it up into a properly separated set. If
> you feel as though the set can't be split up, then you're probably
> coding it wrongly.

thanks, I will modify;
>
> > create mode 100644 drivers/mfd/88pm880-table.c
> > create mode 100644 drivers/mfd/88pm886-table.c
> > create mode 100644 drivers/mfd/88pm88x-core.c
> > create mode 100644 drivers/mfd/88pm88x-i2c.c
> > create mode 100644 drivers/mfd/88pm88x-irq.c
> > create mode 100644 drivers/mfd/88pm88x.h
> > create mode 100644 include/linux/mfd/88pm880-reg.h
> > create mode 100644 include/linux/mfd/88pm880.h
> > create mode 100644 include/linux/mfd/88pm886-reg.h
> > create mode 100644 include/linux/mfd/88pm886.h
> > create mode 100644 include/linux/mfd/88pm88x-reg.h
> > create mode 100644 include/linux/mfd/88pm88x.h
> >
> > diff --git a/drivers/mfd/88pm880-table.c b/drivers/mfd/88pm880-table.c
> > new file mode 100644
> > index 0000000..28ca860
> > --- /dev/null
> > +++ b/drivers/mfd/88pm880-table.c
> > @@ -0,0 +1,173 @@
> > +/*
> > + * Marvell 88PM880 specific setting
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
>
> Tea Maker, Project Manger, CEO, Janitor?
>
> Or "Author: "

My fault, thanks;
>
> > + * 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/kernel.h>
> > +#include <linux/device.h>
> > +#include <linux/mfd/88pm88x.h>
> > +#include <linux/mfd/88pm880.h>
> > +#include <linux/mfd/88pm880-reg.h>
> > +#include <linux/mfd/88pm88x-reg.h>
> > +
> > +#include "88pm88x.h"
> > +
> > +#define PM880_BUCK_NAME "88pm880-buck"
> > +#define PM880_LDO_NAME "88pm880-ldo"
> > +
> > +const struct regmap_config pm880_base_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm880_power_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm880_gpadc_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm880_battery_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm880_test_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +static const struct resource buck_resources[] = {
> > + {
> > + .name = PM880_BUCK_NAME,
>
> Tabbing.

Got it;
>
> > + },
> > +};
> > +
> > +static const struct resource ldo_resources[] = {
> > + {
> > + .name = PM880_LDO_NAME,
>
> Tabbing.

Yes;
>
> > + },
> > +};
> > +
> > +const struct mfd_cell pm880_cell_devs[] = {
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck1a", 0),
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck2", 1),
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck3", 2),
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck4", 3),
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck5", 4),
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck6", 5),
> > + CELL_DEV(PM880_BUCK_NAME, buck_resources, "marvell,88pm880-buck7", 6),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo1", 7),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo2", 8),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo3", 9),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo4", 10),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo5", 11),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo6", 12),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo7", 13),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo8", 14),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo9", 15),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo10", 16),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo11", 17),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo12", 18),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo13", 19),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo14", 20),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo15", 21),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo16", 22),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo17", 23),
> > + CELL_DEV(PM880_LDO_NAME, ldo_resources, "marvell,88pm880-ldo18", 24),
> > +};
>
> You don't want an MFD for each regulator. See the DT documentation
> for regulators.

OK, I will change
>
> > +struct pmic_cell_info pm880_cell_info = {
> > + .cells = pm880_cell_devs,
> > + .cell_nr = ARRAY_SIZE(pm880_cell_devs),
> > +};
>
> No need for this, please remove it.
Yes, you are right, if I change to use one MFD for all of the
regulators
>
> > +static const struct reg_default pm880_base_patch[] = {
> > + {PM88X_WDOG, 0x1}, /* disable watchdog */
>
> Pad it out like you did the others "0x01".
OK;
>
> > + {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
>
> kHz

Thanks very much for pointing it out; got it;
>
> > + {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
>
> Don't put bit names in comments -- you should define them instead.

OK
>
> > + {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
>
> Remove the bit name -- second part is fine.
Yes, you are right;
>
> > + /* enable LPM for internal reference group in sleep */
>
> I see you're not a fan of using capital letter to start a sentence?
..Yes, I just feel capital letter means "see me, I am important.."
OK, I will change to use it to start a sentence
>
> > + {PM88X_LOWPOWER4, 0xc0},
>
> No comment?
I will add it;
>
> > + {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
> > +};
>
> This would be easier to read if you lined it up:
>
> {PM88X_WDOG, 0x01}, /* disable watchdog */
> {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
> {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
> {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
> /* enable LPM for internal reference group in sleep */
> {PM88X_LOWPOWER4, 0xc0},
> {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
>
> ... don't you think?
>
> I also think the values should be defined. If you disagree, at the
> _very least_ please sharpen up the comments.

I will modify them, agree with you, macro is better
>
> > +static const struct reg_default pm880_power_patch[] = {
> > +};
>
> What's the point of this?

I just leave it here to get ready for the next patch, I got your
point, I should add it when I really use it.
thanks;
>
> > +static const struct reg_default pm880_gpadc_patch[] = {
> > + {PM88X_GPADC_CONFIG6, 0x03}, /* enable non-stop mode */
> > +};
>
> I personally prefer spaces after the first and before the second curly
> bracket.

Got it;
>
> > +static const struct reg_default pm880_battery_patch[] = {
> > + {PM88X_CHGBK_CONFIG6, 0xe1},
> > +};
>
> As above.
Yes;
>
> > +static const struct reg_default pm880_test_patch[] = {
> > +};
>
> Why do you have an empty struct const?
As for the above, I will remove it in this patch
>
> > +/* 88pm880 chip itself related */
>
> I have no idea what this means?
Sorry for confusing you, I mean the following setting tries to cover
the change related to chip itself, for the board specific requirement,
it should not be here;
I will modify this comments, thanks for pointing this out;
>
> > +int pm880_apply_patch(struct pm88x_chip *chip)
> > +{
> > + int ret, size;
> > +
> > + if (!chip || !chip->base_regmap || !chip->power_regmap ||
> > + !chip->gpadc_regmap || !chip->battery_regmap ||
> > + !chip->test_regmap)
> > + return -EINVAL;
>
> You already checked for this in pm88x_post_init_chip().

OK, I will remove;
>
> > + size = ARRAY_SIZE(pm880_base_patch);
> > + if (size == 0)
> > + goto power;
> > + ret = regmap_register_patch(chip->base_regmap, pm880_base_patch, size);
> > + if (ret < 0)
>
> I assume any non-zero return value is bad. If so, please change all
> of these too "if (ret)".

Yes, you are right, I will change
>
> > + return ret;
> > +
> > +power:
> > + size = ARRAY_SIZE(pm880_power_patch);
> > + if (size == 0)
> > + goto gpadc;
> > + ret = regmap_register_patch(chip->power_regmap, pm880_power_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +
> > +gpadc:
> > + size = ARRAY_SIZE(pm880_gpadc_patch);
> > + if (size == 0)
> > + goto battery;
> > + ret = regmap_register_patch(chip->gpadc_regmap, pm880_gpadc_patch, size);
> > + if (ret < 0)
> > + return ret;
>
> '\n'
Yes;
>
> > +battery:
> > + size = ARRAY_SIZE(pm880_battery_patch);
> > + if (size == 0)
> > + goto test;
> > + ret = regmap_register_patch(chip->battery_regmap, pm880_battery_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +
> > +test:
> > + size = ARRAY_SIZE(pm880_test_patch);
> > + if (size == 0)
> > + goto out;
> > + ret = regmap_register_patch(chip->test_regmap, pm880_test_patch, size);
> > + if (ret < 0)
> > + return ret;
>
> '\n'

Yes;
>
> > +out:
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pm880_apply_patch);
> > diff --git a/drivers/mfd/88pm886-table.c b/drivers/mfd/88pm886-table.c
> > new file mode 100644
> > index 0000000..897ee82
> > --- /dev/null
> > +++ b/drivers/mfd/88pm886-table.c
> > @@ -0,0 +1,173 @@
> > +/*
> > + * Marvell 88PM886 specific setting
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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/kernel.h>
> > +#include <linux/device.h>
> > +#include <linux/mfd/88pm88x.h>
> > +#include <linux/mfd/88pm886.h>
> > +#include <linux/mfd/88pm886-reg.h>
> > +#include <linux/mfd/88pm88x-reg.h>
> > +
> > +#include "88pm88x.h"
> > +
> > +#define PM886_BUCK_NAME "88pm886-buck"
> > +#define PM886_LDO_NAME "88pm886-ldo"
> > +
> > +const struct regmap_config pm886_base_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm886_power_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm886_gpadc_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm886_battery_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +const struct regmap_config pm886_test_i2c_regmap = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = 0xfe,
> > +};
> > +
> > +static const struct resource buck_resources[] = {
> > + {
> > + .name = PM886_BUCK_NAME,
> > + },
> > +};
> > +
> > +static const struct resource ldo_resources[] = {
> > + {
> > + .name = PM886_LDO_NAME,
> > + },
> > +};
> > +
> > +const struct mfd_cell pm886_cell_devs[] = {
> > + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck1", 0),
> > + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck2", 1),
> > + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck3", 2),
> > + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck4", 3),
> > + CELL_DEV(PM886_BUCK_NAME, buck_resources, "marvell,88pm886-buck5", 4),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo1", 5),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo2", 6),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo3", 7),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo4", 8),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo5", 9),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo6", 10),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo7", 11),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo8", 12),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo9", 13),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo10", 14),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo11", 15),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo12", 16),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo13", 17),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo14", 18),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo15", 19),
> > + CELL_DEV(PM886_LDO_NAME, ldo_resources, "marvell,88pm886-ldo16", 20),
> > +};
>
> You don't want an MFD for each regulator. See the DT documentation
> for regulators.

Yes;
>
> > +struct pmic_cell_info pm886_cell_info = {
> > + .cells = pm886_cell_devs,
> > + .cell_nr = ARRAY_SIZE(pm886_cell_devs),
> > +};
>
> No need for this, please remove it.
Yes;
>
> > +static const struct reg_default pm886_base_patch[] = {
> > + {PM88X_WDOG, 0x1}, /* disable watchdog */
> > + {PM88X_GPIO_CTRL1, 0x40}, /* gpio1: dvc , gpio0: input */
> > + {PM88X_GPIO_CTRL2, 0x00}, /* , gpio2: input */
> > + {PM88X_GPIO_CTRL3, 0x44}, /* dvc2 , dvc1 */
> > + {PM88X_GPIO_CTRL4, 0x00}, /* gpio5v_1:input, gpio5v_2: input*/
> > + {PM88X_AON_CTRL2, 0x2a}, /* output 32kHZ from XO */
> > + {PM88X_BK_OSC_CTRL1, 0x0f}, /* OSC_FREERUN = 1, to lock FLL */
> > + {PM88X_LOWPOWER2, 0x20}, /* XO_LJ = 1, enable low jitter for 32kHZ */
> > + /* enable LPM for internal reference group in sleep */
> > + {PM88X_LOWPOWER4, 0xc0},
> > + {PM88X_BK_OSC_CTRL3, 0xc0}, /* set the duty cycle of charger DC/DC to max */
> > +};
> > +
> > +static const struct reg_default pm886_power_patch[] = {
> > +};
> > +
> > +static const struct reg_default pm886_gpadc_patch[] = {
> > + {PM88X_GPADC_CONFIG6, 0x03}, /* enable non-stop mode */
> > +};
> > +
> > +static const struct reg_default pm886_battery_patch[] = {
> > + {PM88X_CHGBK_CONFIG6, 0xe1},
> > +};
> > +
> > +static const struct reg_default pm886_test_patch[] = {
> > +};
> > +
> > +/* 88pm886 chip itself related */
> > +int pm886_apply_patch(struct pm88x_chip *chip)
> > +{
> > + int ret, size;
> > +
> > + if (!chip || !chip->base_regmap || !chip->power_regmap ||
> > + !chip->gpadc_regmap || !chip->battery_regmap ||
> > + !chip->test_regmap)
> > + return -EINVAL;
>
> You already checked for this in pm88x_post_init_chip().
Yes;
>
> > + size = ARRAY_SIZE(pm886_base_patch);
> > + if (size == 0)
> > + goto power;
> > + ret = regmap_register_patch(chip->base_regmap, pm886_base_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +
> > +power:
> > + size = ARRAY_SIZE(pm886_power_patch);
> > + if (size == 0)
> > + goto gpadc;
> > + ret = regmap_register_patch(chip->power_regmap, pm886_power_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +
> > +gpadc:
> > + size = ARRAY_SIZE(pm886_gpadc_patch);
> > + if (size == 0)
> > + goto battery;
> > + ret = regmap_register_patch(chip->gpadc_regmap, pm886_gpadc_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +battery:
> > + size = ARRAY_SIZE(pm886_battery_patch);
> > + if (size == 0)
> > + goto test;
> > + ret = regmap_register_patch(chip->battery_regmap, pm886_battery_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +
> > +test:
> > + size = ARRAY_SIZE(pm886_test_patch);
> > + if (size == 0)
> > + goto out;
> > + ret = regmap_register_patch(chip->test_regmap, pm886_test_patch, size);
> > + if (ret < 0)
> > + return ret;
> > +out:
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pm886_apply_patch);
>
> A lot of this code looks identical to the previous *-table.c file and
> a bunch of it is duplicated line from line. Please amalgamate them
> and find a way to reduce the amount of lines. I can think of several
> ways this can be done.

OK, I will modify in next patchset;
>
> > diff --git a/drivers/mfd/88pm88x-core.c b/drivers/mfd/88pm88x-core.c
> > new file mode 100644
> > index 0000000..343e0a0
> > --- /dev/null
> > +++ b/drivers/mfd/88pm88x-core.c
> > @@ -0,0 +1,584 @@
> > +/*
> > + * Base driver for Marvell 88PM886/88PM880 PMIC
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/err.h>
> > +#include <linux/i2c.h>
> > +#include <linux/irq.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/slab.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/88pm88x.h>
> > +#include <linux/mfd/88pm886.h>
> > +#include <linux/mfd/88pm880.h>
> > +#include <linux/regulator/machine.h>
> > +
> > +#include "88pm88x.h"
> > +
> > +#define PM88X_POWER_UP_LOG (0x17)
> > +#define PM88X_POWER_DOWN_LOG1 (0xe5)
> > +#define PM88X_POWER_DOWN_LOG2 (0xe6)
> > +#define PM88X_SW_PDOWN (1 << 5)
> > +
> > +static const struct resource onkey_resources[] = {
> > + CELL_IRQ_RESOURCE(PM88X_ONKEY_NAME, PM88X_IRQ_ONKEY),
> > +};
> > +
> > +static const struct resource rtc_resources[] = {
> > + CELL_IRQ_RESOURCE(PM88X_RTC_NAME, PM88X_IRQ_RTC),
> > +};
> > +
> > +static const struct resource charger_resources[] = {
> > + CELL_IRQ_RESOURCE("88pm88x-chg-fail", PM88X_IRQ_CHG_FAIL),
> > + CELL_IRQ_RESOURCE("88pm88x-chg-done", PM88X_IRQ_CHG_DONE),
> > + CELL_IRQ_RESOURCE("88pm88x-chg-good", PM88X_IRQ_CHG_GOOD),
> > +};
> > +
> > +static const struct resource battery_resources[] = {
> > + CELL_IRQ_RESOURCE("88pm88x-bat-cc", PM88X_IRQ_CC),
> > + CELL_IRQ_RESOURCE("88pm88x-bat-volt", PM88X_IRQ_VBAT),
> > + CELL_IRQ_RESOURCE("88pm88x-bat-detect", PM88X_IRQ_BAT_DET),
> > +};
> > +
> > +static const struct resource headset_resources[] = {
> > + CELL_IRQ_RESOURCE("88pm88x-headset-det", PM88X_IRQ_HS_DET),
> > + CELL_IRQ_RESOURCE("88pm88x-mic-det", PM88X_IRQ_MIC_DET),
> > +};
> > +
> > +static const struct resource vbus_resources[] = {
> > + CELL_IRQ_RESOURCE("88pm88x-vbus-det", PM88X_IRQ_VBUS),
> > + CELL_IRQ_RESOURCE("88pm88x-gpadc0", PM88X_IRQ_GPADC0),
> > + CELL_IRQ_RESOURCE("88pm88x-gpadc1", PM88X_IRQ_GPADC1),
> > + CELL_IRQ_RESOURCE("88pm88x-gpadc2", PM88X_IRQ_GPADC2),
> > + CELL_IRQ_RESOURCE("88pm88x-gpadc3", PM88X_IRQ_GPADC3),
> > + CELL_IRQ_RESOURCE("88pm88x-otg-fail", PM88X_IRQ_OTG_FAIL),
> > +};
> > +
> > +static const struct resource leds_resources[] = {
> > + CELL_IRQ_RESOURCE("88pm88x-cfd-fail", PM88X_IRQ_CFD_FAIL),
> > +};
> > +
> > +static const struct resource dvc_resources[] = {
> > + {
> > + .name = PM88X_DVC_NAME,
> > + },
> > +};
> > +
> > +static const struct resource rgb_resources[] = {
> > + {
> > + .name = PM88X_RGB_NAME,
> > + },
> > +};
> > +
> > +static const struct resource gpadc_resources[] = {
> > + {
> > + .name = PM88X_GPADC_NAME,
> > + },
> > +};
>
> Tabbing for all of the above.

Sure, thanks;
>
> In fact, for one line struct entries, I would prefer:
>
> { .name = PM88X_GPADC_NAME },
>
> > +static const struct mfd_cell common_cell_devs[] = {
> > + CELL_DEV(PM88X_RTC_NAME, rtc_resources, "marvell,88pm88x-rtc", -1),
> > + CELL_DEV(PM88X_ONKEY_NAME, onkey_resources, "marvell,88pm88x-onkey", -1),
> > + CELL_DEV(PM88X_CHARGER_NAME, charger_resources, "marvell,88pm88x-charger", -1),
> > + CELL_DEV(PM88X_BATTERY_NAME, battery_resources, "marvell,88pm88x-battery", -1),
> > + CELL_DEV(PM88X_HEADSET_NAME, headset_resources, "marvell,88pm88x-headset", -1),
> > + CELL_DEV(PM88X_VBUS_NAME, vbus_resources, "marvell,88pm88x-vbus", -1),
> > + CELL_DEV(PM88X_CFD_NAME, leds_resources, "marvell,88pm88x-leds", PM88X_FLASH_LED),
> > + CELL_DEV(PM88X_CFD_NAME, leds_resources, "marvell,88pm88x-leds", PM88X_TORCH_LED),
> > + CELL_DEV(PM88X_DVC_NAME, dvc_resources, "marvell,88pm88x-dvc", -1),
> > + CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb0", PM88X_RGB_LED0),
> > + CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb1", PM88X_RGB_LED1),
> > + CELL_DEV(PM88X_RGB_NAME, rgb_resources, "marvell,88pm88x-rgb2", PM88X_RGB_LED2),
>
> This isn't how it works. You should have one LED entry here, then let
> the LED driver take care of all this stuff.

OK, I will modify it;
>
> > + CELL_DEV(PM88X_GPADC_NAME, gpadc_resources, "marvell,88pm88x-gpadc", -1),
> > +};
>
>
> > +const struct of_device_id pm88x_of_match[] = {
> > + { .compatible = "marvell,88pm886", .data = (void *)PM886 },
> > + { .compatible = "marvell,88pm880", .data = (void *)PM880 },
> > + {},
> > +};
> > +
> > +struct pm88x_chip *pm88x_init_chip(struct i2c_client *client)
>
> Why is this in a seperate file to the one it's called from? I
> suggest you move it into the *-i2c.c file.

OK, I'll do it; yes, it can be there;
>
> > +{
> > + struct pm88x_chip *chip;
> > +
> > + chip = devm_kzalloc(&client->dev, sizeof(struct pm88x_chip), GFP_KERNEL);
>
> Calling this 'chip' is confusing, as it has different connotations
> depending on the subsystem.
>
> ddata (for device data) or priv for (private) is more common.

So thank you; I prefer ddata;
>
> > + if (!chip)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + chip->client = client;
> > + chip->irq = client->irq;
> > + chip->dev = &client->dev;
>
> Why are you storing 'client' AND 'dev'? Do you use 'client' directly?
> If not, ditch it. If you do, then just store 'client' and extract
> 'client->dev' when you require it.

OK, I am in the second case, I will modify it;
>
> > + chip->ldo_page_addr = client->addr + 1;
> > + chip->power_page_addr = client->addr + 1;
> > + chip->gpadc_page_addr = client->addr + 2;
> > + chip->battery_page_addr = client->addr + 3;
> > + chip->buck_page_addr = client->addr + 4;
> > + chip->test_page_addr = client->addr + 7;
>
> I have no idea what's going on here, but it's almost certainly not
> correct. Instead of separating these out per device have a base
> address and create DEFINES for each of them.

it is because this PMIC has several i2c address, each has different
function, the offset vesus the client->addr is fixed, the client->addr
is 0x30 from device tree, then the gpadc_page_addr is 0x32, that is
why I use client->addr + 2;

you mean the DEFINES are better?

>
> > + dev_set_drvdata(chip->dev, chip);
> > + i2c_set_clientdata(chip->client, chip);
>
> Why do you have both of these?

I think i2c_set_clientdata() is enough for my case, thanks;
>
> > + device_init_wakeup(&client->dev, 1);
> > +
> > + return chip;
>
> You're wearing a belt and 2 pairs of braces here. You just stored
> 'chip' into the device's private data area, why are you returning it
> too?

my fault, I will change
>
> > +}
> > +
> > +int pm88x_parse_dt(struct device_node *np, struct pm88x_chip *chip)
> > +{
> > + if (!chip)
> > + return -EINVAL;
> > +
> > + chip->irq_mode =
> > + !of_property_read_bool(np, "marvell,88pm88x-irq-write-clear");
>
> A new function for just one line? Just move it into probe()

I planned to add this function in case more properties are needed,
OK, I will move to probe()
>
> > + return 0;
> > +}
> > +
> > +
>
> Superfluous '\n'.
I will remove;
>
> > +static void parse_powerup_down_log(struct pm88x_chip *chip)
> > +{
> > + int powerup, powerdown1, powerdown2, bit;
> > + int powerup_bits, powerdown1_bits, powerdown2_bits;
> > + static const char * const powerup_name[] = {
> > + "ONKEY_WAKEUP ",
> > + "CHG_WAKEUP ",
> > + "EXTON_WAKEUP ",
> > + "SMPL_WAKEUP ",
> > + "ALARM_WAKEUP ",
> > + "FAULT_WAKEUP ",
> > + "BAT_WAKEUP ",
> > + "WLCHG_WAKEUP ",
> > + };
> > + static const char * const powerdown1_name[] = {
> > + "OVER_TEMP ",
> > + "UV_VANA5 ",
> > + "SW_PDOWN ",
> > + "FL_ALARM ",
> > + "WD ",
> > + "LONG_ONKEY",
> > + "OV_VSYS ",
> > + "RTC_RESET "
> > + };
> > + static const char * const powerdown2_name[] = {
> > + "HYB_DONE ",
> > + "UV_VBAT ",
> > + "HW_RESET2 ",
> > + "PGOOD_PDOWN",
> > + "LONKEY_RTC ",
> > + "HW_RESET1 ",
> > + };
> > +
> > + regmap_read(chip->base_regmap, PM88X_POWER_UP_LOG, &powerup);
> > + regmap_read(chip->base_regmap, PM88X_POWER_DOWN_LOG1, &powerdown1);
> > + regmap_read(chip->base_regmap, PM88X_POWER_DOWN_LOG2, &powerdown2);
> > +
> > + /*
> > + * mask reserved bits
> > + *
> > + * note: HYB_DONE and HW_RESET1 are kept,
> > + * but should not be considered as power down events
> > + */
> > + switch (chip->type) {
> > + case PM886:
> > + powerup &= 0x7f;
> > + powerdown2 &= 0x1f;
> > + powerup_bits = 7;
> > + powerdown1_bits = 8;
> > + powerdown2_bits = 5;
> > + break;
> > + case PM880:
> > + powerdown2 &= 0x3f;
> > + powerup_bits = 8;
> > + powerdown1_bits = 8;
> > + powerdown2_bits = 6;
> > + break;
> > + default:
> > + return;
> > + }
> > +
> > + /* keep globals for external usage */
> > + chip->powerup = powerup;
> > + chip->powerdown1 = powerdown1;
> > + chip->powerdown2 = powerdown2;
> > +
> > + /* power up log */
> > + dev_info(chip->dev, "powerup log 0x%x: 0x%x\n",
> > + PM88X_POWER_UP_LOG, powerup);
> > + dev_info(chip->dev, " ----------------------------\n");
> > + dev_info(chip->dev, "| name(power up) | status |\n");
> > + dev_info(chip->dev, "|-----------------|----------|\n");
> > + for (bit = 0; bit < powerup_bits; bit++)
> > + dev_info(chip->dev, "| %s | %x |\n",
> > + powerup_name[bit], (powerup >> bit) & 1);
> > + dev_info(chip->dev, " ----------------------------\n");
> > +
> > + /* power down log1 */
> > + dev_info(chip->dev, "PowerDW Log1 0x%x: 0x%x\n",
> > + PM88X_POWER_DOWN_LOG1, powerdown1);
> > + dev_info(chip->dev, " -------------------------------\n");
> > + dev_info(chip->dev, "| name(power down1) | status |\n");
> > + dev_info(chip->dev, "|--------------------|----------|\n");
> > + for (bit = 0; bit < powerdown1_bits; bit++)
> > + dev_info(chip->dev, "| %s | %x |\n",
> > + powerdown1_name[bit], (powerdown1 >> bit) & 1);
> > + dev_info(chip->dev, " -------------------------------\n");
> > +
> > + /* power down log2 */
> > + dev_info(chip->dev, "PowerDW Log2 0x%x: 0x%x\n",
> > + PM88X_POWER_DOWN_LOG2, powerdown2);
> > + dev_info(chip->dev, " -------------------------------\n");
> > + dev_info(chip->dev, "| name(power down2) | status |\n");
> > + dev_info(chip->dev, "|--------------------|----------|\n");
> > + for (bit = 0; bit < powerdown2_bits; bit++)
> > + dev_info(chip->dev, "| %s | %x |\n",
> > + powerdown2_name[bit], (powerdown2 >> bit) & 1);
> > + dev_info(chip->dev, " -------------------------------\n");
> > +
> > + /* write to clear power down log */
> > + regmap_write(chip->base_regmap, PM88X_POWER_DOWN_LOG1, 0xff);
> > + regmap_write(chip->base_regmap, PM88X_POWER_DOWN_LOG2, 0xff);
> > +}
> > +
> > +static const char *chip_stepping_to_string(unsigned int id)
> > +{
> > + switch (id) {
> > + case 0xa1:
> > + return "88pm886 A1";
> > + case 0xb1:
> > + return "88pm880 A1";
> > + default:
> > + return "Unknown";
> > + }
> > +}
> > +
> > +int pm88x_post_init_chip(struct pm88x_chip *chip)
> > +{
> > + int ret;
> > + unsigned int val;
> > +
> > + if (!chip || !chip->base_regmap || !chip->power_regmap ||
> > + !chip->gpadc_regmap || !chip->battery_regmap)
> > + return -EINVAL;
> > +
> > + /* save chip stepping */
> > + ret = regmap_read(chip->base_regmap, PM88X_ID_REG, &val);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "Failed to read chip ID: %d\n", ret);
> > + return ret;
> > + }
> > + chip->chip_id = val;
> > +
> > + dev_info(chip->dev, "PM88X chip ID = 0x%x(%s)\n", val,
> > + chip_stepping_to_string(chip->chip_id));
> > +
> > + /* read before alarm wake up bit before initialize interrupt */
> > + ret = regmap_read(chip->base_regmap, PM88X_RTC_ALARM_CTRL1, &val);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
> > + return ret;
> > + }
> > + chip->rtc_wakeup = !!(val & PM88X_ALARM_WAKEUP);
> > +
> > + parse_powerup_down_log(chip);
> > +
> > + return 0;
> > +}
> > +
> > +int pm88x_init_pages(struct pm88x_chip *chip)
> > +{
> > + struct i2c_client *client = chip->client;
> > + const struct regmap_config *base_regmap_config;
> > + const struct regmap_config *power_regmap_config;
> > + const struct regmap_config *gpadc_regmap_config;
> > + const struct regmap_config *battery_regmap_config;
> > + const struct regmap_config *test_regmap_config;
> > + int ret = 0;
> > +
> > + if (!chip || !chip->power_page_addr ||
> > + !chip->gpadc_page_addr || !chip->battery_page_addr)
> > + return -ENODEV;
> > +
> > + chip->type = pm88x_of_get_type(&client->dev);
> > + switch (chip->type) {
> > + case PM886:
> > + base_regmap_config = &pm886_base_i2c_regmap;
> > + power_regmap_config = &pm886_power_i2c_regmap;
> > + gpadc_regmap_config = &pm886_gpadc_i2c_regmap;
> > + battery_regmap_config = &pm886_battery_i2c_regmap;
> > + test_regmap_config = &pm886_test_i2c_regmap;
> > + break;
> > + case PM880:
> > + base_regmap_config = &pm880_base_i2c_regmap;
> > + power_regmap_config = &pm880_power_i2c_regmap;
> > + gpadc_regmap_config = &pm880_gpadc_i2c_regmap;
> > + battery_regmap_config = &pm880_battery_i2c_regmap;
> > + test_regmap_config = &pm880_test_i2c_regmap;
> > + break;
> > + default:
> > + return -ENODEV;
>
> You could probably do with an error message here.
I got your point, good suggestion, thank you;
>
> > + }
> > +
> > + /* base page */
> > + chip->base_regmap = devm_regmap_init_i2c(client, base_regmap_config);
> > + if (IS_ERR(chip->base_regmap)) {
> > + dev_err(chip->dev, "Failed to init base_regmap: %d\n", ret);
> > + ret = PTR_ERR(chip->base_regmap);
> > + goto out;
> > + }
> > +
> > + /* power page */
> > + chip->power_page = i2c_new_dummy(client->adapter, chip->power_page_addr);
> > + if (!chip->power_page) {
> > + dev_err(chip->dev, "Failed to new power_page: %d\n", ret);
> > + ret = -ENODEV;
> > + goto out;
> > + }
> > + chip->power_regmap = devm_regmap_init_i2c(chip->power_page,
> > + power_regmap_config);
> > + if (IS_ERR(chip->power_regmap)) {
> > + dev_err(chip->dev, "Failed to init power_regmap: %d\n", ret);
> > + ret = PTR_ERR(chip->power_regmap);
> > + goto out;
> > + }
> > +
> > + /* gpadc page */
> > + chip->gpadc_page = i2c_new_dummy(client->adapter, chip->gpadc_page_addr);
> > + if (!chip->gpadc_page) {
> > + dev_err(chip->dev, "Failed to new gpadc_page: %d\n", ret);
> > + ret = -ENODEV;
> > + goto out;
> > + }
> > + chip->gpadc_regmap = devm_regmap_init_i2c(chip->gpadc_page,
> > + gpadc_regmap_config);
> > + if (IS_ERR(chip->gpadc_regmap)) {
> > + dev_err(chip->dev, "Failed to init gpadc_regmap: %d\n", ret);
> > + ret = PTR_ERR(chip->gpadc_regmap);
> > + goto out;
> > + }
> > +
> > + /* battery page */
> > + chip->battery_page = i2c_new_dummy(client->adapter, chip->battery_page_addr);
> > + if (!chip->battery_page) {
> > + dev_err(chip->dev, "Failed to new gpadc_page: %d\n", ret);
> > + ret = -ENODEV;
> > + goto out;
> > + }
> > + chip->battery_regmap = devm_regmap_init_i2c(chip->battery_page,
> > + battery_regmap_config);
> > + if (IS_ERR(chip->battery_regmap)) {
> > + dev_err(chip->dev, "Failed to init battery_regmap: %d\n", ret);
> > + ret = PTR_ERR(chip->battery_regmap);
> > + goto out;
> > + }
> > +
> > + /* test page */
> > + chip->test_page = i2c_new_dummy(client->adapter, chip->test_page_addr);
> > + if (!chip->test_page) {
> > + dev_err(chip->dev, "Failed to new test_page: %d\n", ret);
> > + ret = -ENODEV;
> > + goto out;
> > + }
> > + chip->test_regmap = devm_regmap_init_i2c(chip->test_page,
> > + test_regmap_config);
> > + if (IS_ERR(chip->test_regmap)) {
> > + dev_err(chip->dev, "Failed to init test_regmap: %d\n", ret);
> > + ret = PTR_ERR(chip->test_regmap);
> > + goto out;
> > + }
>
> There's a lot of duplicated code here. You can save quite a few lines
> if you thought about it I'm sure.

I will try to fix it in next patchset, thanks for your reminder;
I am ashamed about this
>
> > + chip->type = pm88x_of_get_type(&client->dev);
> > + switch (chip->type) {
> > + case PM886:
> > + /* ldo page */
> > + chip->ldo_page = chip->power_page;
> > + chip->ldo_regmap = chip->power_regmap;
> > + /* buck page */
> > + chip->buck_regmap = chip->power_regmap;
> > + break;
> > + case PM880:
> > + /* ldo page */
> > + chip->ldo_page = chip->power_page;
> > + chip->ldo_regmap = chip->power_regmap;
> > +
> > + /* buck page */
> > + chip->buck_page = i2c_new_dummy(client->adapter,
> > + chip->buck_page_addr);
> > + if (!chip->buck_page) {
> > + dev_err(chip->dev, "Failed to new buck_page: %d\n", ret);
> > + ret = -ENODEV;
> > + goto out;
> > + }
> > + chip->buck_regmap = devm_regmap_init_i2c(chip->buck_page,
> > + power_regmap_config);
> > + if (IS_ERR(chip->buck_regmap)) {
> > + dev_err(chip->dev, "Failed to init buck_regmap: %d\n", ret);
> > + ret = PTR_ERR(chip->buck_regmap);
> > + goto out;
> > + }
> > +
> > + break;
> > + default:
> > + ret = -EINVAL;
>
> Firstly, this probably won't happen, as the first switch() succeeded.
> Secondly, you are returning a different error value, why? Thirdly,
> you probably want an error message here.

for the first question, you are right, I will re-org this function to
make it clearer;
for the second question, my fault, I should write -ENODEV here;
for third, definitly an error message is right, thank you;
>
> > + break;
> > + }
> > +out:
> > + return ret;
> > +}
> > +
> > +void pm800_exit_pages(struct pm88x_chip *chip)
> > +{
> > + if (!chip)
> > + return;
> > +
> > + if (chip->ldo_page)
> > + i2c_unregister_device(chip->ldo_page);
> > + if (chip->gpadc_page)
> > + i2c_unregister_device(chip->gpadc_page);
> > + if (chip->test_page)
> > + i2c_unregister_device(chip->test_page);
>
> '\n'
Yes;
>
> > + /* no need to unregister ldo_page */
> > + switch (chip->type) {
> > + case PM886:
> > + break;
> > + case PM880:
> > + if (chip->buck_page)
> > + i2c_unregister_device(chip->buck_page);
> > + break;
> > + default:
> > + break;
> > + }
>
> No need for this big switch(). Just do:

Good, it's much cleaner than mine, thank you;
>
> if (chip->buck_page)
> i2c_unregister_device(chip->buck_page);
>
> > +}
> > +
> > +int pm88x_init_subdev(struct pm88x_chip *chip)
> > +{
> > + int ret;
>
> '\n'
OK;
>
> This will raise a Checkpatch error. Did you run this set though
> checkpatch.pl?

I am sorry to say it's my fault, I promise it will not happen again.
>
> > + if (!chip)
> > + return -EINVAL;
>
> I'm pretty sure you can't get here if !chip.

Yes, I will change
>
> > + ret = mfd_add_devices(chip->dev, 0, common_cell_devs,
>
> What does 0 mean?

I should use -1 here, though it _seems_ works..
>
> > + ARRAY_SIZE(common_cell_devs), NULL, 0,
> > + regmap_irq_get_domain(chip->irq_data));
> > + if (ret < 0)
>
> if (ret)

OK
>
> > + return ret;
> > +
> > + switch (chip->type) {
> > + case PM886:
> > + ret = mfd_add_devices(chip->dev, 0, pm886_cell_info.cells,
> > + pm886_cell_info.cell_nr, NULL, 0,
> > + regmap_irq_get_domain(chip->irq_data));
> > + break;
> > + case PM880:
> > + ret = mfd_add_devices(chip->dev, 0, pm880_cell_info.cells,
> > + pm880_cell_info.cell_nr, NULL, 0,
> > + regmap_irq_get_domain(chip->irq_data));
>
> switch() on {pm880_cell_info|pm886_cell_info.cells}.cells etc and just
> call mfd_add_devices() once.

Yes, you are right
>
> > + break;
> > + default:
> > + break;
> > + }
> > + return ret;
> > +}
> > +
> > +static int (*apply_to_chip)(struct pm88x_chip *chip);
> > +/* PMIC chip itself related */
> > +int pm88x_apply_patch(struct pm88x_chip *chip)
> > +{
> > + if (!chip || !chip->client)
> > + return -EINVAL;
> > +
> > + chip->type = pm88x_of_get_type(&chip->client->dev);
> > + switch (chip->type) {
> > + case PM886:
> > + apply_to_chip = pm886_apply_patch;
> > + break;
> > + case PM880:
> > + apply_to_chip = pm880_apply_patch;
> > + break;
> > + default:
> > + break;
> > + }
> > + if (apply_to_chip)
> > + apply_to_chip(chip);
> > + return 0;
> > +}
>
> What on earth is going on here?
>
> Why bother assigning the *fn()?

because 88pm886 and 88pm880 have different pages, the latter has one
more page, so I added two function here by assigning the *fn(), after
more thoughts, I think this is not needed, I will modify to use a
better method, thanks for your criticism
>
> > +int pm88x_stepping_fixup(struct pm88x_chip *chip)
> > +{
> > + if (!chip || !chip->client)
> > + return -EINVAL;
>
> I don't think this can happen.

I will remove;
>
> > + chip->type = pm88x_of_get_type(&chip->client->dev);
> > + switch (chip->type) {
> > + case PM886:
> > + case PM880:
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
>
> This function appears to do nothing. Why is it here?

I planed to implement in the following patchset, now I know I should
merge it into the next one;
>
> > +int pm88x_apply_board_fixup(struct pm88x_chip *chip, struct device_node *np)
> > +{
> > + /* add board design specific setting, parsed via device tree */
> > + return 0;
> > +}
>
> What's this? Another empty function.

same with above;
>
> > +long pm88x_of_get_type(struct device *dev)
> > +{
> > + const struct of_device_id *id = of_match_device(pm88x_of_match, dev);
> > +
> > + if (id)
> > + return (long)id->data;
> > + else
> > + return 0;
> > +}
>
> Just do this once, in probe and store the value in driver data. No
> need for a function to do this.

got it;
>
> > +void pm88x_dev_exit(struct pm88x_chip *chip)
> > +{
> > + mfd_remove_devices(chip->dev);
> > + pm88x_irq_exit(chip);
> > +}
> > +
> > +void pm88x_power_off(void)
> > +{
> > + pr_info("powers off the system.");
> > + /* TODO: implement later */
>
> Why can't you implement this now?

OK, I will add in the next version
>
> > + for (;;)
> > + cpu_relax();
>
> What's the point of this?

I just would like to busy loop at the end of the power_off callback;
>
> > +}
> > +
> > +int pm88x_reboot_notifier_callback(struct notifier_block *this,
> > + unsigned long code, void *unused)
> > +{
> > + struct pm88x_chip *chip =
> > + container_of(this, struct pm88x_chip, reboot_notifier);
> > +
> > + switch (code) {
> > + case SYS_HALT:
> > + case SYS_POWER_OFF:
> > + dev_info(chip->dev, "system is down.\n");
> > + break;
> > + case SYS_RESTART:
> > + default:
> > + dev_info(chip->dev, "system will reboot.\n");
> > + break;
> > + }
> > +
> > + return 0;
> > +}
>
> This function is pointless.

my intention is to record what has happened to the system, I will
double consider of it; thanks;
>
> > diff --git a/drivers/mfd/88pm88x-i2c.c b/drivers/mfd/88pm88x-i2c.c
> > new file mode 100644
> > index 0000000..36842ed
> > --- /dev/null
> > +++ b/drivers/mfd/88pm88x-i2c.c
> > @@ -0,0 +1,167 @@
> > +/*
> > + * 88pm88x-i2c.c -- 88pm88x i2c bus interface
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/slab.h>
> > +#include <linux/of.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/88pm886.h>
> > +#include <linux/mfd/88pm880.h>
> > +#include <linux/mfd/88pm88x.h>
> > +
> > +static int pm88x_i2c_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct pm88x_chip *chip;
> > + struct device_node *node = client->dev.of_node;
> > + int ret = 0;
> > +
> > + chip = pm88x_init_chip(client);
>
> This function doesn't actually do any chip initialisation.

actually, yes, it only does some software work.. I will find a better
name in next version
>
> ... and the code should be moved into here.
>
> > + if (IS_ERR(chip)) {
> > + ret = PTR_ERR(chip);
> > + dev_err(chip->dev, "initialize 88pm88x chip fails!\n");
>
> "Failed to initialise chip"

OK, I will scan and modify all of the error log. thanks very much;
>
> > + goto err;
>
> Just return.

OK;
>
> > + }
> > +
> > + ret = pm88x_parse_dt(node, chip);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "parse dt fails!\n");
>
> "Failed to parse DT"
got it;
>
> > + goto err;
>
> Just return.
>
> > + }
> > +
> > + ret = pm88x_init_pages(chip);
> > + if (ret) {
> > + dev_err(chip->dev, "initialize 88pm88x pages fails!\n");
>
> Etc ...
got it;
>
> > + goto err;
>
> Just return.
got it;
>
> > + }
> > +
> > + ret = pm88x_post_init_chip(chip);
> > + if (ret) {
> > + dev_err(chip->dev, "post initialize 88pm88x fails!\n");
> > + goto err;
>
> Just return.
got it;
>
> > + }
> > +
> > + ret = pm88x_irq_init(chip);
> > + if (ret) {
> > + dev_err(chip->dev, "initialize 88pm88x interrupt fails!\n");
> > + goto err_init_irq;
> > + }
> > +
> > + ret = pm88x_init_subdev(chip);
> > + if (ret) {
> > + dev_err(chip->dev, "initialize 88pm88x sub-device fails\n");
> > + goto err_init_subdev;
> > + }
> > +
> > + /* patch for PMIC chip itself */
> > + ret = pm88x_apply_patch(chip);
> > + if (ret) {
> > + dev_err(chip->dev, "apply 88pm88x register patch fails\n");
> > + goto err_apply_patch;
> > + }
> > +
> > + /* fixup according PMIC stepping */
>
> This comment doesn't make any sense.

I will change, my intention is add chip stepping specific setting
>
> > + ret = pm88x_stepping_fixup(chip);
> > + if (ret) {
> > + dev_err(chip->dev, "fixup according to chip stepping\n");
> > + goto err_apply_patch;
> > + }
> > +
> > + /* patch for board configuration */
> > + ret = pm88x_apply_board_fixup(chip, node);
> > + if (ret) {
> > + dev_err(chip->dev, "apply 88pm88x register for board fails\n");
> > + goto err_apply_patch;
> > + }
> > +
> > + pm_power_off = pm88x_power_off;
> > +
> > + chip->reboot_notifier.notifier_call = pm88x_reboot_notifier_callback;
> > + register_reboot_notifier(&(chip->reboot_notifier));
> > +
> > + return 0;
> > +
> > +err_apply_patch:
> > + mfd_remove_devices(chip->dev);
> > +err_init_subdev:
> > + regmap_del_irq_chip(chip->irq, chip->irq_data);
> > +err_init_irq:
> > + pm800_exit_pages(chip);
> > +err:
>
> Remove this label.

Yes, you are right;
>
> > + return ret;
> > +}
> > +
> > +static int pm88x_i2c_remove(struct i2c_client *i2c)
> > +{
> > + struct pm88x_chip *chip = dev_get_drvdata(&i2c->dev);
> > + pm88x_dev_exit(chip);
> > + return 0;
> > +}
>
> Checkpatch will warn you about this function.

Sorry for my fault
>
> > +static const struct i2c_device_id pm88x_i2c_id[] = {
> > + { "88pm886", PM886 },
> > + { "88pm880", PM880 },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, pm88x_i2c_id);
> > +
> > +static int pm88x_i2c_suspend(struct device *dev)
> > +{
> > + return 0;
> > +}
> > +
> > +static int pm88x_i2c_resume(struct device *dev)
> > +{
> > + return 0;
> > +}
>
> Why even supply them?

I planned to implement in the next patch, now I understand I should
remove it. thanks;
>
> > +static SIMPLE_DEV_PM_OPS(pm88x_pm_ops, pm88x_i2c_suspend, pm88x_i2c_resume);
> > +
> > +static struct i2c_driver pm88x_i2c_driver = {
> > + .driver = {
> > + .name = "88pm88x",
> > + .owner = THIS_MODULE,
>
> Remove this line.
got your point
>
> > + .pm = &pm88x_pm_ops,
> > + .of_match_table = of_match_ptr(pm88x_of_match),
> > + },
> > + .probe = pm88x_i2c_probe,
> > + .remove = pm88x_i2c_remove,
> > + .id_table = pm88x_i2c_id,
> > +};
> > +
> > +static int __init pm88x_i2c_init(void)
> > +{
> > + int ret;
> > +
> > + ret = i2c_add_driver(&pm88x_i2c_driver);
> > + if (ret != 0) {
> > + pr_err("88pm88x I2C registration failed %d\n", ret);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +subsys_initcall(pm88x_i2c_init);
> > +
> > +static void __exit pm88x_i2c_exit(void)
> > +{
> > + i2c_del_driver(&pm88x_i2c_driver);
> > +}
> > +module_exit(pm88x_i2c_exit);
>
> I think you want to remove all of this and replace with
> module_i2c_driver().

Yes, I should use module_i2c_driver();
>
> > +MODULE_DESCRIPTION("88pm88x I2C bus interface");
> > +MODULE_AUTHOR("Yi Zhang<[email protected]>");
>
> Missing a space.
Yes
>
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/mfd/88pm88x-irq.c b/drivers/mfd/88pm88x-irq.c
> > new file mode 100644
> > index 0000000..0126df0
> > --- /dev/null
> > +++ b/drivers/mfd/88pm88x-irq.c
> > @@ -0,0 +1,171 @@
> > +/*
> > + * 88pm886 interrupt support
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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/delay.h>
> > +#include <linux/gpio.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/slab.h>
> > +#include <linux/mfd/88pm88x.h>
> > +#include <linux/mfd/88pm886.h>
> > +#include <linux/mfd/88pm880.h>
> > +
> > +/* interrupt status registers */
> > +#define PM88X_INT_STATUS1 (0x05)
> > +
> > +#define PM88X_INT_ENA_1 (0x0a)
> > +#define PM88X_ONKEY_INT_ENA1 (1 << 0)
> > +#define PM88X_EXTON_INT_ENA1 (1 << 1)
> > +#define PM88X_CHG_INT_ENA1 (1 << 2)
> > +#define PM88X_BAT_INT_ENA1 (1 << 3)
> > +#define PM88X_RTC_INT_ENA1 (1 << 4)
> > +#define PM88X_CLASSD_INT_ENA1 (1 << 5)
> > +#define PM88X_XO_INT_ENA1 (1 << 6)
> > +#define PM88X_GPIO_INT_ENA1 (1 << 7)
> > +
> > +#define PM88X_INT_ENA_2 (0x0b)
> > +#define PM88X_VBAT_INT_ENA2 (1 << 0)
> > +#define PM88X_RSVED1_INT_ENA2 (1 << 1)
> > +#define PM88X_VBUS_INT_ENA2 (1 << 2)
> > +#define PM88X_ITEMP_INT_ENA2 (1 << 3)
> > +#define PM88X_BUCK_PGOOD_INT_ENA2 (1 << 4)
> > +#define PM88X_LDO_PGOOD_INT_ENA2 (1 << 5)
> > +#define PM88X_RSVED6_INT_ENA2 (1 << 6)
> > +#define PM88X_RSVED7_INT_ENA2 (1 << 7)
> > +
> > +#define PM88X_INT_ENA_3 (0x0c)
> > +#define PM88X_GPADC0_INT_ENA3 (1 << 0)
> > +#define PM88X_GPADC1_INT_ENA3 (1 << 1)
> > +#define PM88X_GPADC2_INT_ENA3 (1 << 2)
> > +#define PM88X_GPADC3_INT_ENA3 (1 << 3)
> > +#define PM88X_MIC_INT_ENA3 (1 << 4)
> > +#define PM88X_HS_INT_ENA3 (1 << 5)
> > +#define PM88X_GND_INT_ENA3 (1 << 6)
> > +#define PM88X_RSVED7_INT_ENA3 (1 << 7)
> > +
> > +#define PM88X_INT_ENA_4 (0x0d)
> > +#define PM88X_CHG_FAIL_INT_ENA4 (1 << 0)
> > +#define PM88X_CHG_DONE_INT_ENA4 (1 << 1)
> > +#define PM88X_RSVED2_INT_ENA4 (1 << 2)
> > +#define PM88X_OTG_FAIL_INT_ENA4 (1 << 3)
> > +#define PM88X_RSVED4_INT_ENA4 (1 << 4)
> > +#define PM88X_CHG_ILIM_INT_ENA4 (1 << 5)
> > +#define PM88X_CC_INT_ENA4 (1 << 6)
> > +#define PM88X_RSVED7_INT_ENA4 (1 << 7)
> > +
> > +#define PM88X_MISC_CONFIG2 (0x15)
> > +#define PM88X_INV_INT (1 << 0)
> > +#define PM88X_INT_CLEAR (1 << 1)
> > +#define PM88X_INT_RC (0 << 1)
> > +#define PM88X_INT_WC (1 << 1)
> > +#define PM88X_INT_MASK_MODE (1 << 2)
>
> Replace all "1 <<" with BIT()
>
> > +static const struct regmap_irq pm88x_irqs[] = {
> > + /* INT0 */
> > + [PM88X_IRQ_ONKEY] = {.reg_offset = 0, .mask = PM88X_ONKEY_INT_ENA1,},
> > + [PM88X_IRQ_EXTON] = {.reg_offset = 0, .mask = PM88X_EXTON_INT_ENA1,},
> > + [PM88X_IRQ_CHG_GOOD] = {.reg_offset = 0, .mask = PM88X_CHG_INT_ENA1,},
> > + [PM88X_IRQ_BAT_DET] = {.reg_offset = 0, .mask = PM88X_BAT_INT_ENA1,},
> > + [PM88X_IRQ_RTC] = {.reg_offset = 0, .mask = PM88X_RTC_INT_ENA1,},
> > + [PM88X_IRQ_CLASSD] = { .reg_offset = 0, .mask = PM88X_CLASSD_INT_ENA1,},
> > + [PM88X_IRQ_XO] = {.reg_offset = 0, .mask = PM88X_XO_INT_ENA1,},
> > + [PM88X_IRQ_GPIO] = {.reg_offset = 0, .mask = PM88X_GPIO_INT_ENA1,},
> > +
> > + /* INT1 */
> > + [PM88X_IRQ_VBAT] = {.reg_offset = 1, .mask = PM88X_VBAT_INT_ENA2,},
> > + [PM88X_IRQ_VBUS] = {.reg_offset = 1, .mask = PM88X_VBUS_INT_ENA2,},
> > + [PM88X_IRQ_ITEMP] = {.reg_offset = 1, .mask = PM88X_ITEMP_INT_ENA2,},
> > + [PM88X_IRQ_BUCK_PGOOD] = {
> > + .reg_offset = 1,
> > + .mask = PM88X_BUCK_PGOOD_INT_ENA2,
> > + },
> > + [PM88X_IRQ_LDO_PGOOD] = {
> > + .reg_offset = 1,
> > + .mask = PM88X_LDO_PGOOD_INT_ENA2,
> > + },
> > + /* INT2 */
> > + [PM88X_IRQ_GPADC0] = {.reg_offset = 2, .mask = PM88X_GPADC0_INT_ENA3,},
> > + [PM88X_IRQ_GPADC1] = {.reg_offset = 2, .mask = PM88X_GPADC1_INT_ENA3,},
> > + [PM88X_IRQ_GPADC2] = {.reg_offset = 2, .mask = PM88X_GPADC2_INT_ENA3,},
> > + [PM88X_IRQ_GPADC3] = {.reg_offset = 2, .mask = PM88X_GPADC3_INT_ENA3,},
> > + [PM88X_IRQ_MIC_DET] = {.reg_offset = 2, .mask = PM88X_MIC_INT_ENA3,},
> > + [PM88X_IRQ_HS_DET] = {.reg_offset = 2, .mask = PM88X_HS_INT_ENA3,},
> > + [PM88X_IRQ_GND_DET] = {.reg_offset = 2, .mask = PM88X_GND_INT_ENA3,},
>
> This is not how we lay out structures (your code below is correct).
got your point.
>
> > + /* INT3 */
> > + [PM88X_IRQ_CHG_FAIL] = {
> > + .reg_offset = 3,
> > + .mask = PM88X_CHG_FAIL_INT_ENA4,
> > + },
> > + [PM88X_IRQ_CHG_DONE] = {
> > + .reg_offset = 3,
> > + .mask = PM88X_CHG_DONE_INT_ENA4,
> > + },
> > + [PM88X_IRQ_OTG_FAIL] = {
> > + .reg_offset = 3,
> > + .mask = PM88X_OTG_FAIL_INT_ENA4,
> > + },
> > + [PM88X_IRQ_CHG_ILIM] = {
> > + .reg_offset = 3,
> > + .mask = PM88X_CHG_ILIM_INT_ENA4,
> > + },
> > + [PM88X_IRQ_CC] = {.reg_offset = 3, .mask = PM88X_CC_INT_ENA4,},
> > +};
> > +
> > +struct regmap_irq_chip pm88x_irq_chip = {
> > + .name = "88pm88x",
> > + .irqs = pm88x_irqs,
> > + .num_irqs = ARRAY_SIZE(pm88x_irqs),
> > +
>
> No need for the '\n'.
thanks;
>
> > + .num_regs = 4,
> > + .status_base = PM88X_INT_STATUS1,
> > + .mask_base = PM88X_INT_ENA_1,
> > + .ack_base = PM88X_INT_STATUS1,
> > + .mask_invert = 1,
> > +};
> > +
> > +int pm88x_irq_init(struct pm88x_chip *chip)
> > +{
> > + int mask, data, ret;
> > + struct regmap *map;
>
> Not sure this variable is even needed, but if you want to keep it,
> please rename to regmap.

fine, I will use regmap
>
> > + if (!chip || !chip->base_regmap || !chip->irq) {
> > + pr_err("cannot initialize interrupt!\n");
> > + return -EINVAL;
> > + }
> > + map = chip->base_regmap;
> > +
> > + /*
> > + * irq_mode defines the way of clearing interrupt.
> > + * it's read-clear by default.
> > + */
> > + mask = PM88X_INV_INT | PM88X_INT_CLEAR | PM88X_INT_MASK_MODE;
> > + data = (chip->irq_mode) ? PM88X_INT_WC : PM88X_INT_RC;
> > + ret = regmap_update_bits(map, PM88X_MISC_CONFIG2, mask, data);
> > + if (ret < 0) {
> > + dev_err(chip->dev, "cannot set interrupt mode!\n");
> > + return ret;
> > + }
> > +
> > + ret = regmap_add_irq_chip(map, chip->irq, IRQF_ONESHOT, -1,
> > + &pm88x_irq_chip, &chip->irq_data);
> > + return ret;
> > +}
> > +
> > +int pm88x_irq_exit(struct pm88x_chip *chip)
> > +{
> > + regmap_del_irq_chip(chip->irq, chip->irq_data);
> > + return 0;
> > +}
> > diff --git a/drivers/mfd/88pm88x.h b/drivers/mfd/88pm88x.h
> > new file mode 100644
> > index 0000000..a85a486
> > --- /dev/null
> > +++ b/drivers/mfd/88pm88x.h
> > @@ -0,0 +1,51 @@
> > +#ifndef _MFD_88PM88X_H
> > +#define _MFD_88PM88X_H
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/of.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/core.h>
> > +
> > +struct pmic_cell_info {
> > + const struct mfd_cell *cells;
> > + int cell_nr;
> > +};
>
> No need for this, please move it.
Yes;
>
> > +#define CELL_IRQ_RESOURCE(_name, _irq) { \
> > + .name = _name, \
> > + .start = _irq, .end = _irq, \
> > + .flags = IORESOURCE_IRQ, \
> > + }
>
> This already exists, DEFINE_RES*.

Thanks for reminder, yes;
>
> > +#define CELL_DEV(_name, _r, _compatible, _id) { \
> > + .name = _name, \
> > + .of_compatible = _compatible, \
> > + .num_resources = ARRAY_SIZE(_r), \
> > + .resources = _r, \
> > + .id = _id, \
> > + }
>
> This is not required.
>
> If you feel the need for an MFD Cell macro, you probably have too many
> MFD cells and you have done something wrong..

Yes, I should use one MFD to cover regulators and one MFD to cover
LEDs, I will remember this lesson; thanks;
>
> > +/* 88pm886 */
> > +extern const struct regmap_config pm886_base_i2c_regmap;
> > +extern const struct regmap_config pm886_power_i2c_regmap;
> > +extern const struct regmap_config pm886_gpadc_i2c_regmap;
> > +extern const struct regmap_config pm886_battery_i2c_regmap;
> > +extern const struct regmap_config pm886_test_i2c_regmap;
> > +
> > +extern const struct mfd_cell pm886_cell_devs[];
> > +extern struct pmic_cell_info pm886_cell_info;
> > +
> > +int pm886_apply_patch(struct pm88x_chip *chip);
> > +
> > +/* 88pm880 */
> > +extern const struct regmap_config pm880_base_i2c_regmap;
> > +extern const struct regmap_config pm880_power_i2c_regmap;
> > +extern const struct regmap_config pm880_gpadc_i2c_regmap;
> > +extern const struct regmap_config pm880_battery_i2c_regmap;
> > +extern const struct regmap_config pm880_test_i2c_regmap;
> > +
> > +extern const struct mfd_cell pm880_cell_devs[];
> > +extern struct pmic_cell_info pm880_cell_info;
> > +
> > +int pm880_apply_patch(struct pm88x_chip *chip);
>
> Place all of your code in the correct files and all these horrible
> global externs should vanish.

I will think carefully, thanks;
>
> > +#endif
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index d5ad04d..bdebca9 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -390,6 +390,18 @@ config MFD_KEMPLD
> > This driver can also be built as a module. If so, the module
> > will be called kempld-core.
> >
> > +config MFD_88PM88X
> > + tristate "Marvell 88PM886/880 PMIC"
> > + depends on I2C=y
> > + select REGMAP_I2C
> > + select MFD_CORE
> > + help
> > + This supports for Marvell 88PM88X Series Power Management IC:
> > + 88pm886 and 88pm880;
> > + This includes the I2C driver, the interrupt resource distribution
> > + and the core APIs, for individual sub-device as voltage regulators,
> > + RTC, charger, fuelgauge, etc, select under the corresponding menus.
> > +
> > config MFD_88PM800
> > tristate "Marvell 88PM800"
> > depends on I2C=y
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index 0e5cfeb..365d1fa 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -6,6 +6,9 @@
> > obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
> > obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
> > obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
> > +88pm88x-objs := 88pm88x-core.o 88pm88x-i2c.o 88pm88x-irq.o 88pm886-table.o 88pm880-table.o
> > +obj-$(CONFIG_MFD_88PM88X) += 88pm88x.o
> > +
> > obj-$(CONFIG_MFD_SM501) += sm501.o
> > obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
> > obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
> > diff --git a/include/linux/mfd/88pm880-reg.h b/include/linux/mfd/88pm880-reg.h
> > new file mode 100644
> > index 0000000..fdf2315
> > --- /dev/null
> > +++ b/include/linux/mfd/88pm880-reg.h
>
> You don't need a seperate header file for register values. Just put
> them in include/linux/mfd/88pm880.h.

OK, got it;
>
> > @@ -0,0 +1,98 @@
> > +/*
> > + * Marvell 88PM880 registers
> > + *
> > + * Copyright (C) 2014 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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_88PM880_REG_H
> > +#define __LINUX_MFD_88PM880_REG_H
> > +
> > +#define PM880_BUCK1_VOUT (0x28)
> > +
> > +#define PM880_BUCK1A_VOUT (0x28) /* voltage 0 */
> > +#define PM880_BUCK1A_1_VOUT (0x29)
> > +#define PM880_BUCK1A_2_VOUT (0x2a)
> > +#define PM880_BUCK1A_3_VOUT (0x2b)
> > +#define PM880_BUCK1A_4_VOUT (0x2c)
> > +#define PM880_BUCK1A_5_VOUT (0x2d)
> > +#define PM880_BUCK1A_6_VOUT (0x2e)
> > +#define PM880_BUCK1A_7_VOUT (0x2f)
> > +#define PM880_BUCK1A_8_VOUT (0x30)
> > +#define PM880_BUCK1A_9_VOUT (0x31)
> > +#define PM880_BUCK1A_10_VOUT (0x32)
> > +#define PM880_BUCK1A_11_VOUT (0x33)
> > +#define PM880_BUCK1A_12_VOUT (0x34)
> > +#define PM880_BUCK1A_13_VOUT (0x35)
> > +#define PM880_BUCK1A_14_VOUT (0x36)
> > +#define PM880_BUCK1A_15_VOUT (0x37)
> > +
> > +#define PM880_BUCK1B_VOUT (0x40)
> > +#define PM880_BUCK1B_1_VOUT (0x41)
> > +#define PM880_BUCK1B_2_VOUT (0x42)
> > +#define PM880_BUCK1B_3_VOUT (0x43)
> > +#define PM880_BUCK1B_4_VOUT (0x44)
> > +#define PM880_BUCK1B_5_VOUT (0x45)
> > +#define PM880_BUCK1B_6_VOUT (0x46)
> > +#define PM880_BUCK1B_7_VOUT (0x47)
> > +#define PM880_BUCK1B_8_VOUT (0x48)
> > +#define PM880_BUCK1B_9_VOUT (0x49)
> > +#define PM880_BUCK1B_10_VOUT (0x4a)
> > +#define PM880_BUCK1B_11_VOUT (0x4b)
> > +#define PM880_BUCK1B_12_VOUT (0x4c)
> > +#define PM880_BUCK1B_13_VOUT (0x4d)
> > +#define PM880_BUCK1B_14_VOUT (0x4e)
> > +#define PM880_BUCK1B_15_VOUT (0x4f)
> > +
> > +/* buck7 has dvc function */
> > +#define PM880_BUCK7_VOUT (0xb8) /* voltage 0 */
> > +#define PM880_BUCK7_1_VOUT (0xb9)
> > +#define PM880_BUCK7_2_VOUT (0xba)
> > +#define PM880_BUCK7_3_VOUT (0xbb)
> > +
> > +/*
> > + * buck sleep mode control registers:
> > + * 00-disable,
> > + * 01/10-sleep voltage,
> > + * 11-active voltage
> > + */
> > +#define PM880_BUCK1A_SLP_CTRL (0x27)
> > +#define PM880_BUCK1B_SLP_CTRL (0x3c)
> > +#define PM880_BUCK2_SLP_CTRL (0x54)
> > +#define PM880_BUCK3_SLP_CTRL (0x6c)
> > +/* TODO: there are 7 controls bit for buck4~7 */
> > +#define PM880_BUCK4_SLP_CTRL (0x84)
> > +#define PM880_BUCK5_SLP_CTRL (0x94)
> > +#define PM880_BUCK6_SLP_CTRL (0xa4)
> > +#define PM880_BUCK7_SLP_CTRL (0xb4)
> > +
> > +/*
> > + * ldo sleep mode control registers:
> > + * 00-disable,
> > + * 01/10-sleep voltage,
> > + * 11-active voltage
> > + */
> > +#define PM880_LDO1_SLP_CTRL (0x21)
> > +#define PM880_LDO2_SLP_CTRL (0x27)
> > +#define PM880_LDO3_SLP_CTRL (0x2d)
> > +#define PM880_LDO4_SLP_CTRL (0x33)
> > +#define PM880_LDO5_SLP_CTRL (0x39)
> > +#define PM880_LDO6_SLP_CTRL (0x3f)
> > +#define PM880_LDO7_SLP_CTRL (0x45)
> > +#define PM880_LDO8_SLP_CTRL (0x4b)
> > +#define PM880_LDO9_SLP_CTRL (0x51)
> > +#define PM880_LDO10_SLP_CTRL (0x57)
> > +#define PM880_LDO11_SLP_CTRL (0x5d)
> > +#define PM880_LDO12_SLP_CTRL (0x63)
> > +#define PM880_LDO13_SLP_CTRL (0x69)
> > +#define PM880_LDO14_SLP_CTRL (0x6f)
> > +#define PM880_LDO15_SLP_CTRL (0x75)
> > +#define PM880_LDO16_SLP_CTRL (0x7b)
> > +#define PM880_LDO17_SLP_CTRL (0x81)
> > +#define PM880_LDO18_SLP_CTRL (0x87)
>
> Remove the brackets around all of the above.

got it;
>
> > +#endif /*__LINUX_MFD_88PM880_REG_H */
> > diff --git a/include/linux/mfd/88pm880.h b/include/linux/mfd/88pm880.h
> > new file mode 100644
> > index 0000000..94b9e063
> > --- /dev/null
> > +++ b/include/linux/mfd/88pm880.h
> > @@ -0,0 +1,59 @@
> > +/*
> > + * Marvell 88PM880 Interface
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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.
> > + *
> > + * 88pm880 specific configuration: at present it's regulators and dvc part
> > + */
> > +
> > +#ifndef __LINUX_MFD_88PM880_H
> > +#define __LINUX_MFD_88PM880_H
> > +
> > +#include <linux/platform_device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/regmap.h>
> > +#include <linux/atomic.h>
> > +#include <linux/reboot.h>
>
> Why are these required?

My fault, I should remove them;
>
> > +#include "88pm880-reg.h"
>
> <linux/mfd/88pm880.h>
>
> > +enum {
> > + PM880_ID_BUCK1A = 0,
> > + PM880_ID_BUCK2,
> > + PM880_ID_BUCK3,
> > + PM880_ID_BUCK4,
> > + PM880_ID_BUCK5,
> > + PM880_ID_BUCK6,
> > + PM880_ID_BUCK7,
> > +
> > + PM880_ID_BUCK_MAX = 7,
> > +};
> > +
> > +enum {
> > + PM880_ID_LDO1 = 0,
> > + PM880_ID_LDO2,
> > + PM880_ID_LDO3,
> > + PM880_ID_LDO4,
> > + PM880_ID_LDO5,
> > + PM880_ID_LDO6,
> > + PM880_ID_LDO7,
> > + PM880_ID_LDO8,
> > + PM880_ID_LDO9,
> > + PM880_ID_LDO10,
> > + PM880_ID_LDO11,
> > + PM880_ID_LDO12,
> > + PM880_ID_LDO13,
> > + PM880_ID_LDO14 = 13,
> > + PM880_ID_LDO15,
> > + PM880_ID_LDO16 = 15,
> > +
> > + PM880_ID_LDO17 = 16,
> > + PM880_ID_LDO18 = 17,
> > +
> > + PM880_ID_LDO_MAX = 18,
> > +};
> > +#endif /* __LINUX_MFD_88PM880_H */
> > diff --git a/include/linux/mfd/88pm886-reg.h b/include/linux/mfd/88pm886-reg.h
> > new file mode 100644
> > index 0000000..38a7ecd
> > --- /dev/null
> > +++ b/include/linux/mfd/88pm886-reg.h
> > @@ -0,0 +1,59 @@
> > +/*
> > + * Marvell 88PM886 registers
> > + *
> > + * Copyright (C) 2014 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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_88PM886_REG_H
> > +#define __LINUX_MFD_88PM886_REG_H
> > +
> > +#define PM886_BUCK1_VOUT (0xa5)
> > +#define PM886_BUCK1_1_VOUT (0xa6)
> > +#define PM886_BUCK1_2_VOUT (0xa7)
> > +#define PM886_BUCK1_3_VOUT (0xa8)
> > +#define PM886_BUCK1_4_VOUT (0x9a)
> > +#define PM886_BUCK1_5_VOUT (0x9b)
> > +#define PM886_BUCK1_6_VOUT (0x9c)
> > +#define PM886_BUCK1_7_VOUT (0x9d)
> > +
> > +/*
> > + * buck sleep mode control registers:
> > + * 00-disable,
> > + * 01/10-sleep voltage,
> > + * 11-active voltage
> > + */
> > +#define PM886_BUCK1_SLP_CTRL (0xa2)
> > +#define PM886_BUCK2_SLP_CTRL (0xb0)
> > +#define PM886_BUCK3_SLP_CTRL (0xbe)
> > +#define PM886_BUCK4_SLP_CTRL (0xcc)
> > +#define PM886_BUCK5_SLP_CTRL (0xda)
> > +
> > +/*
> > + * ldo sleep mode control registers:
> > + * 00-disable,
> > + * 01/10-sleep voltage,
> > + * 11-active voltage
> > + */
> > +#define PM886_LDO1_SLP_CTRL (0x21)
> > +#define PM886_LDO2_SLP_CTRL (0x27)
> > +#define PM886_LDO3_SLP_CTRL (0x2d)
> > +#define PM886_LDO4_SLP_CTRL (0x33)
> > +#define PM886_LDO5_SLP_CTRL (0x39)
> > +#define PM886_LDO6_SLP_CTRL (0x3f)
> > +#define PM886_LDO7_SLP_CTRL (0x45)
> > +#define PM886_LDO8_SLP_CTRL (0x4b)
> > +#define PM886_LDO9_SLP_CTRL (0x51)
> > +#define PM886_LDO10_SLP_CTRL (0x57)
> > +#define PM886_LDO11_SLP_CTRL (0x5d)
> > +#define PM886_LDO12_SLP_CTRL (0x63)
> > +#define PM886_LDO13_SLP_CTRL (0x69)
> > +#define PM886_LDO14_SLP_CTRL (0x6f)
> > +#define PM886_LDO15_SLP_CTRL (0x75)
> > +#define PM886_LDO16_SLP_CTRL (0x7b)
> > +
> > +#endif
> > diff --git a/include/linux/mfd/88pm886.h b/include/linux/mfd/88pm886.h
> > new file mode 100644
> > index 0000000..9390406
> > --- /dev/null
> > +++ b/include/linux/mfd/88pm886.h
> > @@ -0,0 +1,55 @@
> > +/*
> > + * Marvell 88PM886 Interface
> > + *
> > + * Copyright (C) 2015 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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.
> > + *
> > + * 88pm886 specific configuration: at present it's regulators and dvc part
> > + */
> > +
> > +#ifndef __LINUX_MFD_88PM886_H
> > +#define __LINUX_MFD_88PM886_H
> > +
> > +#include <linux/platform_device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/regmap.h>
> > +#include <linux/atomic.h>
> > +#include <linux/reboot.h>
> > +#include "88pm886-reg.h"
> > +
> > +enum {
> > + PM886_ID_BUCK1 = 0,
> > + PM886_ID_BUCK2,
> > + PM886_ID_BUCK3,
> > + PM886_ID_BUCK4,
> > + PM886_ID_BUCK5,
> > +
> > + PM886_ID_BUCK_MAX = 5,
> > +};
> > +
> > +enum {
> > + PM886_ID_LDO1 = 0,
> > + PM886_ID_LDO2,
> > + PM886_ID_LDO3,
> > + PM886_ID_LDO4,
> > + PM886_ID_LDO5,
> > + PM886_ID_LDO6,
> > + PM886_ID_LDO7,
> > + PM886_ID_LDO8,
> > + PM886_ID_LDO9,
> > + PM886_ID_LDO10,
> > + PM886_ID_LDO11,
> > + PM886_ID_LDO12,
> > + PM886_ID_LDO13,
> > + PM886_ID_LDO14,
> > + PM886_ID_LDO15,
> > + PM886_ID_LDO16 = 15,
> > +
> > + PM886_ID_LDO_MAX = 16,
> > +};
> > +
> > +#endif /* __LINUX_MFD_88PM886_H */
> > diff --git a/include/linux/mfd/88pm88x-reg.h b/include/linux/mfd/88pm88x-reg.h
> > new file mode 100644
> > index 0000000..d767b31
> > --- /dev/null
> > +++ b/include/linux/mfd/88pm88x-reg.h
> > @@ -0,0 +1,118 @@
> > +/*
> > + * Marvell 88PM88X registers
> > + *
> > + * Copyright (C) 2014 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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_88PM88X_REG_H
> > +#define __LINUX_MFD_88PM88X_REG_H
> > +/*
> > + * This file is just used for the common registers,
> > + * which are shared by sub-clients
> > + */
> > +
> > +/*--base page:--------------------------------------------------------------*/
>
> All of the commenting above is superfluous.
>
> > +#define PM88X_ID_REG (0x0)
> > +
> > +#define PM88X_STATUS1 (0x1)
> > +#define PM88X_CHG_DET (1 << 2)
> > +#define PM88X_BAT_DET (1 << 3)
> > +
> > +#define PM88X_MISC_CONFIG1 (0x14)
> > +#define PM88X_LONKEY_RST (1 << 3)
> > +
> > +#define PM88X_WDOG (0x1d)
> > +
> > +#define PM88X_LOWPOWER2 (0x21)
> > +#define PM88X_LOWPOWER4 (0x23)
> > +
> > +/* clk control register */
> > +#define PM88X_CLK_CTRL1 (0x25)
> > +
> > +/* gpio */
> > +#define PM88X_GPIO_CTRL1 (0x30)
> > +#define PM88X_GPIO0_VAL_MSK (0x1 << 0)
> > +#define PM88X_GPIO0_MODE_MSK (0x7 << 1)
> > +#define PM88X_GPIO1_VAL_MSK (0x1 << 4)
> > +#define PM88X_GPIO1_MODE_MSK (0x7 << 5)
> > +#define PM88X_GPIO1_SET_DVC (0x2 << 5)
> > +
> > +#define PM88X_GPIO_CTRL2 (0x31)
> > +#define PM88X_GPIO2_VAL_MSK (0x1 << 0)
> > +#define PM88X_GPIO2_MODE_MSK (0x7 << 1)
> > +
> > +#define PM88X_GPIO_CTRL3 (0x32)
> > +
> > +#define PM88X_GPIO_CTRL4 (0x33)
> > +#define PM88X_GPIO5V_1_VAL_MSK (0x1 << 0)
> > +#define PM88X_GPIO5V_1_MODE_MSK (0x7 << 1)
> > +#define PM88X_GPIO5V_2_VAL_MSK (0x1 << 4)
> > +#define PM88X_GPIO5V_2_MODE_MSK (0x7 << 5)
> > +
> > +#define PM88X_BK_OSC_CTRL1 (0x50)
> > +#define PM88X_BK_OSC_CTRL3 (0x52)
> > +
> > +#define PM88X_RTC_ALARM_CTRL1 (0xd0)
> > +#define PM88X_ALARM_WAKEUP (1 << 4)
> > +#define PM88X_USE_XO (1 << 7)
> > +
> > +#define PM88X_AON_CTRL2 (0xe2)
> > +#define PM88X_AON_CTRL3 (0xe3)
> > +#define PM88X_AON_CTRL4 (0xe4)
> > +#define PM88X_AON_CTRL7 (0xe7)
> > +
> > +/* 0xea, 0xeb, 0xec, 0xed are reserved by RTC */
> > +#define PM88X_RTC_SPARE5 (0xee)
> > +#define PM88X_RTC_SPARE6 (0xef)
> > +/*-------------------------------------------------------------------------*/
> > +
> > +/*--power page:------------------------------------------------------------*/
> > +
> > +/*-------------------------------------------------------------------------*/
> > +
> > +/*--gpadc page:------------------------------------------------------------*/
>
> This type of commenting is messy and unwanted.
>
> > +#define PM88X_GPADC_CONFIG1 (0x1)
> > +
> > +#define PM88X_GPADC_CONFIG2 (0x2)
> > +#define PM88X_GPADC0_MEAS_EN (1 << 2)
> > +#define PM88X_GPADC1_MEAS_EN (1 << 3)
> > +#define PM88X_GPADC2_MEAS_EN (1 << 4)
> > +#define PM88X_GPADC3_MEAS_EN (1 << 5)
> > +
> > +#define PM88X_GPADC_CONFIG3 (0x3)
> > +
> > +#define PM88X_GPADC_CONFIG6 (0x6)
> > +#define PM88X_GPADC_CONFIG8 (0x8)
> > +
> > +#define PM88X_GPADC0_LOW_TH (0x20)
> > +#define PM88X_GPADC1_LOW_TH (0x21)
> > +#define PM88X_GPADC2_LOW_TH (0x22)
> > +#define PM88X_GPADC3_LOW_TH (0x23)
> > +
> > +#define PM88X_GPADC0_UPP_TH (0x30)
> > +#define PM88X_GPADC1_UPP_TH (0x31)
> > +#define PM88X_GPADC2_UPP_TH (0x32)
> > +#define PM88X_GPADC3_UPP_TH (0x33)
> > +
> > +#define PM88X_VBUS_MEAS1 (0x4A)
> > +#define PM88X_GPADC0_MEAS1 (0x54)
> > +#define PM88X_GPADC1_MEAS1 (0x56)
> > +#define PM88X_GPADC2_MEAS1 (0x58)
> > +#define PM88X_GPADC3_MEAS1 (0x5A)
> > +
> > +
> > +/*--charger page:------------------------------------------------------------*/
> > +#define PM88X_CHG_CONFIG1 (0x28)
> > +#define PM88X_CHGBK_CONFIG6 (0x50)
> > +/*-------------------------------------------------------------------------*/
> > +
> > +/*--test page:-------------------------------------------------------------*/
> > +
> > +/*-------------------------------------------------------------------------*/
> > +#endif
> > diff --git a/include/linux/mfd/88pm88x.h b/include/linux/mfd/88pm88x.h
> > new file mode 100644
> > index 0000000..efa2fe6
> > --- /dev/null
> > +++ b/include/linux/mfd/88pm88x.h
> > @@ -0,0 +1,202 @@
> > +/*
> > + * Marvell 88PM88X PMIC Common Interface
> > + *
> > + * Copyright (C) 2014 Marvell International Ltd.
> > + * Yi Zhang <[email protected]>
> > + *
> > + * 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.
> > + *
> > + *
> > + * This file configures the common part of the 88pm88x series PMIC
> > + */
> > +
> > +#ifndef __LINUX_MFD_88PM88X_H
> > +#define __LINUX_MFD_88PM88X_H
> > +
> > +#include <linux/platform_device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/regmap.h>
> > +#include <linux/atomic.h>
> > +#include <linux/reboot.h>
> > +#include "88pm88x-reg.h"
> > +#include "88pm886-reg.h"
>
> Why are all of these required?
>
> > +#define PM88X_RTC_NAME "88pm88x-rtc"
> > +#define PM88X_ONKEY_NAME "88pm88x-onkey"
> > +#define PM88X_CHARGER_NAME "88pm88x-charger"
> > +#define PM88X_BATTERY_NAME "88pm88x-battery"
> > +#define PM88X_HEADSET_NAME "88pm88x-headset"
> > +#define PM88X_VBUS_NAME "88pm88x-vbus"
> > +#define PM88X_CFD_NAME "88pm88x-leds"
> > +#define PM88X_RGB_NAME "88pm88x-rgb"
> > +#define PM88X_GPADC_NAME "88pm88x-gpadc"
> > +#define PM88X_DVC_NAME "88pm88x-dvc"
>
> I would prefer that you just used the name directly.
>
> > +enum pm88x_type {
> > + PM886 = 1,
> > + PM880 = 2,
> > +};
> > +
> > +enum pm88x_pages {
> > + PM88X_BASE_PAGE = 0,
> > + PM88X_LDO_PAGE,
> > + PM88X_GPADC_PAGE,
> > + PM88X_BATTERY_PAGE,
> > + PM88X_BUCK_PAGE = 4,
>
> This is 4 anyway.
>
> > + PM88X_TEST_PAGE = 7,
> > +};
> > +
> > +enum pm88x_gpadc {
> > + PM88X_NO_GPADC = -1,
> > + PM88X_GPADC0 = 0,
> > + PM88X_GPADC1,
> > + PM88X_GPADC2,
> > + PM88X_GPADC3,
> > +};
> > +
> > +/* interrupt number */
> > +enum pm88x_irq_number {
> > + PM88X_IRQ_ONKEY, /* EN1b0 *//* 0 */
>
> At least put a space between the comments.
Yes, you are right, a space between the comments is more readable
>
> > + PM88X_IRQ_EXTON, /* EN1b1 */
> > + PM88X_IRQ_CHG_GOOD, /* EN1b2 */
> > + PM88X_IRQ_BAT_DET, /* EN1b3 */
> > + PM88X_IRQ_RTC, /* EN1b4 */
> > + PM88X_IRQ_CLASSD, /* EN1b5 *//* 5 */
> > + PM88X_IRQ_XO, /* EN1b6 */
> > + PM88X_IRQ_GPIO, /* EN1b7 */
> > +
> > + PM88X_IRQ_VBAT, /* EN2b0 *//* 8 */
> > + /* EN2b1 */
> > + PM88X_IRQ_VBUS, /* EN2b2 */
> > + PM88X_IRQ_ITEMP, /* EN2b3 *//* 10 */
> > + PM88X_IRQ_BUCK_PGOOD, /* EN2b4 */
> > + PM88X_IRQ_LDO_PGOOD, /* EN2b5 */
> > +
> > + PM88X_IRQ_GPADC0, /* EN3b0 */
> > + PM88X_IRQ_GPADC1, /* EN3b1 */
> > + PM88X_IRQ_GPADC2, /* EN3b2 *//* 15 */
> > + PM88X_IRQ_GPADC3, /* EN3b3 */
> > + PM88X_IRQ_MIC_DET, /* EN3b4 */
> > + PM88X_IRQ_HS_DET, /* EN3b5 */
> > + PM88X_IRQ_GND_DET, /* EN3b6 */
> > +
> > + PM88X_IRQ_CHG_FAIL, /* EN4b0 *//* 20 */
> > + PM88X_IRQ_CHG_DONE, /* EN4b1 */
> > + /* EN4b2 */
> > + PM88X_IRQ_CFD_FAIL, /* EN4b3 */
> > + PM88X_IRQ_OTG_FAIL, /* EN4b4 */
> > + PM88X_IRQ_CHG_ILIM, /* EN4b5 *//* 25 */
> > + /* EN4b6 */
> > + PM88X_IRQ_CC, /* EN4b7 *//* 27 */
> > +
> > + PM88X_MAX_IRQ, /* 28 */
> > +};
> > +
> > +/* 3 rgb led indicators */
> > +enum {
> > + PM88X_RGB_LED0,
> > + PM88X_RGB_LED1,
> > + PM88X_RGB_LED2,
> > +};
> > +
> > +/* camera flash/torch */
> > +enum {
> > + PM88X_NO_LED = -1,
> > + PM88X_FLASH_LED = 0,
> > + PM88X_TORCH_LED,
> > +};
> > +
> > +struct pm88x_dvc_ops {
> > + void (*level_to_reg)(u8 level);
> > +};
>
> This appears to be unused.

again, I think I should move it into next patch
>
> > +struct pm88x_buck1_dvc_desc {
> > + u8 current_reg;
> > + int max_level;
> > + int uV_step1;
> > + int uV_step2;
> > + int min_uV;
> > + int mid_uV;
> > + int max_uV;
> > + int mid_reg_val;
> > +};
> >
> > +struct pm88x_dvc {
> > + struct device *dev;
> > + struct pm88x_chip *chip;
> > + struct pm88x_dvc_ops ops;
> > + struct pm88x_buck1_dvc_desc desc;
> > +};
> > +
> > +struct pm88x_chip {
> > + struct i2c_client *client;
> > + struct device *dev;
> > +
> > + struct i2c_client *ldo_page; /* chip client for ldo page */
> > + struct i2c_client *power_page; /* chip client for power page */
> > + struct i2c_client *gpadc_page; /* chip client for gpadc page */
> > + struct i2c_client *battery_page;/* chip client for battery page */
> > + struct i2c_client *buck_page; /* chip client for buck page */
> > + struct i2c_client *test_page; /* chip client for test page */
> > +
> > + struct regmap *base_regmap;
> > + struct regmap *ldo_regmap;
> > + struct regmap *power_regmap;
> > + struct regmap *gpadc_regmap;
> > + struct regmap *battery_regmap;
> > + struct regmap *buck_regmap;
> > + struct regmap *test_regmap;
> > + struct regmap *codec_regmap;
> > +
> > + unsigned short ldo_page_addr; /* ldo page I2C address */
> > + unsigned short power_page_addr; /* power page I2C address */
> > + unsigned short gpadc_page_addr; /* gpadc page I2C address */
> > + unsigned short battery_page_addr;/* battery page I2C address */
> > + unsigned short buck_page_addr; /* buck page I2C address */
> > + unsigned short test_page_addr; /* test page I2C address */
> > +
> > + unsigned int chip_id;
> > + long type; /* specific chip */
> > + int irq;
> > +
> > + int irq_mode; /* write/read clear */
> > + struct regmap_irq_chip_data *irq_data;
> > +
> > + bool rtc_wakeup; /* is it powered up by expired alarm? */
> > + u8 powerdown1; /* save power down reason */
> > + u8 powerdown2;
> > + u8 powerup; /* the reason of power on */
> > +
> > + struct notifier_block reboot_notifier;
> > + struct pm88x_dvc *dvc;
> > +};
>
> This is an awful lot of junk to be held in a device data structure.
> Try to remove as much as you can.

thanks, I will think carefully
>
> > +extern struct regmap_irq_chip pm88x_irq_chip;
> > +extern const struct of_device_id pm88x_of_match[];
> > +
> > +struct pm88x_chip *pm88x_init_chip(struct i2c_client *client);
> > +int pm88x_parse_dt(struct device_node *np, struct pm88x_chip *chip);
> > +
> > +int pm88x_init_pages(struct pm88x_chip *chip);
> > +int pm88x_post_init_chip(struct pm88x_chip *chip);
> > +void pm800_exit_pages(struct pm88x_chip *chip);
> > +
> > +int pm88x_init_subdev(struct pm88x_chip *chip);
> > +long pm88x_of_get_type(struct device *dev);
> > +void pm88x_dev_exit(struct pm88x_chip *chip);
> > +
> > +int pm88x_irq_init(struct pm88x_chip *chip);
> > +int pm88x_irq_exit(struct pm88x_chip *chip);
> > +int pm88x_apply_patch(struct pm88x_chip *chip);
> > +int pm88x_stepping_fixup(struct pm88x_chip *chip);
> > +int pm88x_apply_board_fixup(struct pm88x_chip *chip, struct device_node *np);
> > +
> > +struct pm88x_chip *pm88x_get_chip(void);
> > +void pm88x_set_chip(struct pm88x_chip *chip);
> > +void pm88x_power_off(void);
> > +int pm88x_reboot_notifier_callback(struct notifier_block *nb,
> > + unsigned long code, void *unused);
>
> All of these should probably be moved into a single file, unless you
> have a good reason to spread them out?

thanks; I will modify it;
>
> > +#endif /* __LINUX_MFD_88PM88X_H */
>
> --
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog

2015-07-01 12:20:33

by Lee Jones

[permalink] [raw]
Subject: Re: [PATCH V2 2/2] mfd: 88pm88x: initialize 88pm886/88pm880 base support

On Fri, 26 Jun 2015, Yi Zhang wrote:
> On Thu, Jun 25, 2015 at 09:32:48AM +0100, Lee Jones wrote:
> > On Fri, 12 Jun 2015, Yi Zhang wrote:
> >
> > > 88pm886 and 88pm880 are combo PMIC chip, which integrates
> > > regulator, onkey, rtc, gpadc, charger, fuelgauge function;
> > >
> > > this patch add the basic support for them, adding related resource, such as
> > > interrupt, preparing for the client-device driver
> > >
> > > Signed-off-by: Yi Zhang <[email protected]>
> > > ---
> > > drivers/mfd/88pm880-table.c | 173 ++++++++++++
> > > drivers/mfd/88pm886-table.c | 173 ++++++++++++
> > > drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++++++++++++++++++++
> > > drivers/mfd/88pm88x-i2c.c | 167 ++++++++++++
> > > drivers/mfd/88pm88x-irq.c | 171 ++++++++++++
> > > drivers/mfd/88pm88x.h | 51 ++++
>
> Thanks very much for your patience on such a long patch, it is my
> fault, I will modify according to your precious comments and split
> them in the next version, thanks very much;

You are welcome.

[...] /* Cut 100's of lines where you were agreeing with me */

In future, please cut all of the lines that you agree with me and only
leave in the useful or contentious items.

> > > + chip->ldo_page_addr = client->addr + 1;
> > > + chip->power_page_addr = client->addr + 1;
> > > + chip->gpadc_page_addr = client->addr + 2;
> > > + chip->battery_page_addr = client->addr + 3;
> > > + chip->buck_page_addr = client->addr + 4;
> > > + chip->test_page_addr = client->addr + 7;
> >
> > I have no idea what's going on here, but it's almost certainly not
> > correct. Instead of separating these out per device have a base
> > address and create DEFINES for each of them.
>
> it is because this PMIC has several i2c address, each has different
> function, the offset vesus the client->addr is fixed, the client->addr
> is 0x30 from device tree, then the gpadc_page_addr is 0x32, that is
> why I use client->addr + 2;
>
> you mean the DEFINES are better?

I do mean that, yes. If you don't want supply nodes for each of the
client devices (what is the reason for that?), then just supply the
base address and use DEFINES to obtain the offsets.

[...] /* Lots more 'yes's removed */

> > > + ret = mfd_add_devices(chip->dev, 0, common_cell_devs,
> >
> > What does 0 mean?
>
> I should use -1 here, though it _seems_ works..

Please use the pre-allocated platform DEFINEs for them.

[...]

> > > + for (;;)
> > > + cpu_relax();
> >
> > What's the point of this?
>
> I just would like to busy loop at the end of the power_off callback;

Is that when you're meant to do? Why can't you just return?

[...]

> > > +#define CELL_DEV(_name, _r, _compatible, _id) { \
> > > + .name = _name, \
> > > + .of_compatible = _compatible, \
> > > + .num_resources = ARRAY_SIZE(_r), \
> > > + .resources = _r, \
> > > + .id = _id, \
> > > + }
> >
> > This is not required.
> >
> > If you feel the need for an MFD Cell macro, you probably have too many
> > MFD cells and you have done something wrong..
>
> Yes, I should use one MFD to cover regulators and one MFD to cover
> LEDs, I will remember this lesson; thanks;

That wasn't what I was saying. Just remove the MACRO and add the
information into the cells like all the other MFD drivers do.

[...]

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

2015-07-09 11:52:59

by Yi Zhang

[permalink] [raw]
Subject: Re: [PATCH V2 2/2] mfd: 88pm88x: initialize 88pm886/88pm880 base support

On Wed, Jul 01, 2015 at 01:20:18PM +0100, Lee Jones wrote:
> On Fri, 26 Jun 2015, Yi Zhang wrote:
> > On Thu, Jun 25, 2015 at 09:32:48AM +0100, Lee Jones wrote:
> > > On Fri, 12 Jun 2015, Yi Zhang wrote:
> > >
> > > > 88pm886 and 88pm880 are combo PMIC chip, which integrates
> > > > regulator, onkey, rtc, gpadc, charger, fuelgauge function;
> > > >
> > > > this patch add the basic support for them, adding related resource, such as
> > > > interrupt, preparing for the client-device driver
> > > >
> > > > Signed-off-by: Yi Zhang <[email protected]>
> > > > ---
> > > > drivers/mfd/88pm880-table.c | 173 ++++++++++++
> > > > drivers/mfd/88pm886-table.c | 173 ++++++++++++
> > > > drivers/mfd/88pm88x-core.c | 584 ++++++++++++++++++++++++++++++++++++++++
> > > > drivers/mfd/88pm88x-i2c.c | 167 ++++++++++++
> > > > drivers/mfd/88pm88x-irq.c | 171 ++++++++++++
> > > > drivers/mfd/88pm88x.h | 51 ++++
> >
> > Thanks very much for your patience on such a long patch, it is my
> > fault, I will modify according to your precious comments and split
> > them in the next version, thanks very much;
>
> You are welcome.
>
> [...] /* Cut 100's of lines where you were agreeing with me */
>
> In future, please cut all of the lines that you agree with me and only
> leave in the useful or contentious items.

Thanks, I need to pay attention to the etiquette, reading such a long
mail is absolutly not a good experience, my fault, sorry;
>
> > > > + chip->ldo_page_addr = client->addr + 1;
> > > > + chip->power_page_addr = client->addr + 1;
> > > > + chip->gpadc_page_addr = client->addr + 2;
> > > > + chip->battery_page_addr = client->addr + 3;
> > > > + chip->buck_page_addr = client->addr + 4;
> > > > + chip->test_page_addr = client->addr + 7;
> > >
> > > I have no idea what's going on here, but it's almost certainly not
> > > correct. Instead of separating these out per device have a base
> > > address and create DEFINES for each of them.
> >
> > it is because this PMIC has several i2c address, each has different
> > function, the offset vesus the client->addr is fixed, the client->addr
> > is 0x30 from device tree, then the gpadc_page_addr is 0x32, that is
> > why I use client->addr + 2;
> >
> > you mean the DEFINES are better?
>
> I do mean that, yes. If you don't want supply nodes for each of the
> client devices (what is the reason for that?), then just supply the
> base address and use DEFINES to obtain the offsets.

I got your point.
>
> [...] /* Lots more 'yes's removed */
>
> > > > + ret = mfd_add_devices(chip->dev, 0, common_cell_devs,
> > >
> > > What does 0 mean?
> >
> > I should use -1 here, though it _seems_ works..
>
> Please use the pre-allocated platform DEFINEs for them.

Clear now;
>
> [...]
>
> > > > + for (;;)
> > > > + cpu_relax();
> > >
> > > What's the point of this?
> >
> > I just would like to busy loop at the end of the power_off callback;
>
> Is that when you're meant to do? Why can't you just return?

Just return is a good idea, my intention is trying to invoke the
user's attention that the power off fails..
>
> [...]
>
> > > > +#define CELL_DEV(_name, _r, _compatible, _id) { \
> > > > + .name = _name, \
> > > > + .of_compatible = _compatible, \
> > > > + .num_resources = ARRAY_SIZE(_r), \
> > > > + .resources = _r, \
> > > > + .id = _id, \
> > > > + }
> > >
> > > This is not required.
> > >
> > > If you feel the need for an MFD Cell macro, you probably have too many
> > > MFD cells and you have done something wrong..
> >
> > Yes, I should use one MFD to cover regulators and one MFD to cover
> > LEDs, I will remember this lesson; thanks;
>
> That wasn't what I was saying. Just remove the MACRO and add the
> information into the cells like all the other MFD drivers do.

Got your point, I will remove them and clean up the regulator part;
thanks;
>
> [...]
>
> --
> Lee Jones
> Linaro STMicroelectronics Landing Team Lead
> Linaro.org │ Open source software for ARM SoCs
> Follow Linaro: Facebook | Twitter | Blog