[PATCH v3 2/2] platform: starfive: add PMIC power ops in JH7110 visionfive2 board

Minda Chen minda.chen at starfivetech.com
Wed Mar 8 22:19:59 PST 2023


add reboot and poweroff support. The whole reboot and shutdown
pm op includes shutdown jh7110 pmu device power domain
and access on board pmic register through I2C.

Signed-off-by: Minda Chen <minda.chen at starfivetech.com>
Reviewed-by: Anup Patel <anup at brainfault.org>
---
 platform/generic/configs/defconfig |   1 +
 platform/generic/starfive/jh7110.c | 272 +++++++++++++++++++++++++++++
 2 files changed, 273 insertions(+)

diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 2ad953d..ee0df38 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -9,6 +9,7 @@ CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_GPIO_STARFIVE=y
 CONFIG_FDT_I2C=y
 CONFIG_FDT_I2C_SIFIVE=y
+CONFIG_FDT_I2C_DW=y
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_IPI_PLICSW=y
diff --git a/platform/generic/starfive/jh7110.c b/platform/generic/starfive/jh7110.c
index c665658..31c6bd3 100644
--- a/platform/generic/starfive/jh7110.c
+++ b/platform/generic/starfive/jh7110.c
@@ -5,14 +5,285 @@
  *
  * Authors:
  *   Wei Liang Lim <weiliang.lim at starfivetech.com>
+ *   Minda Chen <minda.chen at starfivetech.com>
  */
 
 #include <libfdt.h>
 #include <platform_override.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_bitops.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/riscv_io.h>
 #include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/reset/fdt_reset.h>
+#include <sbi_utils/i2c/fdt_i2c.h>
 
+struct pmic {
+	struct i2c_adapter *adapter;
+	u32 dev_addr;
+	const char *compatible;
+};
+
+struct jh7110 {
+	u64 pmu_reg_base;
+	u64 clk_reg_base;
+	u32 i2c_index;
+};
+
+static struct pmic pmic_inst;
+static struct jh7110 jh7110_inst;
 static u32 selected_hartid = -1;
 
+/* PMU 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
+
+#define DEVICE_PD_MASK			0xfc
+#define SYSTOP_CPU_PD_MASK		0x3
+
+#define TIMEOUT_COUNT			100000
+#define AXP15060_POWER_REG		0x32
+#define AXP15060_POWER_OFF_BIT		BIT(7)
+#define AXP15060_RESET_BIT		BIT(6)
+
+#define I2C_APB_CLK_OFFSET		0x228
+#define I2C_APB_CLK_ENABLE_BIT		BIT(31)
+
+static int pm_system_reset_check(u32 type, u32 reason)
+{
+	switch (type) {
+	case SBI_SRST_RESET_TYPE_SHUTDOWN:
+		return 1;
+	case SBI_SRST_RESET_TYPE_COLD_REBOOT:
+		return 255;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int wait_pmu_pd_state(u32 mask)
+{
+	int count = 0;
+	unsigned long addr = jh7110_inst.pmu_reg_base;
+	u32 val;
+
+	do {
+		val = readl((void *)(addr + CURR_POWER_MODE));
+		if (val == mask)
+			return 0;
+
+		sbi_timer_udelay(2);
+		count += 1;
+		if (count == TIMEOUT_COUNT)
+			return SBI_ETIMEDOUT;
+	} while (1);
+}
+
+static int shutdown_device_power_domain(void)
+{
+	unsigned long addr = jh7110_inst.pmu_reg_base;
+	u32 curr_mode;
+	int ret = 0;
+
+	curr_mode = readl((void *)(addr + CURR_POWER_MODE));
+	curr_mode &= DEVICE_PD_MASK;
+
+	if (curr_mode) {
+		writel(curr_mode, (void *)(addr + SW_TURN_OFF_POWER_MODE));
+		writel(SW_MODE_ENCOURAGE_ON, (void *)(addr + SW_ENCOURAGE));
+		writel(SW_MODE_ENCOURAGE_DIS_LO, (void *)(addr + SW_ENCOURAGE));
+		writel(SW_MODE_ENCOURAGE_DIS_HI, (void *)(addr + SW_ENCOURAGE));
+		ret = wait_pmu_pd_state(SYSTOP_CPU_PD_MASK);
+		if (ret)
+			sbi_printf("%s shutdown device power %x error\n",
+				   __func__, curr_mode);
+	}
+	return ret;
+}
+
+static void pmic_ops(struct pmic *pmic, int type)
+{
+	int ret = 0;
+	u8 val;
+
+	ret = shutdown_device_power_domain();
+
+	if (ret)
+		return;
+
+	if (!sbi_strcmp("stf,axp15060-regulator", pmic->compatible)) {
+		ret = i2c_adapter_reg_read(pmic->adapter, pmic->dev_addr,
+					   AXP15060_POWER_REG, &val);
+
+		if (ret) {
+			sbi_printf("%s: cannot read pmic power register\n",
+				   __func__);
+			return;
+		}
+
+		val |= AXP15060_POWER_OFF_BIT;
+		if (type == SBI_SRST_RESET_TYPE_SHUTDOWN)
+			val |= AXP15060_POWER_OFF_BIT;
+		else
+			val |= AXP15060_RESET_BIT;
+
+		ret = i2c_adapter_reg_write(pmic->adapter, pmic->dev_addr,
+					    AXP15060_POWER_REG, val);
+		if (ret)
+			sbi_printf("%s: cannot write pmic power register\n",
+				   __func__);
+	}
+}
+
+static void pmic_i2c_clk_enable(void)
+{
+	u64 clock_base;
+	unsigned int val;
+
+	clock_base = jh7110_inst.clk_reg_base +
+		I2C_APB_CLK_OFFSET +
+		(jh7110_inst.i2c_index << 2);
+
+	val = readl((void *)clock_base);
+
+	if (!val)
+		writel(I2C_APB_CLK_ENABLE_BIT, (void *)(clock_base));
+}
+
+static void pm_system_reset(u32 type, u32 reason)
+{
+	if (pmic_inst.adapter) {
+		switch (type) {
+		case SBI_SRST_RESET_TYPE_SHUTDOWN:
+		case SBI_SRST_RESET_TYPE_COLD_REBOOT:
+			/* i2c clk may be disabled by kernel driver */
+			pmic_i2c_clk_enable();
+			pmic_ops(&pmic_inst, type);
+			break;
+		default:
+			break;
+		}
+	}
+
+	sbi_hart_hang();
+}
+
+static struct sbi_system_reset_device pm_reset = {
+	.name = "pm-reset",
+	.system_reset_check = pm_system_reset_check,
+	.system_reset = pm_system_reset
+};
+
+static int pm_reset_init(void *fdt, int nodeoff,
+			 const struct fdt_match *match)
+{
+	int rc;
+	int i2c_bus;
+	struct i2c_adapter *adapter;
+	u64 addr;
+
+	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
+	if (rc)
+		return rc;
+
+	pmic_inst.dev_addr = addr;
+	pmic_inst.compatible = match->compatible;
+
+	i2c_bus = fdt_parent_offset(fdt, nodeoff);
+	if (i2c_bus < 0)
+		return i2c_bus;
+
+	/* i2c adapter get */
+	rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
+	if (rc)
+		return rc;
+
+	pmic_inst.adapter = adapter;
+
+	sbi_system_reset_add_device(&pm_reset);
+
+	return 0;
+}
+
+static const struct fdt_match pm_reset_match[] = {
+	{ .compatible = "stf,axp15060-regulator", .data = (void *)true },
+	{ },
+};
+
+static struct fdt_reset fdt_reset_pmic = {
+	.match_table = pm_reset_match,
+	.init = pm_reset_init,
+};
+
+static int starfive_jh7110_inst_init(void *fdt)
+{
+	int noff, rc = 0;
+	const char *name;
+	u64 addr;
+
+	noff = fdt_node_offset_by_compatible(fdt, -1, "starfive,jh7110-pmu");
+	if (-1 < noff) {
+		rc = fdt_get_node_addr_size(fdt, noff, 0, &addr, NULL);
+		if (rc)
+			goto err;
+		jh7110_inst.pmu_reg_base = addr;
+	}
+
+	noff = fdt_node_offset_by_compatible(fdt, -1, "starfive,jh7110-clkgen");
+	if (-1 < noff) {
+		rc = fdt_get_node_addr_size(fdt, noff, 0, &addr, NULL);
+		if (rc)
+			goto err;
+		jh7110_inst.clk_reg_base = addr;
+	}
+
+	if (pmic_inst.adapter) {
+		name = fdt_get_name(fdt, pmic_inst.adapter->id, NULL);
+		if (!sbi_strncmp(name, "i2c", 3))
+			jh7110_inst.i2c_index = name[3] - '0';
+		else
+			rc = SBI_EINVAL;
+	}
+err:
+	return rc;
+}
+
+static int starfive_jh7110_final_init(bool cold_boot,
+				      const struct fdt_match *match)
+{
+	void *fdt = fdt_get_address();
+
+	if (cold_boot) {
+		fdt_reset_driver_init(fdt, &fdt_reset_pmic);
+		return starfive_jh7110_inst_init(fdt);
+	}
+
+	return 0;
+}
+
 static bool starfive_jh7110_cold_boot_allowed(u32 hartid,
 				   const struct fdt_match *match)
 {
@@ -44,4 +315,5 @@ const struct platform_override starfive_jh7110 = {
 	.match_table = starfive_jh7110_match,
 	.cold_boot_allowed = starfive_jh7110_cold_boot_allowed,
 	.fw_init = starfive_jh7110_fw_init,
+	.final_init = starfive_jh7110_final_init,
 };
-- 
2.17.1




More information about the opensbi mailing list