power: add power subsystem driver in uboot
authorkeith.zhao <keith.zhao@statfivetech.com>
Wed, 1 Nov 2023 07:13:51 +0000 (16:13 +0900)
committerHoegeun Kwon <hoegeun.kwon@samsung.com>
Fri, 3 Nov 2023 04:59:37 +0000 (13:59 +0900)
add power subsystem in driver,include pmu pmic and regulator
pmu : dc8200 power
pmic : mipi power
regulator : entend power

Signed-off-by: keith.zhao<keith.zhao@statfivetech.com>
[hoegeun.kwon: cherry-pick the commit 520f4c7187b2 from https://github.com/starfive-tech/u-boot/tree/JH7110_VisionFive2_devel]
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
Change-Id: I096a0a3673f11f4b5776e7b7f3ab24f16ddcd1b3

drivers/power/domain/Kconfig
drivers/power/domain/Makefile
drivers/power/domain/starfive-power-domain.c [new file with mode: 0644]
drivers/power/pmic/Kconfig
drivers/power/pmic/Makefile
drivers/power/pmic/pmic_starfive.c [new file with mode: 0644]
drivers/power/regulator/Kconfig
drivers/power/regulator/Makefile
drivers/power/regulator/starfive_regulator.c [new file with mode: 0644]

index 411c210..c3113cd 100644 (file)
@@ -47,6 +47,13 @@ config IMX8MP_HSIOMIX_BLKCTRL
        help
          Enable support for manipulating NXP i.MX8MP on-SoC HSIOMIX block controller.
 
+config STARFIVE_POWER_DOMAIN
+       bool "Enable starfive power domain driver"
+       depends on POWER_DOMAIN
+       help
+         Enable support for pwoer up on-SoC power domains via
+         requests to the ATF.
+
 config MTK_POWER_DOMAIN
        bool "Enable the MediaTek power domain driver"
        depends on POWER_DOMAIN && ARCH_MEDIATEK
index aa5a4ba..68167de 100644 (file)
@@ -19,3 +19,4 @@ obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o
 obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
 obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
 obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o
+obj-$(CONFIG_STARFIVE_POWER_DOMAIN) += starfive-power-domain.o
diff --git a/drivers/power/domain/starfive-power-domain.c b/drivers/power/domain/starfive-power-domain.c
new file mode 100644 (file)
index 0000000..d33c9a5
--- /dev/null
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
+ */
+#include <asm/io.h>
+#include <common.h>
+#include <dm.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <malloc.h>
+#include <power-domain-uclass.h>
+
+#define usleep_range(a, b) udelay((b))
+
+#define MAX_DOMAINS    64
+#define TIMEOUT_US             100000
+
+/* register define */
+#define HW_EVENT_TURN_ON_MASK          0x04
+#define HW_EVENT_TURN_OFF_MASK         0x08
+#define SW_TURN_ON_POWER_MODE          0x0C
+#define SW_TURN_OFF_POWER_MODE         0x10
+#define SW_ENCOURAGE                   0x44
+#define PMU_INT_MASK                   0x48
+#define PCH_BYPASS                     0x4C
+#define PCH_PSTATE                     0x50
+#define PCH_TIMEOUT                    0x54
+#define LP_TIMEOUT                     0x58
+#define HW_TURN_ON_MODE                        0x5C
+#define CURR_POWER_MODE                        0x80
+#define PMU_EVENT_STATUS               0x88
+#define PMU_INT_STATUS                 0x8C
+
+/* sw encourage cfg */
+#define SW_MODE_ENCOURAGE_EN_LO                0x05
+#define SW_MODE_ENCOURAGE_EN_HI                0x50
+#define SW_MODE_ENCOURAGE_DIS_LO       0x0A
+#define SW_MODE_ENCOURAGE_DIS_HI       0xA0
+#define SW_MODE_ENCOURAGE_ON           0xFF
+
+struct sf_power_domain {
+       void __iomem *regs;
+};
+
+static int sf_power_domain_request(struct power_domain *power_domain)
+{
+       if (power_domain->id >= MAX_DOMAINS)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sf_power_domain_free(struct power_domain *power_domain)
+{
+       return 0;
+}
+
+static int sf_power_domain_on(struct power_domain *power_domain)
+{
+       struct sf_power_domain *priv = dev_get_priv(power_domain->dev);
+       uint32_t mode;
+       uint32_t val;
+       uint32_t encourage_lo;
+       uint32_t encourage_hi;
+       int ret;
+       mode = SW_TURN_ON_POWER_MODE;
+       encourage_lo = SW_MODE_ENCOURAGE_EN_LO;
+       encourage_hi = SW_MODE_ENCOURAGE_EN_HI;
+
+       /* write SW_ENCOURAGE to make the configuration take effect */
+       val = __raw_readl(priv->regs + mode) | BIT(power_domain->id);
+
+       __raw_writel(val, priv->regs + mode);
+       __raw_writel(SW_MODE_ENCOURAGE_ON, priv->regs + SW_ENCOURAGE);
+       __raw_writel(encourage_lo, priv->regs + SW_ENCOURAGE);
+       __raw_writel(encourage_hi, priv->regs + SW_ENCOURAGE);
+
+       ret = readl_poll_timeout(priv->regs + CURR_POWER_MODE, val,
+                                       val & BIT(power_domain->id),
+                                       TIMEOUT_US);
+       if (ret) {
+               pr_err("power_on failed");
+               return -ETIMEDOUT;
+       }
+
+
+       return 0;
+}
+
+static int sf_power_domain_off(struct power_domain *power_domain)
+{
+       struct sf_power_domain *priv = dev_get_priv(power_domain->dev);
+       uint32_t mode;
+       uint32_t val;
+       uint32_t encourage_lo;
+       uint32_t encourage_hi;
+       int ret;
+       mode = SW_TURN_OFF_POWER_MODE;
+       encourage_lo = SW_MODE_ENCOURAGE_DIS_LO;
+       encourage_hi = SW_MODE_ENCOURAGE_DIS_HI;
+
+       val = __raw_readl(priv->regs + mode) & ~(BIT(power_domain->id));
+
+       __raw_writel(val, priv->regs + mode);
+       __raw_writel(SW_MODE_ENCOURAGE_ON, priv->regs + SW_ENCOURAGE);
+       __raw_writel(encourage_lo, priv->regs + SW_ENCOURAGE);
+       __raw_writel(encourage_hi, priv->regs + SW_ENCOURAGE);
+
+       ret = readl_poll_timeout(priv->regs + CURR_POWER_MODE, val,
+                                       !(val & BIT(power_domain->id)),
+                                       TIMEOUT_US);
+       if (ret) {
+               pr_err("power_off failed");
+               return -ETIMEDOUT;
+       }
+
+
+       return 0;
+}
+
+
+static int sf_power_domain_probe(struct udevice *dev)
+{
+       struct sf_power_domain *priv = dev_get_priv(dev);
+
+       fdt_addr_t addr;
+       addr = dev_read_addr_index(dev, 0);
+       priv->regs = (void __iomem *)addr;
+
+       if (!priv->regs)
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct udevice_id sf_power_domain_ids[] = {
+       { .compatible = "starfive,jh7110-pmu" },
+       { /* sentinel */ }
+};
+
+struct power_domain_ops sf_power_domain_ops = {
+       .rfree = sf_power_domain_free,
+       .off = sf_power_domain_off,
+       .on = sf_power_domain_on,
+       .request = sf_power_domain_request,
+};
+
+U_BOOT_DRIVER(sf_power_domain) = {
+       .name = "sf_power_domain",
+       .id = UCLASS_POWER_DOMAIN,
+       .of_match = sf_power_domain_ids,
+       .ops = &sf_power_domain_ops,
+       .priv_auto      = sizeof(struct sf_power_domain),
+       .probe = sf_power_domain_probe,
+};
index 176fb07..5765d61 100644 (file)
@@ -377,6 +377,13 @@ config PMIC_TPS65941
        The TPS65941 is a PMIC containing a bunch of SMPS & LDOs.
        This driver binds the pmic children.
 
+config PMIC_STARFIVE
+       bool "Enable driver for STARFIVE PMIC"
+       depends on DM_PMIC
+       help
+       The PMIC contains a bunch of SMPS & LDOs.
+       This driver binds the pmic children.
+
 config PMIC_TPS65219
        bool "Enable driver for Texas Instruments TPS65219 PMIC"
        depends on DM_PMIC
index 0b3b3d6..f2fca32 100644 (file)
@@ -46,3 +46,4 @@ endif
 
 obj-$(CONFIG_$(SPL_)POWER_TPS62362) += pmic_tps62362.o
 obj-$(CONFIG_SPL_POWER_TPS65910) += pmic_tps65910.o
+obj-$(CONFIG_PMIC_STARFIVE) += pmic_starfive.o
diff --git a/drivers/power/pmic/pmic_starfive.c b/drivers/power/pmic/pmic_starfive.c
new file mode 100644 (file)
index 0000000..71e4d2c
--- /dev/null
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Starfive, Inc.
+ * Author:     keith.zhao<keith.zhao@statfivetech.com>
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <dm/device.h>
+
+#define        LP8732          0x0
+#define LP8733         0x1
+
+#define LP873X_LDO_NUM         8
+
+/* Drivers name */
+#define LP873X_LDO_DRIVER      "lp873x_ldo"
+#define LP873X_BUCK_DRIVER     "lp873x_buck"
+
+#define LP873X_BUCK_VOLT_MASK          0xFF
+#define LP873X_BUCK_VOLT_MAX_HEX       0xFF
+#define LP873X_BUCK_VOLT_MAX           3360000
+#define LP873X_BUCK_MODE_MASK          0x1
+
+#define LP873X_LDO_VOLT_MASK    0x1F
+#define LP873X_LDO_VOLT_MAX_HEX 0x19
+#define LP873X_LDO_VOLT_MAX     3300000
+#define LP873X_LDO_MODE_MASK   0x1
+
+static const struct pmic_child_info pmic_children_info[] = {
+       { .prefix = "ldo", .driver = LP873X_LDO_DRIVER },
+       { },
+};
+
+static int lp873x_write(struct udevice *dev, uint reg, const uint8_t *buff,
+                         int len)
+{
+       if (dm_i2c_write(dev, reg, buff, len)) {
+               pr_err("write error to device: %p register: %#x!\n", dev, reg);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int lp873x_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+       if (dm_i2c_read(dev, reg, buff, len)) {
+               pr_err("read error from device: %p register: %#x!\n", dev, reg);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int starfive_bind(struct udevice *dev)
+{
+       ofnode regulators_node;
+       int children;
+
+       regulators_node = dev_read_subnode(dev, "regulators");
+       if (!ofnode_valid(regulators_node)) {
+               printf("%s: %s regulators subnode not found!\n", __func__,
+                     dev->name);
+               return -ENXIO;
+       }
+
+       children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+       if (!children)
+               printf("%s: %s - no child found\n", __func__, dev->name);
+
+       /* Always return success for this device */
+       return 0;
+}
+
+static struct dm_pmic_ops lp873x_ops = {
+       .read = lp873x_read,
+       .write = lp873x_write,
+};
+
+static const struct udevice_id starfive_ids[] = {
+       { .compatible = "starfive,jh7110-evb-regulator", .data = LP8732 },
+       { }
+};
+
+U_BOOT_DRIVER(pmic_starfive) = {
+       .name = "pmic_starfive",
+       .id = UCLASS_PMIC,
+       .of_match = starfive_ids,
+       .bind = starfive_bind,
+       .ops = &lp873x_ops,
+};
index eb5aa38..80a6273 100644 (file)
@@ -310,6 +310,14 @@ config DM_REGULATOR_LP873X
        features for REGULATOR LP873X and the family of LP873X PMICs.
        The driver implements get/set api for: value and enable.
 
+config DM_REGULATOR_STARFIVE
+       bool "Enable driver for STARFIVE PMIC regulators"
+        depends on PMIC_STARFIVE
+       ---help---
+       This enables implementation of driver-model regulator uclass
+       features for REGULATOR LP873X and the family of LP873X PMICs.
+       The driver implements get/set api for: value and enable.
+
 config DM_REGULATOR_LP87565
        bool "Enable driver for LP87565 PMIC regulators"
         depends on PMIC_LP87565
index d9e0cd5..deb62c5 100644 (file)
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o
 obj-$(CONFIG_$(SPL_)DM_REGULATOR_PBIAS) += pbias_regulator.o
 obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o
 obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o
+obj-$(CONFIG_$(SPL_)DM_REGULATOR_STARFIVE) += starfive_regulator.o
 obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
 obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o
 obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o
diff --git a/drivers/power/regulator/starfive_regulator.c b/drivers/power/regulator/starfive_regulator.c
new file mode 100644 (file)
index 0000000..5f0f8bd
--- /dev/null
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Starfive, Inc.
+ * Author:     keith.zhao<keith.zhao@statfivetech.com>
+ */
+
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/lp873x.h>
+
+static const char lp873x_ldo_ctrl[LP873X_LDO_NUM] = {0x8, 0x9};
+static const char lp873x_ldo_volt[LP873X_LDO_NUM] = {0xA, 0xB};
+
+
+static int lp873x_ldo_enable(struct udevice *dev, int op, bool *enable)
+{
+       int ret;
+       unsigned int adr;
+       struct dm_regulator_uclass_plat *uc_pdata;
+
+       uc_pdata = dev_get_uclass_plat(dev);
+       adr = uc_pdata->ctrl_reg;
+
+       ret = pmic_reg_read(dev->parent, adr);
+       if (ret < 0)
+               return ret;
+
+       if (op == PMIC_OP_GET) {
+               ret &= LP873X_LDO_MODE_MASK;
+
+               if (ret)
+                       *enable = true;
+               else
+                       *enable = false;
+
+               return 0;
+       } else if (op == PMIC_OP_SET) {
+               if (*enable)
+                       ret |= LP873X_LDO_MODE_MASK;
+               else
+                       ret &= ~(LP873X_LDO_MODE_MASK);
+
+               ret = pmic_reg_write(dev->parent, adr, ret);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int lp873x_ldo_volt2hex(int uV)
+{
+       if (uV > LP873X_LDO_VOLT_MAX)
+               return -EINVAL;
+
+       return (uV - 800000) / 100000;
+}
+
+static int lp873x_ldo_hex2volt(int hex)
+{
+       if (hex > LP873X_LDO_VOLT_MAX_HEX)
+               return -EINVAL;
+
+       if (!hex)
+               return 0;
+
+       return (hex * 100000) + 800000;
+}
+
+static int lp873x_ldo_val(struct udevice *dev, int op, int *uV)
+{
+       unsigned int hex, adr;
+       int ret;
+
+       struct dm_regulator_uclass_plat *uc_pdata;
+
+       if (op == PMIC_OP_GET)
+               *uV = 0;
+
+       uc_pdata = dev_get_uclass_plat(dev);
+
+       adr = uc_pdata->volt_reg;
+
+       ret = pmic_reg_read(dev->parent, adr);
+       if (ret < 0)
+               return ret;
+
+       if (op == PMIC_OP_GET) {
+               ret &= LP873X_LDO_VOLT_MASK;
+               ret = lp873x_ldo_hex2volt(ret);
+               if (ret < 0)
+                       return ret;
+               *uV = ret;
+               return 0;
+       }
+
+       hex = lp873x_ldo_volt2hex(*uV);
+       if (hex < 0)
+               return hex;
+
+       ret &= ~LP873X_LDO_VOLT_MASK;
+       ret |= hex;
+       if (*uV > 1650000)
+               ret |= 0x80;
+       ret = pmic_reg_write(dev->parent, adr, ret);
+
+       return ret;
+}
+
+static int lp873x_ldo_probe(struct udevice *dev)
+{
+       struct dm_regulator_uclass_plat *uc_pdata;
+
+       uc_pdata = dev_get_uclass_plat(dev);
+       uc_pdata->type = REGULATOR_TYPE_LDO;
+
+       int idx = dev->driver_data;
+       if (idx >= LP873X_LDO_NUM) {
+               return -1;
+       }
+
+       uc_pdata->ctrl_reg = lp873x_ldo_ctrl[idx];
+       uc_pdata->volt_reg = lp873x_ldo_volt[idx];
+
+       return 0;
+}
+
+static int ldo_get_value(struct udevice *dev)
+{
+       int uV;
+       int ret;
+
+       ret = lp873x_ldo_val(dev, PMIC_OP_GET, &uV);
+       if (ret)
+               return ret;
+
+       return uV;
+}
+
+static int ldo_set_value(struct udevice *dev, int uV)
+{
+       return lp873x_ldo_val(dev, PMIC_OP_SET, &uV);
+}
+
+static int ldo_get_enable(struct udevice *dev)
+{
+       bool enable = false;
+       int ret;
+
+       ret = lp873x_ldo_enable(dev, PMIC_OP_GET, &enable);
+       if (ret)
+               return ret;
+
+       return enable;
+}
+
+static int ldo_set_enable(struct udevice *dev, bool enable)
+{
+       return lp873x_ldo_enable(dev, PMIC_OP_SET, &enable);
+}
+
+static const struct dm_regulator_ops lp873x_ldo_ops = {
+       .get_value  = ldo_get_value,
+       .set_value  = ldo_set_value,
+       .get_enable = ldo_get_enable,
+       .set_enable = ldo_set_enable,
+};
+
+U_BOOT_DRIVER(lp873x_ldo) = {
+       .name = LP873X_LDO_DRIVER,
+       .id = UCLASS_REGULATOR,
+       .ops = &lp873x_ldo_ops,
+       .probe = lp873x_ldo_probe,
+};