[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