Most of i2c PMIC drivers follow the same initialization sequence,
let's generalize it in a common file.

The initialization function finds the PMIC in the device tree, and if
found - registers it in the list of known PMICs and initializes it,
iterating through the table of settings supplied by the caller.

Signed-off-by: Vadim Bendebury <vben...@chromium.org>
Signed-off-by: Leela Krishna Amudala <l.kris...@samsung.com>
Reviewed-by: Doug Anderson <diand...@google.com>
---
 board/samsung/common/board.c     |   23 ++++++++-
 drivers/power/pmic/Makefile      |    1 +
 drivers/power/pmic/pmic_common.c |   97 ++++++++++++++++++++++++++++++++++++++
 drivers/power/power_core.c       |   14 ++++++
 include/power/pmic.h             |   34 +++++++++++++
 5 files changed, 168 insertions(+), 1 deletion(-)
 create mode 100644 drivers/power/pmic/pmic_common.c

diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index 87ca9de..561e270 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -19,6 +19,7 @@
 #include <asm/arch/power.h>
 #include <power/pmic.h>
 #include <power/max77686_pmic.h>
+#include <power/s2mps11_pmic.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -150,6 +151,25 @@ static int board_init_cros_ec_devices(const void *blob)
 }
 #endif
 
+#ifdef CONFIG_POWER_S2MPS11
+int board_init_s2mps11(void)
+{
+       const struct pmic_init_ops pmic_ops[] = {
+               {PMIC_REG_WRITE, S2MPS11_BUCK1_CTRL2, S2MPS11_BUCK_CTRL2_1V},
+               {PMIC_REG_WRITE, S2MPS11_BUCK2_CTRL2,
+                       S2MPS11_BUCK_CTRL2_1_2625V},
+               {PMIC_REG_WRITE, S2MPS11_BUCK3_CTRL2, S2MPS11_BUCK_CTRL2_1V},
+               {PMIC_REG_WRITE, S2MPS11_BUCK4_CTRL2, S2MPS11_BUCK_CTRL2_1V},
+               {PMIC_REG_WRITE, S2MPS11_BUCK6_CTRL2, S2MPS11_BUCK_CTRL2_1V},
+               {PMIC_REG_UPDATE, S2MPS11_REG_RTC_CTRL,
+                       S2MPS11_RTC_CTRL_32KHZ_CP_EN | S2MPS11_RTC_CTRL_JIT},
+               {PMIC_REG_BAIL}
+       };
+
+       return pmic_common_init(COMPAT_SAMSUNG_S2MPS11_PMIC, pmic_ops);
+}
+#endif
+
 #if defined(CONFIG_POWER)
 #ifdef CONFIG_POWER_MAX77686
 static int max77686_init(void)
@@ -255,8 +275,9 @@ int power_init_board(void)
 
 #ifdef CONFIG_POWER_MAX77686
        ret = max77686_init();
+#elif defined(CONFIG_POWER_S2MPS11)
+       ret = board_init_s2mps11();
 #endif
-
        return ret;
 }
 #endif
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index f054470..97b945c 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -13,6 +13,7 @@ COBJS-$(CONFIG_POWER_MAX8998) += pmic_max8998.o
 COBJS-$(CONFIG_POWER_MAX8997) += pmic_max8997.o
 COBJS-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o
 COBJS-$(CONFIG_POWER_MAX77686) += pmic_max77686.o
+COBJS-$(CONFIG_POWER) += pmic_common.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/power/pmic/pmic_common.c b/drivers/power/pmic/pmic_common.c
new file mode 100644
index 0000000..772d1ee
--- /dev/null
+++ b/drivers/power/pmic/pmic_common.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <power/pmic.h>
+#include <power/s2mps11_pmic.h>
+#include <power/max77686_pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static unsigned pmic_number_of_regs(enum fdt_compat_id pmic_compat)
+{
+       switch (pmic_compat) {
+       case COMPAT_SAMSUNG_S2MPS11_PMIC:
+               return S2MPS11_NUM_OF_REGS;
+       default:
+               break;
+       }
+       return 0;
+}
+
+int pmic_common_init(enum fdt_compat_id pmic_compat,
+                    const struct pmic_init_ops *pmic_ops)
+{
+       const void *blob = gd->fdt_blob;
+       struct pmic *p;
+       int node, parent, ret;
+       unsigned number_of_regs = pmic_number_of_regs(pmic_compat);
+       const char *pmic_name, *comma;
+
+       if (!number_of_regs) {
+               printf("%s: %s - not a supported PMIC\n",
+                      __func__, fdtdec_get_compatible(pmic_compat));
+               return -1;
+       }
+
+       node = fdtdec_next_compatible(blob, 0, pmic_compat);
+       if (node < 0) {
+               debug("PMIC: Error %s. No node for %s in device tree\n",
+                     fdt_strerror(node), fdtdec_get_compatible(pmic_compat));
+               return node;
+       }
+
+       pmic_name = fdtdec_get_compatible(pmic_compat);
+       comma = strchr(pmic_name, ',');
+       if (comma)
+               pmic_name = comma + 1;
+
+       p = pmic_alloc();
+
+       if (!p) {
+               printf("%s: POWER allocation error!\n", __func__);
+               return -ENOMEM;
+       }
+       parent = fdt_parent_offset(blob, node);
+       if (parent < 0) {
+               debug("%s: Cannot find node parent\n", __func__);
+               return -1;
+       }
+
+       p->bus = i2c_get_bus_num_fdt(parent);
+       if (p->bus < 0) {
+               debug("%s: Cannot find I2C bus\n", __func__);
+               return -1;
+       }
+       p->hw.i2c.addr = fdtdec_get_int(blob, node, "reg", 9);
+
+       p->name = pmic_name;
+       p->interface = PMIC_I2C;
+       p->hw.i2c.tx_num = 1;
+       p->number_of_regs = number_of_regs;
+       p->compat_id = pmic_compat;
+
+       ret = 0;
+       while ((pmic_ops->reg_op != PMIC_REG_BAIL) && !ret) {
+               if (pmic_ops->reg_op == PMIC_REG_WRITE)
+                       ret = pmic_reg_write(p,
+                                            pmic_ops->reg_addr,
+                                            pmic_ops->reg_value);
+               else
+                       ret = pmic_reg_update(p,
+                                            pmic_ops->reg_addr,
+                                            pmic_ops->reg_value);
+               pmic_ops++;
+       }
+
+       if (ret)
+               printf("%s: Failed accessing reg 0x%x of %s\n",
+                      __func__, pmic_ops[-1].reg_addr, p->name);
+       else
+               printf("PMIC %s initialized\n", p->name);
+       return ret;
+}
diff --git a/drivers/power/power_core.c b/drivers/power/power_core.c
index d79971b..3e5eefa 100644
--- a/drivers/power/power_core.c
+++ b/drivers/power/power_core.c
@@ -205,6 +205,20 @@ int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * 
const argv[])
        return CMD_RET_SUCCESS;
 }
 
+struct pmic *pmic_get_by_id(enum fdt_compat_id pmic_compat)
+{
+       struct pmic *p;
+
+       list_for_each_entry(p, &pmic_list, list) {
+               if (p->compat_id == pmic_compat) {
+                       debug("%s: pmic %s -> 0x%p\n", __func__, p->name, p);
+                       return p;
+               }
+       }
+
+       return NULL;
+}
+
 U_BOOT_CMD(
        pmic,   CONFIG_SYS_MAXARGS, 1, do_pmic,
        "PMIC",
diff --git a/include/power/pmic.h b/include/power/pmic.h
index d17dbdc..f7d3cf4 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -12,6 +12,7 @@
 #include <linux/list.h>
 #include <i2c.h>
 #include <power/power_chrg.h>
+#include <fdtdec.h>
 
 enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
 enum { I2C_PMIC, I2C_NUM, };
@@ -72,6 +73,7 @@ struct pmic {
 
        struct pmic *parent;
        struct list_head list;
+       enum fdt_compat_id compat_id;
 };
 
 int pmic_init(unsigned char bus);
@@ -84,6 +86,38 @@ int pmic_reg_read(struct pmic *p, u32 reg, u32 *val);
 int pmic_reg_write(struct pmic *p, u32 reg, u32 val);
 int pmic_set_output(struct pmic *p, u32 reg, int ldo, int on);
 int pmic_reg_update(struct pmic *p, int reg, uint regval);
+/*
+ * Find registered PMIC based on its compatibility ID.
+ *
+ * @param pmic_compat   compatibility ID of the PMIC to search for.
+ * @return pointer to the relevant 'struct pmic' on success or NULL
+ */
+struct pmic *pmic_get_by_id(enum fdt_compat_id pmic_compat);
+
+enum pmic_reg_op { PMIC_REG_BAIL, PMIC_REG_WRITE, PMIC_REG_UPDATE };
+struct pmic_init_ops {
+       enum pmic_reg_op reg_op;
+       u8      reg_addr;
+       u8      reg_value;
+};
+
+/**
+ * Common function used to intialize an i2c based PMIC.
+ *
+ * This function finds the PMIC in the device tree based on its compatibility
+ * ID. If found, the struct pmic is allocated, initialized and registered.
+ *
+ * Then the table of initialization settings is scanned and the PMIC registers
+ * are set as dictated by the table contents,
+ *
+ * @param pmic_compat   compatibility ID f the PMIC to be initialized.
+ * @param pmic_ops      a pointer to the table containing PMIC initialization
+ *                     settings. The last entry contains reg_op
+ *                     of PMIC_REG_BAIL.
+ * @return zero on success, nonzero on failure
+ */
+int pmic_common_init(enum fdt_compat_id pmic_compat,
+                    const struct pmic_init_ops *pmic_ops);
 
 #define pmic_i2c_addr (p->hw.i2c.addr)
 #define pmic_i2c_tx_num (p->hw.i2c.tx_num)
-- 
1.7.10.4

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to