platform: starfive: add PMIC power ops in JH7110 visionfive2 board
authorMinda Chen <minda.chen@starfivetech.com>
Thu, 9 Mar 2023 06:19:59 +0000 (14:19 +0800)
committerAnup Patel <anup@brainfault.org>
Thu, 9 Mar 2023 15:41:20 +0000 (21:11 +0530)
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@starfivetech.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
platform/generic/configs/defconfig
platform/generic/starfive/jh7110.c

index 2ad953d3ab05b56aa6eb701f4a5b9af90157cdb9..ee0df38a22ee414fddc7680fc2b82c26d2ea6a6e 100644 (file)
@@ -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
index c665658ac9b7b736367e28b1fdac1a0c4311bcfa..dcd6306be5fc996b0f0a6cdf69df2c5bfce1aff5 100644 (file)
  *
  * Authors:
  *   Wei Liang Lim <weiliang.lim@starfivetech.com>
+ *   Minda Chen <minda.chen@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)
+{
+       unsigned long 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,
 };