+{
+ const void *blob = gd->fdt_blob;
+ const struct fdt_property *prop;
+ struct udevice *dev = NULL;
+ const char *path;
+ const char *alias;
+ int alias_node, node, offset, ret = 0;
+ int alias_len;
+ int len;
+
+ alias = "pmic";
+ alias_len = strlen(alias);
+
+ alias_node = fdt_path_offset(blob, "/aliases");
+ offset = fdt_first_property_offset(blob, alias_node);
+
+ if (offset < 0) {
+ error("Alias node not found.");
+ return -ENODEV;
+ }
+
+ offset = fdt_first_property_offset(blob, alias_node);
+ for (; offset > 0; offset = fdt_next_property_offset(blob,
offset)) {
+ prop = fdt_get_property_by_offset(blob, offset, &len);
+ if (!len)
+ continue;
+
+ path = fdt_string(blob, fdt32_to_cpu(prop->nameoff));
+
+ if (!strncmp(alias, path, alias_len))
+ node = fdt_path_offset(blob, prop->data);
+ else
+ node = 0;
+
+ if (node <= 0)
+ continue;
+
+ ret = lists_bind_fdt(gd->dm_root, blob, node, &dev);
+ if (ret < 0)
+ continue;
+
+ if (device_probe(dev))
+ error("Device: %s, probe error", dev->name);
+ }
+
+ return 0;
+}
+
+UCLASS_DRIVER(pmic) = {
+ .id = UCLASS_PMIC,
+ .name = "pmic",
+};
diff --git a/drivers/power/pmic_i2c.c b/drivers/power/pmic_i2c.c
new file mode 100644
index 0000000..350d375
--- /dev/null
+++ b/drivers/power/pmic_i2c.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marc...@samsung.com>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majew...@samsung.com>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sba...@denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+ struct pmic_platdata *p;
+ unsigned char buf[4] = { 0 };
+
+ if (!dev)
+ return -ENODEV;
+
+ p = dev->platdata;
+
+ if (pmic_check_reg(p, reg))
+ return -EINVAL;
+
+ I2C_SET_BUS(p->bus);
+
+ switch (pmic_i2c_tx_num) {
+ case 3:
+ if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+ buf[2] = (cpu_to_le32(val) >> 16) & 0xff;
+ buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+ buf[0] = cpu_to_le32(val) & 0xff;
+ } else {
+ buf[0] = (cpu_to_le32(val) >> 16) & 0xff;
+ buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+ buf[2] = cpu_to_le32(val) & 0xff;
+ }
+ break;
+ case 2:
+ if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) {
+ buf[1] = (cpu_to_le32(val) >> 8) & 0xff;
+ buf[0] = cpu_to_le32(val) & 0xff;
+ } else {
+ buf[0] = (cpu_to_le32(val) >> 8) & 0xff;
+ buf[1] = cpu_to_le32(val) & 0xff;
+ }
+ break;
+ case 1:
+ buf[0] = cpu_to_le32(val) & 0xff;
+ break;
+ default:
+ printf("%s: invalid tx_num: %d", __func__,
pmic_i2c_tx_num);
+ return -1;
+ }
+
+ if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+ return -1;
+
+ return 0;
+}
+
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+ struct pmic_platdata *p;
+ unsigned char buf[4] = { 0 };
+ u32 ret_val = 0;
+
+ if (!dev)
+ return -ENODEV;
+
+ p = dev->platdata;
+
+ if (pmic_check_reg(p, reg))
+ return -EINVAL;
+
+ I2C_SET_BUS(p->bus);
+
+ if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num))
+ return -1;
+
+ switch (pmic_i2c_tx_num) {
+ case 3:
+ if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+ ret_val = le32_to_cpu(buf[2] << 16
+ | buf[1] << 8 | buf[0]);
+ else
+ ret_val = le32_to_cpu(buf[0] << 16 |
+ buf[1] << 8 | buf[2]);
+ break;
+ case 2:
+ if (p->byte_order == PMIC_SENSOR_BYTE_ORDER_BIG)
+ ret_val = le32_to_cpu(buf[1] << 8 | buf[0]);
+ else
+ ret_val = le32_to_cpu(buf[0] << 8 | buf[1]);
+ break;
+ case 1:
+ ret_val = le32_to_cpu(buf[0]);
+ break;
+ default:
+ printf("%s: invalid tx_num: %d", __func__,
pmic_i2c_tx_num);
+ return -1;
+ }
+ memcpy(val, &ret_val, sizeof(ret_val));
+
+ return 0;
+}
+
+int pmic_i2c_probe(struct udevice *dev)
+{
+ struct pmic_platdata *p;
+
+ if (!dev)
+ return -ENODEV;
+
+ p = dev->platdata;
+
+ i2c_set_bus_num(p->bus);
+ debug("Bus: %d PMIC:%s probed!\n", p->bus, dev->name);
+ if (i2c_probe(pmic_i2c_addr)) {
+ printf("Can't find PMIC:%s\n", dev->name);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/drivers/power/pmic_spi.c b/drivers/power/pmic_spi.c
new file mode 100644
index 0000000..7851adf
--- /dev/null
+++ b/drivers/power/pmic_spi.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marc...@samsung.com>
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majew...@samsung.com>
+ *
+ * (C) Copyright 2010
+ * Stefano Babic, DENX Software Engineering, sba...@denx.de
+ *
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <power/pmic.h>
+#include <errno.h>
+#include <dm.h>
+#include <spi.h>
+
+static struct spi_slave *slave;
+
+void pmic_spi_free(struct spi_slave *slave)
+{
+ if (slave)
+ spi_free_slave(slave);
+}
+
+struct spi_slave *pmic_spi_probe(struct udevice *dev)
+{
+ struct pmic_platdata *p;
+
+ if (!dev)
+ return NULL;
+
+ p = dev->platdata;
+
+ if (pmic_check_reg(p, reg))
+ return NULL;
+
+ return spi_setup_slave(p->bus,
+ p->hw.spi.cs,
+ p->hw.spi.clk,
+ p->hw.spi.mode);
+}
+
+static int pmic_spi_reg(struct udevice *dev, unsigned reg, unsigned
*val,
+ int write)
+{
+ struct pmic_platdata *p;
+ u32 pmic_tx, pmic_rx;
+ u32 tmp;
+
+ if (!dev)
+ return -EINVAL;
+
+ p = dev->platdata;
+
+ if (pmic_check_reg(p, reg))
+ return -EFAULT;
+
+ if (!slave) {
+ slave = pmic_spi_probe(p);
+
+ if (!slave)
+ return -ENODEV;
+ }
+
+ if (pmic_check_reg(p, reg))
+ return -EFAULT;
+
+ if (spi_claim_bus(slave))
+ return -EIO;
+
+ pmic_tx = p->hw.spi.prepare_tx(reg, val, write);
+
+ tmp = cpu_to_be32(pmic_tx);
+
+ if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+ pmic_spi_flags)) {
+ spi_release_bus(slave);
+ return -EIO;
+ }
+
+ if (write) {
+ pmic_tx = p->hw.spi.prepare_tx(reg, val, 0);
+ tmp = cpu_to_be32(pmic_tx);
+ if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx,
+ pmic_spi_flags)) {
+ spi_release_bus(slave);
+ return -EIO;
+ }
+ }
+
+ spi_release_bus(slave);
+ *val = cpu_to_be32(pmic_rx);
+
+ return 0;
+}
+
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val)
+{
+ struct pmic_platdata *p;
+
+ if (!dev)
+ return -EINVAL;
+
+ p = dev->platdata;
+
+ if (pmic_check_reg(p, reg))
+ return -EFAULT;
+
+ if (pmic_spi_reg(p, reg, &val, 1))
+ return -1;
+
+ return 0;
+}
+
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val)
+{
+ struct pmic_platdata *p;
+
+ if (!dev)
+ return -EINVAL;
+
+ p = dev->platdata;
+
+ if (pmic_check_reg(p, reg))
+ return -EFAULT;
+
+ if (pmic_spi_reg(p, reg, val, 0))
+ return -1;
+
+ return 0;
+}
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index e3e9296..e6d9d39 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -29,6 +29,9 @@ enum uclass_id {
UCLASS_SPI_FLASH, /* SPI flash */
UCLASS_CROS_EC, /* Chrome OS EC */
+ /* PMIC uclass and PMIC related uclasses */
+ UCLASS_PMIC,
+
UCLASS_COUNT,
UCLASS_INVALID = -1,
};
diff --git a/include/power/pmic.h b/include/power/pmic.h
index afbc5aa..7114650 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marc...@samsung.com>
+ *
* Copyright (C) 2011-2012 Samsung Electronics
* Lukasz Majewski <l.majew...@samsung.com>
*
@@ -8,9 +11,12 @@
#ifndef __CORE_PMIC_H_
#define __CORE_PMIC_H_
+#include <dm.h>
#include <linux/list.h>
+#include <spi.h>
#include <i2c.h>
#include <power/power_chrg.h>
+#include <power/regulator.h>
enum { PMIC_I2C, PMIC_SPI, PMIC_NONE};
enum { I2C_PMIC, I2C_NUM, };
@@ -78,6 +84,63 @@ struct pmic {
struct list_head list;
};
+#ifdef CONFIG_DM_PMIC
+/* struct pmic_platdata - a standard descriptor for pmic device, which
holds
+ * an informations about interface. It is common for all pmic devices.
+ *
+ * Note:
+ * Interface fields are the same as in: struct pmic.
+ * Note: struct pmic will be removed in the future after drivers
migration
+ *
+ * @bus - a physical bus on which device is connected
+ * @interface - an interface type, one of enum: PMIC_I2C, PMIC_SPI,
PMIC_NONE
+ * @byte_order - one of enum: PMIC_SENSOR_BYTE_ORDER*_LITTLE/_BIG
+ * @regs_num - number of device registers
+ * @hw - one of union structure: p_i2c or p_spi
+ * based on @interface field
+*/
+struct pmic_platdata {
+ int bus;
+ int interface;
+ int byte_order;
+ int regs_num;
+ union hw hw;
+};
+
+/* enum pmic_op_type - used for various pmic devices operation calls,
+ * for decrease a number of functions with the same code for read/write
+ * or get/set.
+ *
+ * @PMIC_OP_GET - get operation
+ * @PMIC_OP_SET - set operation
+*/
+enum pmic_op_type {
+ PMIC_OP_GET,
+ PMIC_OP_SET,
+};
+
+/* drivers/power/pmic-uclass.c */
+int power_init_dm(void);
+struct udevice *pmic_get_by_name(int uclass_id, char *name);
+struct udevice *pmic_get_by_interface(int uclass_id, int bus, int
addr_cs);
+const char *pmic_if_str(int interface);
+int pmic_check_reg(struct pmic_platdata *p, unsigned reg);
+int pmic_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_probe(struct udevice *dev, struct spi_slave *spi_slave);
+
+/* drivers/power/pmic_i2c.c */
+int pmic_i2c_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_i2c_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+int pmic_i2c_probe(struct udevice *dev);
+
+/* drivers/power/pmic_spi.c */
+int pmic_spi_reg_write(struct udevice *dev, unsigned reg, unsigned val);
+int pmic_spi_reg_read(struct udevice *dev, unsigned reg, unsigned *val);
+struct spi_slave *pmic_spi_probe(struct udevice *dev);
+#endif /* CONFIG_DM_PMIC */
+
+#ifdef CONFIG_POWER
int pmic_init(unsigned char bus);
int power_init_board(void);
int pmic_dialog_init(unsigned char bus);
@@ -88,6 +151,7 @@ int pmic_probe(struct pmic *p);
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);