diff options
-rw-r--r-- | platform/generic/configs/defconfig | 1 | ||||
-rw-r--r-- | platform/generic/starfive/jh7110.c | 272 |
2 files changed, 273 insertions, 0 deletions
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..dcd6306 100644 --- a/platform/generic/starfive/jh7110.c +++ b/platform/generic/starfive/jh7110.c @@ -5,14 +5,285 @@ * * 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, }; |