[U-Boot] [PATCH 05/19] dm: pmic: add implementation of driver model regulator uclass

Przemyslaw Marczak
2014-10-08T20:48:41Z

This is the implementation of driver model regulator uclass api.
To use it, the CONFIG_DM_PMIC is required with driver implementation,
since it provides pmic devices I/O API.

The regulator framework is based on a 'structure dm_regulator_ops',
which provides all regulator functions call types.

The optional and useful regulator features are two descriptor types:
- struct regulator_desc - describes the regulator name and value limits
should be set by device driver for each regulator number.
- struct regulator_mode_desc - also should be defined as mode array for
each regulator, since regulators supports few modes, at least: ON/OFF.

The regulator driver operations are clear and described in file:
include/power/regulator.h:

Each regulator "struct driver.ops" should point to "struct dm_regulator_ops".
If do, then the drivers can use the regulator api(if implemented):
- pmic_ldo_cnt(...)
- pmic_buck_cnt(...)
- pmic_get_ldo_val(...)
- pmic_set_ldo_val(...)
- pmic_get_ldo_mode(...)
- pmic_set_ldo_mode(...)
- pmic_get_buck_val(...)
- pmic_set_buck_val(...)
- pmic_get_buck_mode(...)
- pmic_set_buck_mode(...)

To get the regulator device we can use two functions:
- pmic_get_by_name(...)
- pmic_get_by_interface(...)

Main files:
- drivers/power/regulator-uclass.c - provides regulator common functions api
- include/power/regulator.h - define all structures required by the regulator

Changes:
- new uclass-id: UCLASS_PMIC_REGULATOR
- new config: CONFIG_DM_REGULATOR

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
---
drivers/power/Makefile | 1 +
drivers/power/regulator-uclass.c | 250 ++++++++++++++++++++++++++++++++++++
include/dm/uclass-id.h | 1 +
include/power/pmic.h | 18 +++
include/power/regulator.h | 267 +++++++++++++++++++++++++++++++++++++++
5 files changed, 537 insertions(+)
create mode 100644 drivers/power/regulator-uclass.c
create mode 100644 include/power/regulator.h

diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 8def501..9a0b8c4 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_POWER_SPI) += power_spi.o
obj-$(CONFIG_DM_PMIC_SPI) += pmic_spi.o
obj-$(CONFIG_DM_PMIC_I2C) += pmic_i2c.o
obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
+obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
diff --git a/drivers/power/regulator-uclass.c b/drivers/power/regulator-uclass.c
new file mode 100644
index 0000000..4c9614e
--- /dev/null
+++ b/drivers/power/regulator-uclass.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <linux/types.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <i2c.h>
+#include <compiler.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int pmic_ldo_cnt(struct udevice *dev)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->get_ldo_cnt)
+ return -EPERM;
+
+ return ops->get_ldo_cnt(dev);
+}
+
+int pmic_buck_cnt(struct udevice *dev)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->get_buck_cnt)
+ return -EPERM;
+
+ return ops->get_buck_cnt(dev);
+}
+
+struct regulator_desc *pmic_ldo_desc(struct udevice *dev, int ldo)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return NULL;
+
+ if (!ops->get_val_desc)
+ return NULL;
+
+ return ops->get_val_desc(dev, DESC_TYPE_LDO, ldo);
+}
+
+struct regulator_desc *pmic_buck_desc(struct udevice *dev, int buck)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return NULL;
+
+ if (!ops->get_val_desc)
+ return NULL;
+
+ return ops->get_val_desc(dev, DESC_TYPE_BUCK, buck);
+}
+
+struct regulator_mode_desc *pmic_ldo_mode_desc(struct udevice *dev, int ldo,
+ int *mode_cnt)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return NULL;
+
+ if (!ops->get_mode_desc_array)
+ return NULL;
+
+ return ops->get_mode_desc_array(dev, DESC_TYPE_LDO, ldo, mode_cnt);
+}
+
+struct regulator_mode_desc *pmic_buck_mode_desc(struct udevice *dev, int buck,
+ int *mode_cnt)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return NULL;
+
+ if (!ops->get_mode_desc_array)
+ return NULL;
+
+ return ops->get_mode_desc_array(dev, DESC_TYPE_BUCK, buck, mode_cnt);
+}
+
+int pmic_get_ldo_val(struct udevice *dev, int ldo)
+{
+ const struct dm_regulator_ops *ops;
+ int val = -1;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->ldo_val)
+ return -EPERM;
+
+ if (ops->ldo_val(dev, PMIC_OP_GET, ldo, &val))
+ return -EIO;
+
+ return val;
+}
+
+int pmic_set_ldo_val(struct udevice *dev, int ldo, int val)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->ldo_val)
+ return -EPERM;
+
+ if (ops->ldo_val(dev, PMIC_OP_SET, ldo, &val))
+ return -EIO;
+
+ return 0;
+}
+
+int pmic_get_ldo_mode(struct udevice *dev, int ldo)
+{
+ const struct dm_regulator_ops *ops;
+ int mode;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->ldo_mode)
+ return -EPERM;
+
+ if (ops->ldo_mode(dev, PMIC_OP_GET, ldo, &mode))
+ return -EIO;
+
+ return mode;
+}
+
+int pmic_set_ldo_mode(struct udevice *dev, int ldo, int mode)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->ldo_mode)
+ return -EPERM;
+
+ if (ops->ldo_mode(dev, PMIC_OP_SET, ldo, &mode))
+ return -EIO;
+
+ return 0;
+}
+
+int pmic_get_buck_val(struct udevice *dev, int buck)
+{
+ const struct dm_regulator_ops *ops;
+ int val;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->buck_val)
+ return -EPERM;
+
+ if (ops->buck_val(dev, PMIC_OP_GET, buck, &val))
+ return -EIO;
+
+ return val;
+}
+
+int pmic_set_buck_val(struct udevice *dev, int buck, int val)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->buck_val)
+ return -EPERM;
+
+ if (ops->buck_val(dev, PMIC_OP_SET, buck, &val))
+ return -EIO;
+
+ return 0;
+}
+
+int pmic_get_buck_mode(struct udevice *dev, int buck)
+{
+ const struct dm_regulator_ops *ops;
+ int mode;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->buck_mode)
+ return -EPERM;
+
+ if (ops->buck_mode(dev, PMIC_OP_GET, buck, &mode))
+ return -EIO;
+
+ return mode;
+}
+
+int pmic_set_buck_mode(struct udevice *dev, int buck, int mode)
+{
+ const struct dm_regulator_ops *ops;
+
+ ops = pmic_get_uclass_ops(dev, UCLASS_PMIC_REGULATOR);
+ if (!ops)
+ return -ENODEV;
+
+ if (!ops->buck_mode)
+ return -EPERM;
+
+ if (ops->buck_mode(dev, PMIC_OP_SET, buck, &mode))
+ return -EIO;
+
+ return 0;
+}
+
+UCLASS_DRIVER(pmic_regulator) = {
+ .id = UCLASS_PMIC_REGULATOR,
+ .name = "regulator",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index e6d9d39..147222c 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -31,6 +31,7 @@ enum uclass_id {

/* PMIC uclass and PMIC related uclasses */
UCLASS_PMIC,
+ UCLASS_PMIC_REGULATOR,

UCLASS_COUNT,
UCLASS_INVALID = -1,
diff --git a/include/power/pmic.h b/include/power/pmic.h
index 7114650..5a06ba4 100644
--- a/include/power/pmic.h
+++ b/include/power/pmic.h
@@ -119,6 +119,24 @@ enum pmic_op_type {
PMIC_OP_SET,
};

+static __inline__ const void *pmic_get_uclass_ops(struct udevice *dev,
+ int uclass_id)
+{
+ const void *ops;
+
+ if (!dev)
+ return NULL;
+
+ if (dev->driver->id != uclass_id)
+ return NULL;
+
+ ops = dev->driver->ops;
+ if (!ops)
+ return NULL;
+
+ return ops;
+}
+
/* drivers/power/pmic-uclass.c */
int power_init_dm(void);
struct udevice *pmic_get_by_name(int uclass_id, char *name);
diff --git a/include/power/regulator.h b/include/power/regulator.h
new file mode 100644
index 0000000..3e2fea6
--- /dev/null
+++ b/include/power/regulator.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2014 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _INCLUDE_REGULATOR_H_
+#define _INCLUDE_REGULATOR_H_
+
+#define DESC_NAME_LEN 20
+
+/* enum regulator_desc_type - used for lbo/buck descriptor calls */
+enum regulator_desc_type {
+ DESC_TYPE_LDO,
+ DESC_TYPE_BUCK,
+};
+
+/**
+ * struct regulator_desc - this structure holds an information about each
+ * ldo/buck voltage limits. There is no "step" voltage value - so driver
+ * should take care of this. This is rather platform specific so can be
+ * taken from the device-tree.
+ *
+ * @type - one of: DESC_TYPE_LDO or DESC_TYPE_BUCK
+ * @number - hardware ldo/buck number
+ * @min_uV - minimum voltage (micro Volts)
+ * @max_uV - maximum voltage (micro Volts)
+ * @name - name of LDO - usually will be taken from device tree and can
+ * describe ldo/buck really connected hardware
+*/
+struct regulator_desc {
+ int type;
+ int number;
+ int min_uV;
+ int max_uV;
+ char name[DESC_NAME_LEN];
+};
+
+/**
+ * struct regulator_mode_desc - this structure holds an information about each
+ * ldo/buck operation modes. Probably in most cases - an array of modes for
+ * each ldo/buck. This will be probably a driver static data since modes
+ * are device specific more than platform specific.
+ *
+ * @type - one of: DESC_TYPE_LDO or DESC_TYPE_BUCK
+ * @val - a driver specific value for this mode
+ * @name - the name of mode - for command purposes
+ */
+struct regulator_mode_desc {
+ int mode;
+ int reg_val;
+ char *name;
+};
+
+/* PMIC regulator device operations */
+struct dm_regulator_ops {
+ /* Number of LDOs and BUCKs */
+ int (*get_ldo_cnt)(struct udevice *dev);
+ int (*get_buck_cnt)(struct udevice *dev);
+
+ /**
+ * LDO and BUCK attribute descriptors can be usually const static data,
+ * but ldo/buck value limits are often defined in the device-tree,
+ * so those are platform dependent attributes.
+ */
+ struct regulator_desc *(*get_val_desc)(struct udevice *dev, int d_type,
+ int d_num);
+
+ /**
+ * LDO/BUCK mode descriptors are device specific, so in this case
+ * we have also the same static data for each driver device.
+ * But each LDO/BUCK regulator can support different modes,
+ * so this is the reason why returns mode descriptors array for each
+ * regulator number.
+ */
+ struct regulator_mode_desc *(*get_mode_desc_array)(struct udevice *dev,
+ int desc_type,
+ int desc_num,
+ int *desc_mode_cnt);
+
+ /**
+ * The 'ldo/reg_val()' function calls should operate on a micro Volts
+ * values, however the regulator commands operates in mili Volts.
+ *
+ * The 'ldo/buck_mode()' function calls as a taken/returned mode value
+ * should always use a 'mode' field value from structure type:
+ * 'regulator_mode_desc'. So for this purpose each driver should defines
+ * its own mode types for friendly use with regulator commands.
+ */
+
+ /* LDO operations */
+ int (*ldo_val)(struct udevice *dev, int operation, int ldo,
+ int *val);
+ int (*ldo_mode)(struct udevice *dev, int operation, int ldo,
+ int *mode);
+
+ /* Buck converters operations */
+ int (*buck_val)(struct udevice *dev, int operation, int buck,
+ int *val);
+ int (*buck_mode)(struct udevice *dev, int operation, int buck,
+ int *mode);
+};
+
+/**
+ * pmic_ldo_cnt - returns the number of driver registered linear regulators
+ * and this is used for pmic regulator commands purposes.
+ *
+ * @dev - pointer to regulator device
+ * Returns: a null or positive number of LDO or negative value of errno.
+ */
+int pmic_ldo_cnt(struct udevice *dev);
+
+/**
+ * pmic_buck_cnt - returns the number of driver registered step-down converters
+ * and this is used for pmic regulator commands purposes.
+ *
+ * @dev - pointer to regulator device
+ * Returns: a null or positive number of BUCK or negative value of errno.
+ */
+int pmic_buck_cnt(struct udevice *dev);
+
+/**
+ * pmic_ldo_desc - returns a pointer to the regulator value descriptor
+ * of a given device LDO number.
+ *
+ * @dev - pointer to regulator device
+ * @ldo - descriptor number: device specific output number
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_desc *pmic_ldo_desc(struct udevice *dev, int ldo);
+
+/**
+ * pmic_buck_desc - returns a pointer to the regulator value descriptor
+ * of a given device BUCK number.
+ *
+ * @dev - pointer to regulator device
+ * @buck - descriptor number: device specific output number
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_desc *pmic_buck_desc(struct udevice *dev, int buck);
+
+/**
+ * pmic_ldo_mode_desc - returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * @mode_cnt - pointer to the returned descriptor array entries
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_mode_desc *pmic_ldo_mode_desc(struct udevice *dev, int ldo,
+ int *mode_cnt);
+
+/**
+ * pmic_buck_mode_desc - returns a pointer to the array of regulator mode
+ * descriptors of a given device regulator type and number.
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * @mode_cnt - pointer to the returned descriptor array entries
+ * Returns: pointer to descriptor if found or NULL on error.
+ */
+struct regulator_mode_desc *pmic_buck_mode_desc(struct udevice *dev, int buck,
+ int *mode_cnt);
+
+/**
+ * pmic_get_ldo_val: returns LDO voltage value of a given device regulator
+ * number.
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * Returns: - ldo number voltage - unit: uV (micro Volts) or -errno if fails
+ */
+int pmic_get_ldo_val(struct udevice *dev, int ldo);
+
+/**
+ * pmic_set_ldo_val: set LDO voltage value of a given device regulator
+ * number to the given value
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * @val - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int pmic_set_ldo_val(struct udevice *dev, int ldo, int val);
+
+/**
+ * pmic_get_ldo_mode: get LDO mode type of a given device regulator
+ * number
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * Returns - mode number on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and reg_val for a raw register value.
+ */
+int pmic_get_ldo_mode(struct udevice *dev, int ldo);
+
+/**
+ * pmic_set_ldo_mode: set given regulator LDO mode to the given mode type
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * @mode - mode type (struct regulator_mode_desc.mode)
+ *
+ * Returns - 0 on success or -errno value if fails
+ * Note:
+ * The regulator driver should take one of defined, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * a mode field for this purpose and reg_val for a raw register value.
+ */
+int pmic_set_ldo_mode(struct udevice *dev, int ldo, int mode);
+
+/**
+ * pmic_get_buck_val: returns BUCK voltage value of a given device regulator
+ * number.
+ *
+ * @dev - pointer to regulator device
+ * @ldo - output number: device specific
+ * Returns - BUCK number voltage - unit: uV (micro Volts) or -errno if fails
+ */
+int pmic_get_buck_val(struct udevice *dev, int buck);
+
+/**
+ * pmic_set_buck_val: set BUCK voltage value of a given device regulator
+ * number to the given value
+ *
+ * @dev - pointer to regulator device
+ * @buck - output number: device specific
+ * @val - voltage value to set - unit: uV (micro Volts)
+ * Returns - 0 on success or -errno val if fails
+ */
+int pmic_set_buck_val(struct udevice *dev, int buck, int val);
+
+/**
+ * pmic_get_buck_mode: get BUCK mode type of a given device regulator
+ * number
+ *
+ * @dev - pointer to regulator device
+ * @buck - output number: device specific
+ * Returns - mode number on success or -errno val if fails
+ * Note:
+ * The regulator driver should return one of defined, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * the 'mode' field for this purpose and the 'reg_val' for a raw register value.
+ */
+int pmic_get_buck_mode(struct udevice *dev, int buck);
+
+/**
+ * pmic_set_buck_mode: set given regulator buck mode to the given mode type
+ *
+ * @dev - pointer to regulator device
+ * @buck - output number: device specific
+ * @mode - mode type (struct regulator_mode_desc.mode)
+ *
+ * Returns - 0 on success or -errno value if fails
+ * Note:
+ * The regulator driver should take one of defined, continuous mode number,
+ * rather than a raw register value. The struct type 'regulator_mode_desc' has
+ * the 'mode' field for this purpose and the 'reg_val' for a raw register value.
+ */
+int pmic_set_buck_mode(struct udevice *dev, int buck, int mode);
+
+#endif /* _INCLUDE_REGULATOR_H_ */
--
1.9.1