diff --git a/bsp/rockchip/dm/Kconfig b/bsp/rockchip/dm/Kconfig index 976548e8eb..36d8d27cbb 100755 --- a/bsp/rockchip/dm/Kconfig +++ b/bsp/rockchip/dm/Kconfig @@ -1,6 +1,9 @@ SOC_DM_ADC_DIR = $(SOC_DM_DIR)/adc SOC_DM_CAN_DIR = $(SOC_DM_DIR)/can SOC_DM_CLK_DIR = $(SOC_DM_DIR)/clk +SOC_DM_DVFS_EVENT_DIR = $(SOC_DM_DIR)/dvfs/event +SOC_DM_DVFS_CPUFREQ_DIR = $(SOC_DM_DIR)/dvfs/cpufreq +SOC_DM_DVFS_DEVFREQ_DIR = $(SOC_DM_DIR)/dvfs/devfreq SOC_DM_HWCRYPTO_DIR = $(SOC_DM_DIR)/hwcrypto SOC_DM_HWSPINLOCK_DIR = $(SOC_DM_DIR)/hwspinlock SOC_DM_CLOCK_TIME_DIR = $(SOC_DM_DIR)/hwtimer diff --git a/bsp/rockchip/dm/dvfs/SConscript b/bsp/rockchip/dm/dvfs/SConscript new file mode 100755 index 0000000000..07281ac7e7 --- /dev/null +++ b/bsp/rockchip/dm/dvfs/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DVFS']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = [] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + group = group + SConscript(os.path.join(d, 'SConscript')) + +Return('group') diff --git a/bsp/rockchip/dm/dvfs/cpufreq/Kconfig b/bsp/rockchip/dm/dvfs/cpufreq/Kconfig new file mode 100755 index 0000000000..29be68288d --- /dev/null +++ b/bsp/rockchip/dm/dvfs/cpufreq/Kconfig @@ -0,0 +1,10 @@ +config RT_DVFS_ROCKCHIP_CPUFREQ + bool "Rockchip CPUfreq driver" + depends on RT_USING_DVFS + depends on RT_USING_OFW + default y + help + Rockchip CPUfreq with OPP table parsing and optional nvmem + bin/leakage voltage selection. Uses SCMI clock and DT cpu-supply + regulator (e.g. FAN53555). Thermal throttling uses + thermal-cool-dvfs on the cpufreq device. diff --git a/bsp/rockchip/dm/dvfs/cpufreq/SConscript b/bsp/rockchip/dm/dvfs/cpufreq/SConscript new file mode 100644 index 0000000000..e6e8825012 --- /dev/null +++ b/bsp/rockchip/dm/dvfs/cpufreq/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +if GetDepend(['RT_DVFS_ROCKCHIP_CPUFREQ']): + src += ['dvfs-rockchip-cpufreq.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/rockchip/dm/dvfs/cpufreq/dvfs-rockchip-cpufreq.c b/bsp/rockchip/dm/dvfs/cpufreq/dvfs-rockchip-cpufreq.c new file mode 100644 index 0000000000..f0e4b9e356 --- /dev/null +++ b/bsp/rockchip/dm/dvfs/cpufreq/dvfs-rockchip-cpufreq.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "dvfs.cpufreq" +#define DBG_LVL DBG_INFO +#include + +#include + +struct cpufreq_cluster +{ + rt_list_t list; + struct rt_dvfs_cpufreq cpufreq; + struct opp_info opp_info; + struct rt_platform_device *pdev; +#ifdef RT_THERMAL_COOL_DVFS + struct rt_platform_device *cool_pdev; +#endif + RT_BITMAP_DECLARE(cpus_map, RT_CPUS_NR); +}; + +static rt_list_t cluster_list = RT_LIST_OBJECT_INIT(cluster_list); + +static struct cpufreq_cluster *cluster_by_opp(struct rt_ofw_node *opp_np) +{ + struct cpufreq_cluster *cluster; + + if (!opp_np) + { + return RT_NULL; + } + + rt_list_for_each_entry(cluster, &cluster_list, list) + { + if (cluster->opp_info.opp_np == opp_np) + { + return cluster; + } + } + + return RT_NULL; +} + +static rt_bool_t cpu_shares_opp(struct rt_ofw_node *cpu_np, + struct rt_ofw_node *opp_np) +{ + struct rt_ofw_node *cpu_opp; + + cpu_opp = rt_ofw_parse_phandle(cpu_np, "operating-points-v2", 0); + if (!cpu_opp) + { + return RT_FALSE; + } + + if (cpu_opp == opp_np) + { + rt_ofw_node_put(cpu_opp); + return RT_TRUE; + } + + rt_ofw_node_put(cpu_opp); + return RT_FALSE; +} + +static rt_err_t scaling_set_opp(struct rt_dvfs_scaling *scaling, + struct rt_dvfs_opp *opp) +{ + struct cpufreq_cluster *cluster = scaling->priv; + + return cpufreq_set_opp(scaling, opp, &cluster->opp_info); +} + +static rt_err_t cpufreq_parse_opp(struct rt_dvfs_scaling *dvfs, + struct rt_dvfs_opp *opp, void *fw_np) +{ + struct cpufreq_cluster *cluster = dvfs->priv; + + return opp_parse(dvfs, opp, (struct rt_ofw_node *)fw_np, &cluster->opp_info); +} + +static struct rt_dvfs_scaling_ops cpufreq_ops = +{ + .set_opp = scaling_set_opp, + .parse_opp = cpufreq_parse_opp, +}; + +#ifdef RT_THERMAL_COOL_DVFS +static rt_err_t cluster_cool_register(struct cpufreq_cluster *cluster, + struct rt_ofw_node *cpu_np, struct rt_dvfs_scaling *scaling) +{ + rt_err_t err; + struct rt_platform_device *cool; + + if (!rt_ofw_prop_read_bool(cpu_np, "#cooling-cells")) + { + return RT_EOK; + } + + if (!(cool = rt_platform_device_alloc("dvfs-cool"))) + { + return -RT_ENOMEM; + } + + cool->parent.ofw_node = cpu_np; + rt_ofw_node_get(cpu_np); + cool->priv = scaling; + + if ((err = rt_platform_device_register(cool))) + { + rt_ofw_node_put(cpu_np); + rt_free(cool); + return err; + } + + cluster->cool_pdev = cool; + + return RT_EOK; +} + +static void cluster_cool_unregister(struct cpufreq_cluster *cluster) +{ + struct rt_platform_device *cool = cluster->cool_pdev; + + if (!cool) + { + return; + } + + rt_bus_remove_device(&cool->parent); + rt_ofw_node_put(cool->parent.ofw_node); + rt_free(cool); + cluster->cool_pdev = RT_NULL; +} +#endif /* RT_THERMAL_COOL_DVFS */ + +static void cluster_destroy(struct cpufreq_cluster *cluster, + struct rt_device *dev, struct rt_dvfs_scaling *scaling) +{ +#ifdef RT_THERMAL_COOL_DVFS + cluster_cool_unregister(cluster); +#endif + + if (scaling) + { + rt_dvfs_cpufreq_unregister(&cluster->cpufreq); + } + + if (dev) + { + opp_table_uninit(dev, &cluster->opp_info); + rt_device_unregister(dev); + rt_ofw_node_put(dev->ofw_node); + } + + if (cluster->pdev) + { + rt_free(cluster->pdev); + cluster->pdev = RT_NULL; + } +} + +static rt_err_t cluster_init(struct cpufreq_cluster *cluster, + struct rt_ofw_node *cpu_np, struct rt_ofw_node *opp_np) +{ + rt_err_t err; + const char *reg_name; + struct rt_device *dev; + struct rt_dvfs_scaling *scaling; + struct rt_platform_device *pdev; + + if (!(reg_name = regulator_name(cpu_np))) + { + LOG_W("%s: no cpu supply property, skip cluster", + rt_ofw_node_full_name(cpu_np)); + return -RT_ENOENT; + } + + if (!(pdev = rt_platform_device_alloc("cpufreq"))) + { + return -RT_ENOMEM; + } + + dev = &pdev->parent; + dev->ofw_node = cpu_np; + rt_ofw_node_get(cpu_np); + + rt_dm_dev_set_name_auto(dev, "cpufreq"); + + if ((err = rt_device_register(dev, rt_dm_dev_get_name(dev), RT_DEVICE_FLAG_DEACTIVATE))) + { + rt_ofw_node_put(cpu_np); + rt_free(pdev); + return err; + } + + scaling = rt_dvfs_cpufreq_to_scaling(&cluster->cpufreq); + scaling->dev = dev; + scaling->ops = &cpufreq_ops; + scaling->priv = cluster; + + if ((err = opp_table_init(dev, &cluster->opp_info, reg_name))) + { + goto _fail; + } + + if (cluster->opp_info.opp_np != opp_np) + { + rt_ofw_node_put(cluster->opp_info.opp_np); + cluster->opp_info.opp_np = opp_np; + rt_ofw_node_get(opp_np); + } + + scaling->clk = cluster->opp_info.clk; + scaling->supply = cluster->opp_info.supply; + + if (!scaling->transition_latency) + { + scaling->transition_latency = 1000000; + } + + if ((err = rt_dvfs_cpufreq_register(&cluster->cpufreq))) + { + goto _fail; + } + + if ((err = opp_table_adjust(dev, &cluster->opp_info, scaling))) + { + goto _fail_scaling; + } + + if ((err = cpufreq_sync_hw_state(scaling))) + { + LOG_W("%s: sync hw state failed: %s, cpufreq may retry regulator", + rt_dm_dev_get_name(dev), rt_strerror(err)); + } + + if ((err = rt_dvfs_scaling_set_governor(scaling, RT_DVFS_GOVERNOR_TYPE_ONDEMAND))) + { + rt_dvfs_scaling_set_governor(scaling, RT_DVFS_GOVERNOR_TYPE_PERFORMANCE); + } +#ifdef RT_THERMAL_COOL_DVFS + if ((err = cluster_cool_register(cluster, cpu_np, scaling))) + { + LOG_W("%s: dvfs-cool register failed: %s", + rt_dm_dev_get_name(dev), rt_strerror(err)); + } +#endif + + cluster->pdev = pdev; + rt_list_insert_before(&cluster_list, &cluster->list); + + LOG_D("CPUFreq cluster %s %lu-%lu Hz on %s", + rt_ofw_node_full_name(cpu_np), + scaling->min_freq, scaling->max_freq, + rt_dm_dev_get_name(dev)); + + return RT_EOK; + +_fail_scaling: + rt_dvfs_cpufreq_unregister(&cluster->cpufreq); +_fail: + cluster_destroy(cluster, dev, RT_NULL); + return err; +} + +static int rockchip_dvfs_cpufreq_init(void) +{ + rt_err_t err; + int cpu_id = 0, registered = 0; + struct rt_ofw_node *cpu_np = RT_NULL; + struct cpufreq_cluster *cluster; + struct rt_ofw_node *opp_np; + + rt_ofw_foreach_cpu_node(cpu_np) + { + int share_id = 0; + struct rt_ofw_node *share_np = RT_NULL; + + if (!(opp_np = rt_ofw_parse_phandle(cpu_np, "operating-points-v2", 0))) + { + ++cpu_id; + continue; + } + + if (cluster_by_opp(opp_np)) + { + rt_ofw_node_put(opp_np); + ++cpu_id; + continue; + } + + if (!(cluster = rt_calloc(1, sizeof(*cluster)))) + { + rt_ofw_node_put(opp_np); + return -RT_ENOMEM; + } + + cluster->cpufreq.master_cpu = cpu_id; + rt_bitmap_set_bit(cluster->cpus_map, cpu_id); + + rt_ofw_foreach_cpu_node(share_np) + { + if (cpu_shares_opp(share_np, opp_np)) + { + rt_bitmap_set_bit(cluster->cpus_map, share_id); + } + ++share_id; + } + + rt_memcpy(cluster->cpufreq.cpus_map, cluster->cpus_map, + sizeof(cluster->cpus_map)); + + err = cluster_init(cluster, cpu_np, opp_np); + rt_ofw_node_put(opp_np); + + if (err) + { + LOG_W("CPUFreq cluster %s init failed: %s", + rt_ofw_node_full_name(cpu_np), rt_strerror(err)); + rt_free(cluster); + } + else + { + ++registered; + } + + ++cpu_id; + } + + if (!registered) + { + return (int)-RT_ENOENT; + } + + return (int)RT_EOK; +} +INIT_APP_EXPORT(rockchip_dvfs_cpufreq_init); diff --git a/bsp/rockchip/dm/dvfs/devfreq/Kconfig b/bsp/rockchip/dm/dvfs/devfreq/Kconfig new file mode 100755 index 0000000000..75d717f76a --- /dev/null +++ b/bsp/rockchip/dm/dvfs/devfreq/Kconfig @@ -0,0 +1,10 @@ +config RT_DVFS_ROCKCHIP_DMC + bool "Rockchip DMC devfreq (DFI event test stub)" + depends on RT_USING_DVFS + depends on RT_USING_DVFS_EVENT + select RT_DVFS_EVENT_ROCKCHIP_DFI + default n + help + Minimal devfreq on the DMC node to bind DFI event and verify + devfreq_load_from_event. Does not perform ATF/SIP DDR scaling. + diff --git a/bsp/rockchip/dm/dvfs/devfreq/SConscript b/bsp/rockchip/dm/dvfs/devfreq/SConscript new file mode 100644 index 0000000000..0277d3f7cb --- /dev/null +++ b/bsp/rockchip/dm/dvfs/devfreq/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +if GetDepend(['RT_DVFS_ROCKCHIP_DMC']): + src += ['dvfs-rockchip-dmc.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/rockchip/dm/dvfs/devfreq/dvfs-rockchip-dmc.c b/bsp/rockchip/dm/dvfs/devfreq/dvfs-rockchip-dmc.c new file mode 100755 index 0000000000..4db7f57f80 --- /dev/null +++ b/bsp/rockchip/dm/dvfs/devfreq/dvfs-rockchip-dmc.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "dvfs.rockchip.dmc" +#define DBG_LVL DBG_INFO +#include + +struct rockchip_dmcfreq +{ + struct rt_dvfs_devfreq devfreq; + struct rt_platform_device *pdev; +}; + +static const struct rt_ofw_node_id dfi_compat_ids[] = +{ + { .compatible = "rockchip,rk3568-dfi" }, + { .compatible = "rockchip,rk3562-dfi" }, + { .compatible = "rockchip,px30-dfi" }, + { /* sentinel */ } +}; + +static rt_err_t rockchip_dmc_bind_dfi_event(struct rockchip_dmcfreq *dmc, + struct rt_device *dev) +{ + struct rt_ofw_node *np = dev->ofw_node, *ev_np; + + for (int i = 0; ; ++i) + { + if (!(ev_np = rt_ofw_parse_phandle(np, "devfreq-events", i))) + { + break; + } + + if (rt_ofw_node_match(ev_np, dfi_compat_ids)) + { + dmc->devfreq.ev = rt_ofw_data(ev_np); + } + + rt_ofw_node_put(ev_np); + + if (dmc->devfreq.ev) + { + break; + } + } + + if (!dmc->devfreq.ev) + { + LOG_W("%s: no DFI devfreq-event", rt_dm_dev_get_name(dev)); + return -RT_ENOENT; + } + + return RT_EOK; +} + +static rt_err_t rockchip_dmc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rockchip_dmcfreq *dmc; + struct rt_dvfs_scaling *scaling; + + if (!(dmc = rt_calloc(1, sizeof(*dmc)))) + { + return -RT_ENOMEM; + } + + dmc->pdev = pdev; + scaling = rt_dvfs_devfreq_to_scaling(&dmc->devfreq); + + rt_dm_dev_set_name_auto(dev, "dmc"); + + if ((err = rt_device_register(dev, rt_dm_dev_get_name(dev), RT_DEVICE_FLAG_DEACTIVATE))) + { + rt_free(dmc); + return err; + } + + scaling->dev = dev; + scaling->ops = &rt_dvfs_devfreq_ops; + scaling->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(scaling->clk)) + { + LOG_W("%s: no dmc clk, event test only", rt_dm_dev_get_name(dev)); + scaling->clk = RT_NULL; + } + + scaling->supply = rt_regulator_get(dev, "center"); + if (rt_is_err(scaling->supply)) + { + scaling->supply = RT_NULL; + } + + if ((err = rockchip_dmc_bind_dfi_event(dmc, dev))) + { + goto _fail; + } + + if ((err = rt_dvfs_devfreq_register(&dmc->devfreq))) + { + goto _fail; + } + + if ((err = rt_dvfs_scaling_set_governor(scaling, RT_DVFS_GOVERNOR_TYPE_ONDEMAND))) + { + LOG_W("%s: ondemand failed: %s", rt_dm_dev_get_name(dev), rt_strerror(err)); + } + + pdev->priv = dmc; + + LOG_I("%s: event-only devfreq ready, freq %lu-%lu Hz", + rt_dm_dev_get_name(dev), scaling->min_freq, scaling->max_freq); + + return RT_EOK; + +_fail: + if (scaling->clk) + { + rt_clk_put(scaling->clk); + } + if (scaling->supply) + { + rt_regulator_put(scaling->supply); + } + rt_device_unregister(dev); + rt_free(dmc); + return err; +} + +static rt_err_t rockchip_dmc_remove(struct rt_platform_device *pdev) +{ + struct rockchip_dmcfreq *dmc = pdev->priv; + + if (!dmc) + { + return RT_EOK; + } + + rt_dvfs_devfreq_unregister(&dmc->devfreq); + + struct rt_dvfs_scaling *scaling = rt_dvfs_devfreq_to_scaling(&dmc->devfreq); + if (scaling->clk) + { + rt_clk_put(scaling->clk); + } + if (scaling->supply) + { + rt_regulator_put(scaling->supply); + } + + rt_device_unregister(&pdev->parent); + rt_free(dmc); + pdev->priv = RT_NULL; + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_dmc_ofw_ids[] = +{ + { .compatible = "rockchip,rk3568-dmc" }, + { .compatible = "rockchip,rk3562-dmc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_dmc_driver = +{ + .name = "rockchip-dmc", + .ids = rockchip_dmc_ofw_ids, + .probe = rockchip_dmc_probe, + .remove = rockchip_dmc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(rockchip_dmc_driver); diff --git a/bsp/rockchip/dm/dvfs/event/Kconfig b/bsp/rockchip/dm/dvfs/event/Kconfig new file mode 100755 index 0000000000..9b711e616e --- /dev/null +++ b/bsp/rockchip/dm/dvfs/event/Kconfig @@ -0,0 +1,4 @@ +config RT_DVFS_EVENT_ROCKCHIP_DFI + bool "Rockchip DFI Devfreq event driver" + depends on RT_USING_DVFS_EVENT + default n diff --git a/bsp/rockchip/dm/dvfs/event/SConscript b/bsp/rockchip/dm/dvfs/event/SConscript new file mode 100644 index 0000000000..1cc3da291e --- /dev/null +++ b/bsp/rockchip/dm/dvfs/event/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +if GetDepend(['RT_DVFS_EVENT_ROCKCHIP_DFI']): + src += ['event-rockchip-dfi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/rockchip/dm/dvfs/event/event-rockchip-dfi.c b/bsp/rockchip/dm/dvfs/event/event-rockchip-dfi.c new file mode 100755 index 0000000000..60f90395ca --- /dev/null +++ b/bsp/rockchip/dm/dvfs/event/event-rockchip-dfi.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "dvfs.dfi" +#define DBG_LVL DBG_INFO +#include + +#define PX30_PMUGRF_OS_REG2 0x208 +#define PX30_PMUGRF_OS_REG3 0x20c + +#define MAX_DMC_NUM_CH 4 +#define READ_DRAMTYPE_INFO(n) (((n) >> 13) & 0x7) +#define READ_DRAMTYPE_INFO_V3(n, m) ((((n) >> 13) & 0x7) | ((((m) >> 12) & 0x3) << 3)) +#define READ_SYSREG_VERSION(m) (((m) >> 28) & 0xf) + +#define DDRMON_CTRL 0x04 +#define CLR_DDRMON_CTRL ((rt_uint32_t)0xffff0000 << 0) +#define DDR2_3_EN (0x10001 << 15) +#define LPDDR5_EN (0x10001 << 6) +#define DDR4_EN (0x10001 << 5) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN (0x10001 << 3) +#define LPDDR2_3_EN (0x10001 << 2) +#define SOFTWARE_EN (0x10001 << 1) +#define SOFTWARE_DIS (0x10000 << 1) + +enum rockchip_dram_type +{ + ROCKCHIP_DFI_DDR4 = 0, + ROCKCHIP_DFI_DDR2 = 2, + ROCKCHIP_DFI_DDR3 = 3, + ROCKCHIP_DFI_LPDDR2 = 5, + ROCKCHIP_DFI_LPDDR3 = 6, + ROCKCHIP_DFI_LPDDR4 = 7, + ROCKCHIP_DFI_LPDDR4X = 8, + ROCKCHIP_DFI_LPDDR5 = 9, +}; + +struct rockchip_dfi_usage +{ + rt_uint64_t access; + rt_uint64_t total; +}; + +struct rockchip_dfi +{ + struct rt_dvfs_event event; + struct rt_platform_device *pdev; + + struct rockchip_dfi_usage ch_usage[MAX_DMC_NUM_CH]; + + volatile rt_uint32_t *regs; + struct rt_syscon *pmugrf; + + rt_uint32_t dram_type; + rt_uint32_t mon_version; + rt_uint32_t mon_idx; + rt_uint32_t mon_ctrl0; + rt_uint32_t mon_access_num; + rt_uint32_t mon_count_num; + rt_uint32_t count_rate; + rt_uint32_t ch_msk; +}; + +static void rockchip_dfi_get_mon_version(struct rockchip_dfi *dfi) +{ + dfi->mon_version = dfi->regs[0]; + + if (dfi->mon_version < 0x40) + { + dfi->mon_ctrl0 = 0x4; + dfi->mon_access_num = 0x2c; + dfi->mon_count_num = 0x28; + } + else + { + dfi->mon_ctrl0 = 0x4; + dfi->mon_access_num = 0x34; + dfi->mon_count_num = 0x30; + } +} + +static void rockchip_dfi_start_counter(struct rockchip_dfi *dfi) +{ + rt_uint32_t mon_idx = dfi->mon_idx, ctrl0 = dfi->mon_ctrl0; + + for (rt_uint32_t i = 0; i < MAX_DMC_NUM_CH; ++i) + { + rt_uint32_t base; + + if (!(dfi->ch_msk & RT_BIT(i))) + { + continue; + } + + if (i > 0 && mon_idx == 0) + { + continue; + } + + base = i * mon_idx; + dfi->regs[(base + ctrl0) / 4] = CLR_DDRMON_CTRL; + + switch (dfi->dram_type) + { + case ROCKCHIP_DFI_LPDDR3: + case ROCKCHIP_DFI_LPDDR2: + dfi->regs[(base + ctrl0) / 4] = LPDDR2_3_EN; + break; + case ROCKCHIP_DFI_LPDDR4: + case ROCKCHIP_DFI_LPDDR4X: + dfi->regs[(base + ctrl0) / 4] = LPDDR4_EN; + break; + case ROCKCHIP_DFI_DDR2: + case ROCKCHIP_DFI_DDR3: + dfi->regs[(base + ctrl0) / 4] = DDR2_3_EN; + break; + case ROCKCHIP_DFI_DDR4: + dfi->regs[(base + ctrl0) / 4] = DDR4_EN; + break; + default: + break; + } + + dfi->regs[(base + ctrl0) / 4] = SOFTWARE_EN; + } +} + +static void rockchip_dfi_stop_counter(struct rockchip_dfi *dfi) +{ + rt_uint32_t mon_idx = dfi->mon_idx; + + for (rt_uint32_t i = 0; i < MAX_DMC_NUM_CH; ++i) + { + rt_uint32_t base; + + if (!(dfi->ch_msk & RT_BIT(i))) + { + continue; + } + + if (i > 0 && mon_idx == 0) + { + continue; + } + + base = i * mon_idx; + dfi->regs[(base + DDRMON_CTRL) / 4] = SOFTWARE_DIS; + } +} + +static int rockchip_dfi_get_busier_ch(struct rockchip_dfi *dfi) +{ + rt_uint32_t busier_ch = 0, max = 0; + rt_uint32_t mon_idx = dfi->mon_idx ? dfi->mon_idx : 0x14; + rt_uint32_t count_rate = dfi->count_rate ? dfi->count_rate : 1; + + rockchip_dfi_stop_counter(dfi); + + for (rt_uint32_t i = 0; i < MAX_DMC_NUM_CH; ++i) + { + rt_uint32_t tmp, base; + + if (!(dfi->ch_msk & RT_BIT(i))) + { + continue; + } + + base = i * mon_idx; + dfi->ch_usage[i].total = dfi->regs[(base + dfi->mon_count_num) / 4] * count_rate; + + tmp = dfi->regs[(base + dfi->mon_access_num) / 4]; + if (dfi->dram_type == ROCKCHIP_DFI_LPDDR4 || dfi->dram_type == ROCKCHIP_DFI_LPDDR4X) + { + tmp *= 8; + } + else + { + tmp *= 4; + } + dfi->ch_usage[i].access = tmp; + + if (tmp > max) + { + max = tmp; + busier_ch = i; + } + } + + rockchip_dfi_start_counter(dfi); + + return (int)busier_ch; +} + +static rt_err_t rockchip_dfi_event_ready(struct rt_dvfs_event *ev) +{ + RT_UNUSED(ev); + return RT_EOK; +} + +static rt_err_t rockchip_dfi_event_enable(struct rt_dvfs_event *ev) +{ + struct rockchip_dfi *dfi = ev->priv; + + rockchip_dfi_get_mon_version(dfi); + rockchip_dfi_start_counter(dfi); + + return RT_EOK; +} + +static rt_err_t rockchip_dfi_event_disable(struct rt_dvfs_event *ev) +{ + struct rockchip_dfi *dfi = ev->priv; + + rockchip_dfi_stop_counter(dfi); + + return RT_EOK; +} + +static rt_err_t rockchip_dfi_event_read(struct rt_dvfs_event *ev, + struct rt_dvfs_event_data *evd) +{ + int busier_ch; + rt_base_t level; + struct rockchip_dfi *dfi = ev->priv; + + level = rt_hw_interrupt_disable(); + busier_ch = rockchip_dfi_get_busier_ch(dfi); + rt_hw_interrupt_enable(level); + + evd->load_count = dfi->ch_usage[busier_ch].access; + evd->total_count = dfi->ch_usage[busier_ch].total; + + return RT_EOK; +} + +static const struct rt_dvfs_event_ops rockchip_dfi_event_ops = +{ + .ready = rockchip_dfi_event_ready, + .read = rockchip_dfi_event_read, + .enable = rockchip_dfi_event_enable, + .disable = rockchip_dfi_event_disable, +}; + +static rt_err_t rockchip_px30_dfi_init(struct rockchip_dfi *dfi, + struct rt_ofw_node *np) +{ + rt_uint32_t val_2 = 0, val_3 = 0; + + dfi->pmugrf = rt_syscon_find_by_ofw_phandle(np, "rockchip,pmugrf"); + if (!dfi->pmugrf) + { + return -RT_ENOENT; + } + + rt_syscon_read(dfi->pmugrf, PX30_PMUGRF_OS_REG2, &val_2); + rt_syscon_read(dfi->pmugrf, PX30_PMUGRF_OS_REG3, &val_3); + + if (READ_SYSREG_VERSION(val_3) >= 0x3) + { + dfi->dram_type = READ_DRAMTYPE_INFO_V3(val_2, val_3); + } + else + { + dfi->dram_type = READ_DRAMTYPE_INFO(val_2); + } + + dfi->ch_msk = 1; + dfi->count_rate = 1; + + return RT_EOK; +} + +static rt_err_t rockchip_dfi_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rockchip_dfi *dfi; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *np = dev->ofw_node; + const struct rt_ofw_node_id *match; + + if (!(dfi = rt_calloc(1, sizeof(*dfi)))) + { + return -RT_ENOMEM; + } + + dfi->pdev = pdev; + dfi->event.dev = dev; + dfi->event.ops = &rockchip_dfi_event_ops; + dfi->event.priv = dfi; + + if (!(dfi->regs = rt_dm_dev_iomap(dev, 0))) + { + err = -RT_EIO; + goto _fail; + } + + match = pdev->id; + if (match && match->data) + { + err = ((rt_err_t (*)(struct rockchip_dfi *, struct rt_ofw_node *))match->data)(dfi, np); + if (err) + { + goto _fail; + } + } + else + { + err = -RT_ENOSYS; + goto _fail; + } + + rt_dm_dev_set_name_auto(dev, "dfi"); + + if ((err = rt_device_register(dev, rt_dm_dev_get_name(dev), RT_DEVICE_FLAG_DEACTIVATE))) + { + goto _fail; + } + + if ((err = rt_dvfs_event_register(&dfi->event))) + { + goto _fail_dev; + } + + pdev->priv = dfi; + + LOG_I("%s: dram_type=%u ch_msk=0x%x", rt_dm_dev_get_name(dev), + dfi->dram_type, dfi->ch_msk); + + return RT_EOK; + +_fail_dev: + rt_device_unregister(dev); +_fail: + if (dfi) + { + rt_free(dfi); + } + return err; +} + +static rt_err_t rockchip_dfi_remove(struct rt_platform_device *pdev) +{ + struct rockchip_dfi *dfi = pdev->priv; + + if (!dfi) + { + return RT_EOK; + } + + rt_dvfs_event_unregister(&dfi->event); + rt_bus_remove_device(&dfi->pdev->parent); + rt_free(dfi); + pdev->priv = RT_NULL; + + return RT_EOK; +} + +static const struct rt_ofw_node_id rockchip_dfi_ofw_ids[] = +{ + { .compatible = "rockchip,rk3568-dfi", .data = rockchip_px30_dfi_init }, + { .compatible = "rockchip,rk3562-dfi", .data = rockchip_px30_dfi_init }, + { .compatible = "rockchip,px30-dfi", .data = rockchip_px30_dfi_init }, + { /* sentinel */ } +}; + +static struct rt_platform_driver rockchip_dfi_driver = +{ + .name = "rockchip-dfi", + .ids = rockchip_dfi_ofw_ids, + .probe = rockchip_dfi_probe, + .remove = rockchip_dfi_remove, +}; + +static int rockchip_dfi_driver_register(void) +{ + rt_platform_driver_register(&rockchip_dfi_driver); + + return 0; +} +INIT_PLATFORM_EXPORT(rockchip_dfi_driver_register); + +#ifdef RT_USING_FINSH +#include + +static struct rt_dvfs_event *dfi_event_get(const char *name) +{ + struct rt_device *dev = rt_device_find(name); + + if (!dev || !dev->ofw_node) + { + return RT_NULL; + } + + return rt_ofw_data(dev->ofw_node); +} + +static int dfi_read(int argc, char **argv) +{ + struct rt_dvfs_event *ev; + struct rt_dvfs_event_data evd; + rt_err_t err; + + if (argc != 2) + { + rt_kprintf("Usage: dfi_read \n"); + return -1; + } + + if (!(ev = dfi_event_get(argv[1]))) + { + rt_kprintf("dfi device not found\n"); + return -1; + } + + if ((err = rt_dvfs_event_enable(ev))) + { + rt_kprintf("enable failed: %s\n", rt_strerror(err)); + return -1; + } + + err = rt_dvfs_event_read(ev, &evd); + if (err) + { + rt_kprintf("read failed: %s\n", rt_strerror(err)); + rt_dvfs_event_disable(ev); + return -1; + } + + if (evd.total_count) + { + rt_uint64_t load_p = evd.load_count * 100000000ULL / evd.total_count; + rt_uint32_t load_int = (rt_uint32_t)(load_p / 1000000ULL); + rt_uint32_t load_frac = (rt_uint32_t)(load_p % 1000000ULL); + + rt_kprintf("load_count=%lu total_count=%lu load=%u.%06u%%\n", + (unsigned long)evd.load_count, + (unsigned long)evd.total_count, + load_int, load_frac); + } + else + { + rt_kprintf("load_count=%lu total_count=%lu load=0%%\n", + (unsigned long)evd.load_count, + (unsigned long)evd.total_count); + } + + return 0; +} +MSH_CMD_EXPORT(dfi_read, read rockchip DFI devfreq-event counters); +#endif /* RT_USING_FINSH */ diff --git a/bsp/rockchip/dm/i2c/i2c-rk3x.c b/bsp/rockchip/dm/i2c/i2c-rk3x.c index 60918a1e44..d73873acd9 100755 --- a/bsp/rockchip/dm/i2c/i2c-rk3x.c +++ b/bsp/rockchip/dm/i2c/i2c-rk3x.c @@ -28,6 +28,8 @@ #define REG_IEN 0x18 /* interrupt enable */ #define REG_IPD 0x1c /* interrupt pending */ #define REG_FCNT 0x20 /* finished count */ +#define REG_SCL_OE_DB 0x24 /* slave hold scl debounce */ +#define REG_CON1 0x228 /* control register1 */ /* Data buffer offsets */ #define TXBUFFER_BASE 0x100 @@ -57,6 +59,20 @@ enum #define REG_CON_STA_CFG(cfg) ((cfg) << 12) #define REG_CON_STO_CFG(cfg) ((cfg) << 14) +#define REG_CON_VERSION RT_GENMASK_ULL(24, 16) +#define REG_CON_VERSION_SHIFT 16 + +#define REG_CON1_AUTO_STOP RT_BIT(0) +#define REG_CON1_TRANSFER_AUTO_STOP RT_BIT(1) +#define REG_CON1_NACK_AUTO_STOP RT_BIT(2) + +enum +{ + RK_I2C_VERSION0 = 0, + RK_I2C_VERSION1, + RK_I2C_VERSION5 = 5, +}; + /* REG_MRXADDR bits */ #define REG_MRXADDR_VALID(x) RT_BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */ @@ -68,10 +84,13 @@ enum #define REG_INT_START RT_BIT(4) /* START condition generated */ #define REG_INT_STOP RT_BIT(5) /* STOP condition generated */ #define REG_INT_NAKRCV RT_BIT(6) /* NACK received */ -#define REG_INT_ALL 0x7f +#define REG_INT_SLV_HDSCL RT_BIT(7) /* slave hold scl */ +#define REG_INT_ALL 0x3ff + +#define IEN_ALL_DISABLE 0 /* Constants */ -#define WAIT_TIMEOUT 1000 /* ms */ +#define WAIT_TIMEOUT 200 /* ms */ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ /* I2C specification values for various modes */ @@ -138,7 +157,6 @@ struct rk3x_i2c_calced_timings enum rk3x_i2c_state { STATE_IDLE, - STATE_START, STATE_READ, STATE_WRITE, STATE_STOP @@ -177,10 +195,15 @@ struct rk3x_i2c enum rk3x_i2c_state state; rt_uint32_t processed; rt_err_t error; + + rt_bool_t autostop_supported; }; #define raw_to_rk3x_i2c(raw) rt_container_of(raw, struct rk3x_i2c, parent) +static rt_uint32_t rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c, rt_bool_t sendend); +static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c); + rt_inline void i2c_writel(struct rk3x_i2c *i2c, rt_uint32_t value, int offset) { HWREG32(i2c->regs + offset) = value; @@ -197,23 +220,140 @@ rt_inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c) i2c_writel(i2c, REG_INT_ALL, REG_IPD); } -/* Generate a START condition, which triggers a REG_INT_START interrupt */ -static void rk3x_i2c_start(struct rk3x_i2c *i2c) +/* Force controller back to idle (disable adapter, clear IRQs) */ +static void rk3x_i2c_hw_idle(struct rk3x_i2c *i2c) +{ + rt_uint32_t val; + + i2c_writel(i2c, 0, REG_IEN); + val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + i2c_writel(i2c, val, REG_CON); + rk3x_i2c_clean_ipd(i2c); + i2c->state = STATE_IDLE; +} + +static void rk3x_i2c_disable_irq(struct rk3x_i2c *i2c) +{ + i2c_writel(i2c, IEN_ALL_DISABLE, REG_IEN); +} + +static void rk3x_i2c_disable(struct rk3x_i2c *i2c) { rt_uint32_t val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; - i2c_writel(i2c, REG_INT_START, REG_IEN); + i2c_writel(i2c, val, REG_CON); +} + +static rt_bool_t rk3x_i2c_auto_stop(struct rk3x_i2c *i2c) +{ + rt_uint32_t len, con1 = 0; + + if (!i2c->autostop_supported) + { + return RT_FALSE; + } + + if (!(i2c->msg->flags & RT_I2C_IGNORE_NACK)) + { + con1 = REG_CON1_NACK_AUTO_STOP | REG_CON1_AUTO_STOP; + } + + if (!i2c->is_last_msg) + { + goto out; + } + + len = i2c->msg->len - i2c->processed; + + if (len > 32) + { + goto out; + } + + if (i2c->msg->len == 32 && i2c->mode == REG_CON_MOD_TX && !i2c->processed) + { + goto out; + } + + i2c->state = STATE_STOP; + + con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP; + i2c_writel(i2c, con1, REG_CON1); + if (con1 & REG_CON1_NACK_AUTO_STOP) + { + i2c_writel(i2c, REG_INT_STOP, REG_IEN); + } + else + { + i2c_writel(i2c, REG_INT_STOP | REG_INT_NAKRCV, REG_IEN); + } + + return RT_TRUE; + +out: + i2c_writel(i2c, con1, REG_CON1); + + return RT_FALSE; +} + +static unsigned int rk3x_i2c_get_version(struct rk3x_i2c *i2c) +{ + rt_uint32_t version; + + rt_clk_enable(i2c->pclk); + version = (i2c_readl(i2c, REG_CON) & REG_CON_VERSION) >> REG_CON_VERSION_SHIFT; + rt_clk_disable(i2c->pclk); + + return version; +} + +static rt_bool_t rk3x_i2c_use_autostop(struct rk3x_i2c *i2c) +{ + return rk3x_i2c_get_version(i2c) >= RK_I2C_VERSION5; +} + +/* Generate a START condition and kick off the transfer (RK Linux FIFO path) */ +static void rk3x_i2c_start(struct rk3x_i2c *i2c) +{ + rt_uint32_t val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + rt_bool_t auto_stop = rk3x_i2c_auto_stop(i2c); + rt_uint32_t length = 0; + + if (i2c->mode == REG_CON_MOD_TX) + { + if (!auto_stop) + { + i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN); + i2c->state = STATE_WRITE; + } + length = rk3x_i2c_fill_transmit_buf(i2c, RT_FALSE); + } + else + { + if (!auto_stop) + { + i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN); + i2c->state = STATE_READ; + } + } - /* enable adapter with correct mode, send START condition */ val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START; - /* if we want to react to NACK, set ACTACK bit */ if (!(i2c->msg->flags & RT_I2C_IGNORE_NACK)) { val |= REG_CON_ACTACK; } i2c_writel(i2c, val, REG_CON); + + if (i2c->mode == REG_CON_MOD_TX) + { + i2c_writel(i2c, length, REG_MTXCNT); + } + else + { + rk3x_i2c_prepare_read(i2c); + } } /* Generate a STOP condition, which triggers a REG_INT_STOP interrupt */ @@ -222,7 +362,6 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, rt_err_t error) rt_uint32_t ctrl; i2c->processed = 0; - i2c->msg = RT_NULL; i2c->error = error; if (i2c->is_last_msg) @@ -234,12 +373,14 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, rt_err_t error) ctrl = i2c_readl(i2c, REG_CON); ctrl |= REG_CON_STOP; + ctrl &= ~REG_CON_START; i2c_writel(i2c, ctrl, REG_CON); } else { /* Signal rk3x_i2c_xfer to start the next message. */ i2c->state = STATE_IDLE; + i2c->msg = RT_NULL; /* * The HW is actually not capable of REPEATED START. But we can @@ -249,7 +390,6 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, rt_err_t error) ctrl = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; i2c_writel(i2c, ctrl, REG_CON); - /* signal that we are finished with the current msg */ rt_completion_done(&i2c->done); } } @@ -280,6 +420,10 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c) { con &= ~REG_CON_MOD_MASK; con |= REG_CON_MOD(REG_CON_MOD_RX); + if (con & REG_CON_START) + { + con &= ~REG_CON_START; + } } i2c_writel(i2c, con, REG_CON); @@ -287,7 +431,7 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c) } /* Fill the transmit buffer with data from i2c->msg */ -static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c) +static rt_uint32_t rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c, rt_bool_t sendend) { rt_uint32_t cnt = 0; @@ -325,43 +469,39 @@ static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c) } } - i2c_writel(i2c, cnt, REG_MTXCNT); + if (sendend) + { + i2c_writel(i2c, cnt, REG_MTXCNT); + } + + return cnt; +} + +static void rk3x_i2c_read_chunk(struct rk3x_i2c *i2c) +{ + rt_uint32_t val = 0; + rt_uint32_t len = i2c->msg->len - i2c->processed; + + if (len > 32) + { + len = 32; + } + + for (rt_uint32_t i = 0; i < len; ++i) + { + rt_uint8_t byte; + + if (i % 4 == 0) + { + val = i2c_readl(i2c, RXBUFFER_BASE + (i / 4) * 4); + } + + byte = (val >> ((i % 4) * 8)) & 0xff; + i2c->msg->buf[i2c->processed++] = byte; + } } /* IRQ handlers for individual states */ -static void rk3x_i2c_handle_start(struct rk3x_i2c *i2c, rt_uint32_t ipd) -{ - if (!(ipd & REG_INT_START)) - { - rk3x_i2c_stop(i2c, -RT_EIO); - LOG_W("Unexpected irq in START: 0x%x", ipd); - rk3x_i2c_clean_ipd(i2c); - - return; - } - - /* ack interrupt */ - i2c_writel(i2c, REG_INT_START, REG_IPD); - - /* disable start bit */ - i2c_writel(i2c, i2c_readl(i2c, REG_CON) & ~REG_CON_START, REG_CON); - - /* enable appropriate interrupts and transition */ - if (i2c->mode == REG_CON_MOD_TX) - { - i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN); - i2c->state = STATE_WRITE; - rk3x_i2c_fill_transmit_buf(i2c); - } - else - { - /* in any other case, we are going to be reading. */ - i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN); - i2c->state = STATE_READ; - rk3x_i2c_prepare_read(i2c); - } -} - static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, rt_uint32_t ipd) { if (!(ipd & REG_INT_MBTF)) @@ -376,21 +516,20 @@ static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, rt_uint32_t ipd) /* ack interrupt */ i2c_writel(i2c, REG_INT_MBTF, REG_IPD); - /* are we finished? */ + rk3x_i2c_auto_stop(i2c); + if (i2c->processed == i2c->msg->len) { rk3x_i2c_stop(i2c, i2c->error); } else { - rk3x_i2c_fill_transmit_buf(i2c); + rk3x_i2c_fill_transmit_buf(i2c, RT_TRUE); } } -static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd) +static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, rt_uint32_t ipd) { - rt_uint32_t val = 0, len = i2c->msg->len - i2c->processed; - /* we only care for MBRF here. */ if (!(ipd & REG_INT_MBRF)) { @@ -400,27 +539,10 @@ static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd) /* ack interrupt (read also produces a spurious START flag, clear it too) */ i2c_writel(i2c, REG_INT_MBRF | REG_INT_START, REG_IPD); - /* Can only handle a maximum of 32 bytes at a time */ - if (len > 32) - { - len = 32; - } + rk3x_i2c_read_chunk(i2c); - /* read the data from receive buffer */ - for (int i = 0; i < len; ++i) - { - rt_uint8_t byte; + rk3x_i2c_auto_stop(i2c); - if (i % 4 == 0) - { - val = i2c_readl(i2c, RXBUFFER_BASE + (i / 4) * 4); - } - - byte = (val >> ((i % 4) * 8)) & 0xff; - i2c->msg->buf[i2c->processed++] = byte; - } - - /* are we finished? */ if (i2c->processed == i2c->msg->len) { rk3x_i2c_stop(i2c, i2c->error); @@ -437,24 +559,53 @@ static void rk3x_i2c_handle_stop(struct rk3x_i2c *i2c, rt_uint32_t ipd) if (!(ipd & REG_INT_STOP)) { - rk3x_i2c_stop(i2c, -RT_EIO); - LOG_E("Unexpected irq in STOP: 0x%x", ipd); + LOG_W("Unexpected irq in STOP: 0x%x", ipd); rk3x_i2c_clean_ipd(i2c); + if (i2c->is_last_msg) + { + i2c->error = -RT_EIO; + rk3x_i2c_hw_idle(i2c); + i2c->msg = RT_NULL; + i2c->state = STATE_IDLE; + rt_completion_done(&i2c->done); + } + else + { + rk3x_i2c_stop(i2c, -RT_EIO); + } + return; } /* ack interrupt */ i2c_writel(i2c, REG_INT_STOP, REG_IPD); + if (i2c->autostop_supported && !i2c->error) + { + if (i2c->mode != REG_CON_MOD_TX && i2c->msg) + { + if ((i2c->msg->len - i2c->processed) > 0) + { + rk3x_i2c_read_chunk(i2c); + } + } + + i2c->processed = 0; + } + /* disable STOP bit */ con = i2c_readl(i2c, REG_CON); con &= ~REG_CON_STOP; + if (i2c->autostop_supported) + { + con &= ~REG_CON_START; + } i2c_writel(i2c, con, REG_CON); i2c->state = STATE_IDLE; + i2c->msg = RT_NULL; - /* signal rk3x_i2c_xfer that we are finished */ rt_completion_done(&i2c->done); } @@ -462,7 +613,8 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, rt_ubase_t clk_rate) { rt_err_t err; rt_ubase_t level; - rt_uint32_t val; + rt_uint32_t val, cnt; + rt_ubase_t period, time_hold = (WAIT_TIMEOUT / 2) * 1000000; struct rk3x_i2c_calced_timings calc; struct i2c_timings *timings = &i2c->timings; @@ -481,6 +633,10 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, rt_ubase_t clk_rate) i2c_writel(i2c, val, REG_CON); i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), REG_CLKDIV); + period = RT_DIV_ROUND_UP(1000000000, clk_rate); + cnt = RT_DIV_ROUND_UP(time_hold, period); + i2c_writel(i2c, cnt, REG_SCL_OE_DB); + rt_spin_unlock_irqrestore(&i2c->lock, level); rt_clk_disable(i2c->pclk); @@ -939,12 +1095,17 @@ static rt_ssize_t rk3x_i2c_setup(struct rk3x_i2c *i2c, struct rt_i2c_msg *msgs, } i2c->addr = msgs[0].addr; - i2c->state = STATE_START; + i2c->state = STATE_IDLE; i2c->processed = 0; i2c->error = RT_EOK; rk3x_i2c_clean_ipd(i2c); + if (i2c->autostop_supported) + { + i2c_writel(i2c, 0, REG_CON1); + } + return res; } @@ -954,13 +1115,35 @@ static rt_ssize_t rk3x_i2c_master_xfer(struct rt_i2c_bus_device *bus, rt_ssize_t res = 0; rt_uint32_t val; rt_ubase_t level; + rt_err_t err; rt_err_t timeout_err; struct rk3x_i2c *i2c = raw_to_rk3x_i2c(bus); level = rt_spin_lock_irqsave(&i2c->lock); - rt_clk_enable(i2c->clk); - rt_clk_enable(i2c->pclk); + err = rt_clk_enable(i2c->clk); + if (err) + { + LOG_E("Can't enable i2c clk: %s", rt_strerror(err)); + rt_spin_unlock_irqrestore(&i2c->lock, level); + + return err; + } + + if (!rt_is_err_or_null(i2c->pclk) && i2c->pclk != i2c->clk) + { + err = rt_clk_enable(i2c->pclk); + if (err) + { + LOG_E("Can't enable i2c pclk: %s", rt_strerror(err)); + rt_clk_disable(i2c->clk); + rt_spin_unlock_irqrestore(&i2c->lock, level); + + return err; + } + } + + rk3x_i2c_hw_idle(i2c); i2c->is_last_msg = RT_FALSE; @@ -981,6 +1164,8 @@ static rt_ssize_t rk3x_i2c_master_xfer(struct rt_i2c_bus_device *bus, i2c->is_last_msg = RT_TRUE; } + rt_completion_init(&i2c->done); + rt_spin_unlock_irqrestore(&i2c->lock, level); rk3x_i2c_start(i2c); @@ -999,7 +1184,7 @@ static rt_ssize_t rk3x_i2c_master_xfer(struct rt_i2c_bus_device *bus, val |= REG_CON_EN | REG_CON_STOP; i2c_writel(i2c, val, REG_CON); - i2c->state = STATE_IDLE; + rk3x_i2c_hw_idle(i2c); res = timeout_err; break; @@ -1013,7 +1198,13 @@ static rt_ssize_t rk3x_i2c_master_xfer(struct rt_i2c_bus_device *bus, } } - rt_clk_disable(i2c->pclk); + rk3x_i2c_disable_irq(i2c); + rk3x_i2c_disable(i2c); + + if (!rt_is_err_or_null(i2c->pclk) && i2c->pclk != i2c->clk) + { + rt_clk_disable(i2c->pclk); + } rt_clk_disable(i2c->clk); rt_spin_unlock_irqrestore(&i2c->lock, level); @@ -1046,7 +1237,7 @@ static void rk3x_i2c_isr(int irqno, void *param) LOG_D("IRQ: state %d, ipd: %x", i2c->state, ipd); /* Clean interrupt bits we don't care about */ - ipd &= ~(REG_INT_BRF | REG_INT_BTF); + ipd &= ~(REG_INT_BRF | REG_INT_BTF | REG_INT_START); if (ipd & REG_INT_NAKRCV) { @@ -1061,9 +1252,15 @@ static void rk3x_i2c_isr(int irqno, void *param) if (!(i2c->msg->flags & RT_I2C_IGNORE_NACK)) { - LOG_E("Flags error"); - - rk3x_i2c_stop(i2c, -RT_EIO); + if (i2c->autostop_supported) + { + i2c->error = -RT_EIO; + i2c->state = STATE_STOP; + } + else + { + rk3x_i2c_stop(i2c, -RT_EIO); + } } } @@ -1075,10 +1272,6 @@ static void rk3x_i2c_isr(int irqno, void *param) switch (i2c->state) { - case STATE_START: - rk3x_i2c_handle_start(i2c, ipd); - break; - case STATE_WRITE: rk3x_i2c_handle_write(i2c, ipd); break; @@ -1131,7 +1324,7 @@ static void rk3x_i2c_free(struct rk3x_i2c *i2c) static rt_err_t rk3x_i2c_probe(struct rt_platform_device *pdev) { - int id = -1; + int id = pdev->dev_id; rt_err_t err; const char *dev_name; struct rt_device *dev = &pdev->parent; @@ -1169,8 +1362,6 @@ static rt_err_t rk3x_i2c_probe(struct rt_platform_device *pdev) struct rt_syscon *grf; struct rt_ofw_node *np = dev->ofw_node; - id = pdev->dev_id; - if (id < 0) { err = -RT_EINVAL; @@ -1251,6 +1442,8 @@ static rt_err_t rk3x_i2c_probe(struct rt_platform_device *pdev) rk3x_i2c_adapt_div(i2c, rt_clk_get_rate(i2c->clk)); rt_clk_disable(i2c->clk); + i2c->autostop_supported = rk3x_i2c_use_autostop(i2c); + rt_spin_lock_init(&i2c->lock); rt_completion_init(&i2c->done); @@ -1361,6 +1554,7 @@ static const struct rt_ofw_node_id rk3x_i2c_ofw_ids[] = { .compatible = "rockchip,rk3228-i2c", .data = &rk3228_soc_data }, { .compatible = "rockchip,rk3288-i2c", .data = &rk3288_soc_data }, { .compatible = "rockchip,rk3399-i2c", .data = &rk3399_soc_data }, + { .compatible = "rockchip,rk3568-i2c", .data = &rk3399_soc_data }, { /* sentinel */ } }; diff --git a/bsp/rockchip/dm/include/opp-select.h b/bsp/rockchip/dm/include/opp-select.h new file mode 100644 index 0000000000..808795475d --- /dev/null +++ b/bsp/rockchip/dm/include/opp-select.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#ifndef __OPP_SELECT_H__ +#define __OPP_SELECT_H__ + +#include +#include + +struct opp_info; + +struct volt_rm_table +{ + int volt; + int rm; +}; + +struct opp_data +{ + rt_err_t (*get_soc_info)(struct rt_device *dev, struct rt_ofw_node *np, + int *bin, int *process); + rt_err_t (*set_soc_info)(struct rt_device *dev, struct rt_ofw_node *np, + struct opp_info *info); + rt_err_t (*set_read_margin)(struct rt_device *dev, + struct opp_info *opp_info, rt_uint32_t rm); + rt_err_t (*config_regulators)(struct rt_device *dev, + struct rt_dvfs_opp *old_opp, struct rt_dvfs_opp *new_opp, + struct opp_info *info); +}; + +struct opp_info +{ + struct rt_device *dev; + struct rt_ofw_node *opp_np; + const struct opp_data *data; + + struct rt_clk *clk; + struct rt_regulator *supply; + struct rt_regulator *mem_supply; + + int bin; + int process; + int volt_sel; + int scale; + int leakage; + + rt_bool_t is_runtime_active; + + struct rt_spinlock dvfs_lock; +}; + +#if defined(RT_DVFS_ROCKCHIP_CPUFREQ) + +void get_opp_data(const struct rt_ofw_node_id *matches, struct opp_info *info); + +rt_err_t nvmem_cell_read_u8(struct rt_ofw_node *np, const char *cell_id, + rt_uint8_t *val); + +const char *regulator_name(struct rt_ofw_node *cpu_np); + +rt_err_t opp_table_init(struct rt_device *dev, struct opp_info *info, + const char *reg_name); + +void opp_table_uninit(struct rt_device *dev, struct opp_info *info); + +rt_err_t opp_table_adjust(struct rt_device *dev, struct opp_info *info, + struct rt_dvfs_scaling *scaling); + +rt_err_t opp_parse(struct rt_dvfs_scaling *dvfs, struct rt_dvfs_opp *opp, + struct rt_ofw_node *opp_np, struct opp_info *info); + +rt_err_t cpufreq_set_opp(struct rt_dvfs_scaling *scaling, struct rt_dvfs_opp *opp, + struct opp_info *info); + +rt_err_t cpufreq_sync_hw_state(struct rt_dvfs_scaling *scaling); + +void opp_dvfs_lock(struct opp_info *info); +void opp_dvfs_unlock(struct opp_info *info); + +#else + +static inline void get_opp_data(const struct rt_ofw_node_id *matches, + struct opp_info *info) +{ + RT_UNUSED(matches); + RT_UNUSED(info); +} + +static inline rt_err_t opp_table_init(struct rt_device *dev, + struct opp_info *info, const char *reg_name) +{ + RT_UNUSED(dev); + RT_UNUSED(info); + RT_UNUSED(reg_name); + return -RT_ENOSYS; +} + +#endif /* RT_DVFS_ROCKCHIP_CPUFREQ */ + +#endif /* __OPP_SELECT_H__ */ diff --git a/bsp/rockchip/dm/soc/SConscript b/bsp/rockchip/dm/soc/SConscript index e3fda61b3f..59dfb3e198 100755 --- a/bsp/rockchip/dm/soc/SConscript +++ b/bsp/rockchip/dm/soc/SConscript @@ -17,6 +17,9 @@ if GetDepend(['RT_SOC_ROCKCHIP_HW_DECOMPRESS']): if GetDepend(['RT_SOC_ROCKCHIP_IODOMAIN']): src += ['io-domain.c'] +if GetDepend(['RT_DVFS_ROCKCHIP_CPUFREQ']): + src += ['opp-select.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/bsp/rockchip/dm/soc/opp-select.c b/bsp/rockchip/dm/soc/opp-select.c new file mode 100644 index 0000000000..82fadb91e6 --- /dev/null +++ b/bsp/rockchip/dm/soc/opp-select.c @@ -0,0 +1,833 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-21 GuEe-GUI first version + */ + +#include +#include + +#define DBG_TAG "dvfs.opp" +#define DBG_LVL DBG_INFO +#include + +#include +#ifdef RT_SOC_ROCKCHIP_PVTM +#include +#endif + +static rt_err_t rk3588_get_soc_info(struct rt_device *dev, struct rt_ofw_node *np, + int *bin, int *process) +{ + rt_uint8_t value = 0; + rt_err_t err; + + if (bin) + { + if (!(err = nvmem_cell_read_u8(np, "specification_serial_number", &value))) + { + if (value == 0xb) + { + *bin = 0; + } + else if (value == 0x1) + { + *bin = 1; + } + else if (value == 0x10) + { + *bin = 1; + } + LOG_D("%s: bin=%d", rt_dm_dev_get_name(dev), *bin); + } + } + + RT_UNUSED(process); + return RT_EOK; +} + +static const struct opp_data rk3588_cpu_opp_data = +{ + .get_soc_info = rk3588_get_soc_info, +}; + +static const struct rt_ofw_node_id cpu_opp_ids[] = +{ + { .compatible = "rockchip,rk3588", .data = &rk3588_cpu_opp_data }, + { .compatible = "rockchip,rk3576" }, + { .compatible = "rockchip,rk3568" }, + { .compatible = "rockchip,rk3399" }, + { .compatible = "rockchip,rk3308" }, + { .compatible = "rockchip,rk3328" }, + { .compatible = "rockchip,px30" }, + { /* sentinel */ } +}; + +void get_opp_data(const struct rt_ofw_node_id *matches, struct opp_info *info) +{ + struct rt_ofw_node *soc_np; + const struct rt_ofw_node_id *match; + + if (!info) + { + return; + } + + if (!matches) + { + matches = cpu_opp_ids; + } + + if (!(soc_np = rt_ofw_find_node_by_compatible(RT_NULL, "rockchip,rk3588"))) + { + return; + } + + match = rt_ofw_node_match(soc_np, matches); + if (match && match->data) + { + info->data = match->data; + } +} + +rt_err_t nvmem_cell_read_u8(struct rt_ofw_node *np, const char *cell_id, + rt_uint8_t *val) +{ + struct rt_device dev = { .ofw_node = (struct rt_ofw_node *)np }; + struct rt_nvmem_cell *cell; + rt_ssize_t ret; + + if (!np || !val) + { + return -RT_EINVAL; + } + + cell = rt_nvmem_get_cell_by_name(&dev, cell_id); + if (rt_is_err_or_null(cell)) + { + return -RT_ENOENT; + } + + ret = rt_nvmem_cell_read_u8(cell, val); + rt_nvmem_put_cell(cell); + + return ret < 0 ? (rt_err_t)ret : RT_EOK; +} + +static rt_err_t read_leakage(struct rt_ofw_node *opp_np, int *leakage) +{ + rt_uint8_t value = 0; + rt_err_t err; + + if (!opp_np || !leakage) + { + return -RT_EINVAL; + } + + err = nvmem_cell_read_u8(opp_np, "leakage", &value); + if (!err) + { + *leakage = value; + } + + return err; +} + +static rt_err_t get_soc_info_default(struct rt_device *dev, + struct rt_ofw_node *np, int *bin, int *process) +{ + rt_uint8_t value = 0; + rt_err_t err; + + if (bin) + { + err = nvmem_cell_read_u8(np, "performance", &value); + if (!err) + { + *bin = value; + LOG_D("%s: bin=%d", rt_dm_dev_get_name(dev), *bin); + } + } + + RT_UNUSED(process); + return RT_EOK; +} + +static const struct opp_data default_opp_data = +{ + .get_soc_info = get_soc_info_default, +}; + +void opp_dvfs_lock(struct opp_info *info) +{ + if (info) + { + rt_spin_lock(&info->dvfs_lock); + } +} + +void opp_dvfs_unlock(struct opp_info *info) +{ + if (info) + { + rt_spin_unlock(&info->dvfs_lock); + } +} + +static void opp_pick_voltages(struct rt_ofw_node *opp_np, struct opp_info *info, + rt_ubase_t *cpu_uvolt, rt_ubase_t *mem_uvolt) +{ + int nr, l = 0; + char prop_name[20]; + rt_uint32_t uvolt[6] = {0}; + + if (info->leakage > 0) + { + l = info->leakage; + } + else if (info->volt_sel > 0) + { + l = info->volt_sel; + } + + if (l > 0 && l <= 7) + { + rt_snprintf(prop_name, sizeof(prop_name), "opp-microvolt-L%d", l); + nr = rt_ofw_prop_read_u32_array_index(opp_np, prop_name, 0, + RT_ARRAY_SIZE(uvolt), uvolt); + if (nr >= 2) + { + *cpu_uvolt = uvolt[1] ? uvolt[1] : uvolt[0]; + if (nr >= 6) + { + *mem_uvolt = uvolt[4] ? uvolt[4] : uvolt[3]; + } + return; + } + } + + nr = rt_ofw_prop_read_u32_array_index(opp_np, "opp-microvolt", 0, + RT_ARRAY_SIZE(uvolt), uvolt); + if (nr >= 2) + { + *cpu_uvolt = uvolt[1] ? uvolt[1] : uvolt[0]; + if (nr >= 6) + { + *mem_uvolt = uvolt[4] ? uvolt[4] : uvolt[3]; + } + } + else if (nr >= 1) + { + *cpu_uvolt = uvolt[0]; + } +} + +rt_err_t opp_parse(struct rt_dvfs_scaling *dvfs, struct rt_dvfs_opp *opp, + struct rt_ofw_node *opp_np, struct opp_info *info) +{ + rt_ubase_t cpu_uvolt = opp->uvolt; + rt_ubase_t mem_uvolt = 0; + + if (!opp || !opp_np || !info) + { + return -RT_EINVAL; + } + + opp_pick_voltages(opp_np, info, &cpu_uvolt, &mem_uvolt); + opp->uvolt = cpu_uvolt; + opp->priv = (void *)(rt_ubase_t)mem_uvolt; + + opp->available = RT_TRUE; + + if (rt_ofw_prop_read_bool(opp_np, "opp-suspend")) + { + dvfs->suspend_freq = opp->freq; + } + + return RT_EOK; +} + +static rt_err_t parse_opp_table_props(struct rt_ofw_node *opp_np, + struct opp_info *info) +{ + if (!opp_np || !info) + { + return -RT_EINVAL; + } + + info->opp_np = opp_np; + rt_ofw_node_get(opp_np); + + info->bin = -1; + info->process = -1; + info->volt_sel = -1; + info->scale = 0; + info->leakage = 0; + + read_leakage(opp_np, &info->leakage); + + if (info->data && info->data->get_soc_info) + { + info->data->get_soc_info(info->dev, opp_np, &info->bin, &info->process); + } + else + { + get_soc_info_default(info->dev, opp_np, &info->bin, &info->process); + } + + return RT_EOK; +} + +const char *regulator_name(struct rt_ofw_node *cpu_np) +{ + struct rt_ofw_node *np; + + np = rt_ofw_parse_phandle(cpu_np, "cpu-supply", 0); + if (np) + { + rt_ofw_node_put(np); + return "cpu"; + } + + np = rt_ofw_parse_phandle(cpu_np, "cpu0-supply", 0); + if (np) + { + rt_ofw_node_put(np); + return "cpu0"; + } + + return RT_NULL; +} + +rt_err_t opp_table_init(struct rt_device *dev, struct opp_info *info, + const char *reg_name) +{ + rt_err_t err; + struct rt_ofw_node *cpu_np = dev->ofw_node, *opp_np; + + if (!dev || !info || !cpu_np) + { + return -RT_EINVAL; + } + + rt_memset(info, 0, sizeof(*info)); + rt_spin_lock_init(&info->dvfs_lock); + + info->dev = dev; + info->is_runtime_active = RT_TRUE; + info->data = &default_opp_data; + + if (!(opp_np = rt_ofw_parse_phandle(cpu_np, "operating-points-v2", 0))) + { + LOG_E("%s: no operating-points-v2", rt_dm_dev_get_name(dev)); + return -RT_ENOENT; + } + + get_opp_data(RT_NULL, info); + + if ((err = parse_opp_table_props(opp_np, info))) + { + rt_ofw_node_put(opp_np); + return err; + } + + if (!reg_name) + { + reg_name = regulator_name(cpu_np); + } + if (!reg_name) + { + LOG_E("%s: no cpu supply", rt_dm_dev_get_name(dev)); + err = -RT_ENOENT; + goto _out; + } + + info->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(info->clk)) + { + LOG_E("%s: get clk failed", rt_dm_dev_get_name(dev)); + err = rt_ptr_err(info->clk); + goto _out; + } + + if ((err = rt_clk_prepare_enable(info->clk))) + if (err) + { + LOG_E("%s: enable clk failed: %s", + rt_dm_dev_get_name(dev), rt_strerror(err)); + rt_clk_put(info->clk); + info->clk = RT_NULL; + goto _out; + } + + info->supply = rt_regulator_get(dev, reg_name); + if (rt_is_err(info->supply)) + { + err = rt_ptr_err(info->supply); + LOG_E("%s: get %s supply failed: %s", + rt_dm_dev_get_name(dev), reg_name, rt_strerror(err)); + info->supply = RT_NULL; + goto _out; + } + + if (rt_ofw_prop_read_bool(cpu_np, "mem-supply")) + { + info->mem_supply = rt_regulator_get(dev, "mem"); + if (rt_is_err(info->mem_supply)) + { + info->mem_supply = RT_NULL; + } + } + + rt_ofw_node_put(opp_np); + return RT_EOK; + +_out: + rt_ofw_node_put(opp_np); + if (!rt_is_err(info->clk)) + { + rt_clk_put(info->clk); + } + return err; +} + +void opp_table_uninit(struct rt_device *dev, struct opp_info *info) +{ + RT_UNUSED(dev); + + if (!info) + { + return; + } + + if (!rt_is_err(info->clk)) + { + rt_clk_disable_unprepare(info->clk); + rt_clk_put(info->clk); + info->clk = RT_NULL; + } + + if (info->supply) + { + rt_regulator_put(info->supply); + info->supply = RT_NULL; + } + + if (info->mem_supply) + { + rt_regulator_put(info->mem_supply); + info->mem_supply = RT_NULL; + } + + if (info->opp_np) + { + rt_ofw_node_put(info->opp_np); + info->opp_np = RT_NULL; + } +} + +#ifdef RT_SOC_ROCKCHIP_PVTM +static rt_err_t nvmem_cell_read_u16(struct rt_ofw_node *np, const char *cell_id, + rt_uint16_t *val) +{ + rt_ssize_t ret; + struct rt_device dev = { .ofw_node = (struct rt_ofw_node *)np }; + struct rt_nvmem_cell *cell; + + if (!np || !val) + { + return -RT_EINVAL; + } + + cell = rt_nvmem_get_cell_by_name(&dev, cell_id); + if (rt_is_err_or_null(cell)) + { + return -RT_ENOENT; + } + + ret = rt_nvmem_cell_read_u16(cell, val); + rt_nvmem_put_cell(cell); + + return ret < 0 ? (rt_err_t)ret : RT_EOK; +} + +static rt_err_t get_prop_sel(struct rt_ofw_node *np, const char *prop_name, + rt_int32_t value, int *sel) +{ + rt_uint32_t tuple[3], count; + int picked = -1; + + if (!np || !prop_name || !sel) + { + return -RT_EINVAL; + } + + count = rt_ofw_prop_count_of_size(np, prop_name, sizeof(rt_uint32_t)); + if (count < 0 || (count % 3) != 0) + { + return -RT_EINVAL; + } + + for (rt_uint32_t i = 0; i < count; i += 3) + { + if (rt_ofw_prop_read_u32_array_index(np, prop_name, i, 3, tuple)) + { + continue; + } + + if (value >= (rt_int32_t)tuple[0]) + { + picked = (int)tuple[2]; + } + } + + if (picked < 0) + { + return -RT_ENOENT; + } + + *sel = picked; + return RT_EOK; +} + +static rt_int32_t read_pvtm(struct rt_ofw_node *opp_np) +{ + rt_uint16_t tmp = 0; + rt_uint32_t ch[2] = {0}, sample_time = 1000, pvtm; + + if (!opp_np) + { + return -RT_EINVAL; + } + + if (!nvmem_cell_read_u16(opp_np, "pvtm", &tmp)) + { + return (rt_int32_t)(tmp * 10); + } + + if (rt_ofw_prop_read_u32_array_index(opp_np, "rockchip,pvtm-ch", 0, 2, ch)) + { + return -RT_ENOENT; + } + + rt_ofw_prop_read_u32(opp_np, "rockchip,pvtm-sample-time", &sample_time); + + pvtm = rockchip_get_pvtm_value(ch[0], ch[1], sample_time); + if (pvtm <= 0) + { + return pvtm; + } + + return pvtm; +} + +static void apply_pvtm_sel(struct rt_device *dev, struct rt_ofw_node *opp_np, + struct opp_info *info) +{ + char prop_name[32]; + rt_int32_t pvtm; + int volt_sel = -1, scale_sel = -1; + + if (!dev || !opp_np || !info) + { + return; + } + + if (rt_ofw_prop_read_bool(opp_np, "rockchip,pvtm-pvtpll")) + { + return; + } + + pvtm = read_pvtm(opp_np); + if (pvtm <= 0) + { + return; + } + + if (info->process >= 0) + { + rt_snprintf(prop_name, sizeof(prop_name), + "rockchip,p%d-pvtm-voltage-sel", info->process); + } + else if (info->bin > 0) + { + rt_snprintf(prop_name, sizeof(prop_name), + "rockchip,pvtm-voltage-sel-B%d", info->bin); + } + else + { + rt_strncpy(prop_name, "rockchip,pvtm-voltage-sel", sizeof(prop_name)); + } + + if (!get_prop_sel(opp_np, prop_name, pvtm, &volt_sel)) + { + info->volt_sel = volt_sel; + } + else if (!get_prop_sel(opp_np, "rockchip,pvtm-voltage-sel", pvtm, &volt_sel)) + { + info->volt_sel = volt_sel; + } + + if (info->process >= 0) + { + rt_snprintf(prop_name, sizeof(prop_name), + "rockchip,p%d-pvtm-scaling-sel", info->process); + } + else + { + rt_strncpy(prop_name, "rockchip,pvtm-scaling-sel", sizeof(prop_name)); + } + + if (!get_prop_sel(opp_np, prop_name, pvtm, &scale_sel)) + { + info->scale = scale_sel; + } + + LOG_D("%s: pvtm=%d volt_sel=%d scale=%d", + rt_dm_dev_get_name(dev), pvtm, info->volt_sel, info->scale); +} +#endif /* RT_SOC_ROCKCHIP_PVTM */ + +rt_err_t opp_table_adjust(struct rt_device *dev, struct opp_info *info, + struct rt_dvfs_scaling *scaling) +{ + struct rt_dvfs_opp *opp; + struct rt_ofw_node *opp_np, *opp_child_np; + + if (!dev || !info || !scaling || !scaling->opp_table) + { + return -RT_EINVAL; + } + + opp_np = info->opp_np; + if (!opp_np) + { + return -RT_ENOENT; + } + +#ifdef RT_SOC_ROCKCHIP_PVTM + apply_pvtm_sel(dev, opp_np, info); +#endif + + rt_ofw_foreach_child_node(opp_np, opp_child_np) + { + rt_uint64_t hz = 0; + + if (rt_ofw_prop_read_u64(opp_child_np, "opp-hz", &hz)) + { + continue; + } + + opp = rt_dvfs_scaling_find_opp(scaling, (rt_ubase_t)hz); + if (!opp) + { + continue; + } + + opp_parse(scaling, opp, opp_child_np, info); + } + + if (info->data && info->data->set_soc_info) + { + info->data->set_soc_info(dev, opp_np, info); + } + + LOG_D("%s: OPP adjusted leakage=%d bin=%d %lu-%lu Hz", + rt_dm_dev_get_name(dev), info->leakage, info->bin, + scaling->min_freq, scaling->max_freq); + + return RT_EOK; +} + +static rt_err_t set_regulator_volt(struct rt_regulator *reg, + rt_ubase_t old_uvolt, rt_ubase_t new_uvolt) +{ + if (!reg) + { + return new_uvolt == old_uvolt ? RT_EOK : -RT_EINVAL; + } + + if (new_uvolt == old_uvolt) + { + return RT_EOK; + } + + return rt_regulator_set_voltage(reg, new_uvolt, new_uvolt); +} + +rt_err_t cpufreq_sync_hw_state(struct rt_dvfs_scaling *scaling) +{ + rt_ubase_t rate; + struct rt_dvfs_opp *opp; + + if (!scaling || !scaling->clk || !scaling->opp_table) + { + return -RT_EINVAL; + } + + if (!(rate = rt_clk_get_rate(scaling->clk))) + { + return -RT_ENOENT; + } + + if (!(opp = rt_dvfs_scaling_find_floor_opp(scaling, rate))) + { + opp = rt_dvfs_scaling_find_ceil_opp(scaling, rate); + } + if (!opp || !opp->available) + { + return -RT_ENOENT; + } + + scaling->cur_freq = rate; + scaling->opp_table->current_opp = opp; + + LOG_D("%s: synced hw state %lu Hz @ %lu uV", + rt_dm_dev_get_name(scaling->dev), rate, opp->uvolt); + + return RT_EOK; +} + +rt_err_t cpufreq_set_opp(struct rt_dvfs_scaling *scaling, + struct rt_dvfs_opp *opp, struct opp_info *info) +{ + rt_err_t err = RT_EOK; + struct rt_dvfs_opp *old_opp; + rt_ubase_t old_freq, old_cpu_uvolt, new_freq, new_cpu_uvolt, new_mem_uvolt, old_mem_uvolt = 0; + + if (!scaling || !opp || !info) + { + return -RT_EINVAL; + } + + opp_dvfs_lock(info); + + old_opp = scaling->opp_table ? scaling->opp_table->current_opp : RT_NULL; + old_freq = scaling->cur_freq; + if (old_opp) + { + old_cpu_uvolt = old_opp->uvolt; + if (old_opp->priv) + { + old_mem_uvolt = (rt_ubase_t)old_opp->priv; + } + } + + new_freq = opp->freq; + new_cpu_uvolt = opp->uvolt; + new_mem_uvolt = opp->priv ? (rt_ubase_t)opp->priv : 0; + + opp_dvfs_unlock(info); + + if (!old_opp && scaling->clk) + { + rt_ubase_t hw_rate = rt_clk_get_rate(scaling->clk); + + if (hw_rate) + { + struct rt_dvfs_opp *hw_opp; + + if (!old_freq) + { + old_freq = hw_rate; + } + + hw_opp = rt_dvfs_scaling_find_floor_opp(scaling, hw_rate); + if (!hw_opp) + { + hw_opp = rt_dvfs_scaling_find_ceil_opp(scaling, hw_rate); + } + if (hw_opp) + { + if (!old_opp) + { + old_opp = hw_opp; + old_cpu_uvolt = hw_opp->uvolt; + if (hw_opp->priv) + { + old_mem_uvolt = (rt_ubase_t)hw_opp->priv; + } + } + } + } + } + + if (info->data && info->data->config_regulators) + { + err = info->data->config_regulators(scaling->dev, old_opp, opp, info); + } + else if (new_freq > old_freq) + { + err = set_regulator_volt(scaling->supply, old_cpu_uvolt, new_cpu_uvolt); + if (!err && info->mem_supply) + { + err = set_regulator_volt(info->mem_supply, old_mem_uvolt, new_mem_uvolt); + } + if (!err && scaling->transition_latency) + { + rt_dvfs_ns_sleep(scaling->transition_latency); + } + if (!err) + { + if (scaling->clk) + { + err = rt_clk_set_rate(scaling->clk, new_freq); + } + else + { + err = -RT_ENOSYS; + } + } + } + else if (new_freq < old_freq) + { + if (scaling->clk) + { + err = rt_clk_set_rate(scaling->clk, new_freq); + } + else + { + err = -RT_ENOSYS; + } + if (!err) + { + err = set_regulator_volt(scaling->supply, old_cpu_uvolt, new_cpu_uvolt); + } + if (!err && info->mem_supply) + { + err = set_regulator_volt(info->mem_supply, old_mem_uvolt, new_mem_uvolt); + } + } + else + { + err = set_regulator_volt(scaling->supply, old_cpu_uvolt, new_cpu_uvolt); + if (!err && info->mem_supply) + { + err = set_regulator_volt(info->mem_supply, old_mem_uvolt, new_mem_uvolt); + } + } + + opp_dvfs_lock(info); + + if (!err) + { + scaling->cur_freq = new_freq; + if (scaling->opp_table) + { + scaling->opp_table->current_opp = opp; + } + } + else + { + LOG_W("%s: set OPP %lu Hz/%lu uV failed at %lu Hz/%lu uV: %s", + rt_dm_dev_get_name(scaling->dev), new_freq, new_cpu_uvolt, + old_freq, old_cpu_uvolt, rt_strerror(err)); + } + + opp_dvfs_unlock(info); + return err; +} diff --git a/bsp/rockchip/rk3500/.config b/bsp/rockchip/rk3500/.config index 91f13fc126..cff7bafda2 100644 --- a/bsp/rockchip/rk3500/.config +++ b/bsp/rockchip/rk3500/.config @@ -445,6 +445,7 @@ CONFIG_RT_MBOX_PIC=y CONFIG_RT_MBOX_ROCKCHIP=y CONFIG_RT_USING_HWSPINLOCK=y CONFIG_RT_HWSPINLOCK_ROCKCHIP=y +# CONFIG_RT_USING_RPMSG is not set CONFIG_RT_USING_PHYE=y # CONFIG_RT_PHYE_GENERIC_USB is not set CONFIG_RT_PHYE_ROCKCHIP_NANENG_COMBO=y @@ -464,11 +465,32 @@ CONFIG_RT_BLK_PARTITION_EFI=y # CONFIG_RT_USING_SCSI is not set CONFIG_RT_USING_FIRMWARE=y +# CONFIG_RT_FIRMWARE_QEMU_FW_CFG is not set CONFIG_RT_FIRMWARE_ARM_SCMI=y CONFIG_RT_FIRMWARE_ARM_SCMI_TRANSPORT_MAILBOX=y CONFIG_RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC=y # CONFIG_RT_USING_HWCACHE is not set +CONFIG_RT_USING_DVFS=y +CONFIG_RT_USING_DVFS_EVENT=y +CONFIG_RT_USING_DVFS_OPP_RETRY_MAX=10 + +# +# DVFS Event Drivers +# +CONFIG_RT_DVFS_EVENT_ROCKCHIP_DFI=y + +# +# DVFS CPUfreq Drivers +# +# CONFIG_RT_DVFS_SCMI_CPUFREQ is not set +CONFIG_RT_DVFS_ROCKCHIP_CPUFREQ=y + +# +# DVFS Devfreq Drivers +# +CONFIG_RT_DVFS_ROCKCHIP_DMC=y CONFIG_RT_USING_REGULATOR=y +CONFIG_RT_REGULATOR_FAN53555=y CONFIG_RT_REGULATOR_FIXED=y CONFIG_RT_REGULATOR_GPIO=y CONFIG_RT_REGULATOR_SCMI=y @@ -497,6 +519,7 @@ CONFIG_RT_THERMAL_ROCKCHIP_TSADC=y # # Thermal Cool Drivers # +CONFIG_RT_THERMAL_COOL_DVFS=y CONFIG_RT_THERMAL_COOL_PWM_FAN=y # CONFIG_RT_USING_VIRTIO is not set CONFIG_RT_USING_NVMEM=y @@ -785,6 +808,7 @@ CONFIG_RT_USING_VDSO=y # end of Using USB legacy version # CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_LEGACY_VIRTIO is not set # CONFIG_RT_USING_RUST is not set # end of RT-Thread Components @@ -861,12 +885,31 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_RT_UTEST_TC_USING_DFS_API is not set # end of File System +# +# Tmpfs Testcase +# +# CONFIG_RT_UTEST_TMPFS_CP is not set +# end of Tmpfs Testcase + # # CPP11 # # CONFIG_RT_UTEST_CPP11_THREAD is not set # end of CPP11 +# +# LWP Testcase +# +# CONFIG_RT_UTEST_LWP is not set +# end of LWP Testcase + +# +# Memory Management Subsytem Testcase +# +# CONFIG_RT_UTEST_MM_API is not set +# CONFIG_RT_UTEST_MM_LWP is not set +# end of Memory Management Subsytem Testcase + # # Network # @@ -881,19 +924,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_RT_UTEST_SELF_PASS is not set # end of Utest Framework # end of Kernel Components - -# -# Memory Management Subsytem Testcase -# -# CONFIG_RT_UTEST_MM_API is not set -# CONFIG_RT_UTEST_MM_LWP is not set -# end of Memory Management Subsytem Testcase - -# -# Tmpfs Testcase -# -# CONFIG_RT_UTEST_TMPFS_CP is not set -# end of Tmpfs Testcase # end of RT-Thread Utestcases # @@ -1089,7 +1119,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_U8G2 is not set # end of u8g2: a monochrome graphic library -# CONFIG_PKG_USING_NES_SIMULATOR is not set # CONFIG_PKG_USING_OPENMV is not set # CONFIG_PKG_USING_MUPDF is not set # CONFIG_PKG_USING_STEMWIN is not set @@ -1114,9 +1143,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # # tools packages # -# CONFIG_PKG_USING_VECTOR is not set -# CONFIG_PKG_USING_SORCH is not set -# CONFIG_PKG_USING_DICT is not set # CONFIG_PKG_USING_CMBACKTRACE is not set # CONFIG_PKG_USING_MCOREDUMP is not set # CONFIG_PKG_USING_EASYFLASH is not set @@ -1165,9 +1191,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_RVBACKTRACE is not set # CONFIG_PKG_USING_HPATCHLITE is not set # CONFIG_PKG_USING_THREAD_METRIC is not set -# CONFIG_PKG_USING_UORB is not set -# CONFIG_PKG_USING_RT_TUNNEL is not set -# CONFIG_PKG_USING_VIRTUAL_TERMINAL is not set # end of tools packages # @@ -1262,9 +1285,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_R_RHEALSTONE is not set # CONFIG_PKG_USING_HEARTBEAT is not set # CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set -# CONFIG_PKG_USING_CHERRYECAT is not set -# CONFIG_PKG_USING_EVENT_LOOP is not set -# CONFIG_PKG_USING_THREAD_MANAGER is not set # end of system packages # @@ -1410,7 +1430,9 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # # NUVOTON Drivers # +# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set # CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set # end of NUVOTON Drivers # @@ -1418,24 +1440,7 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # # CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set # CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set -# CONFIG_PKG_USING_GD32_RISCV_SERIES_DRIVER is not set -# CONFIG_PKG_USING_GD32VW55X_WIFI is not set # end of GD32 Drivers - -# -# HPMicro SDK -# -# CONFIG_PKG_USING_HPM_SDK is not set -# end of HPMicro SDK - -# -# FT32 HAL & SDK Drivers -# -# CONFIG_PKG_USING_FT32F0_STD_DRIVER is not set -# CONFIG_PKG_USING_FT32F0_CMSIS_DRIVER is not set -# CONFIG_PKG_USING_FT32F4_STD_DRIVER is not set -# CONFIG_PKG_USING_FT32F4_CMSIS_DRIVER is not set -# end of FT32 HAL & SDK Drivers # end of HAL & SDK Drivers # @@ -1481,11 +1486,9 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_RT3020 is not set # CONFIG_PKG_USING_MLX90632 is not set # CONFIG_PKG_USING_MLX90382 is not set -# CONFIG_PKG_USING_MLX90384 is not set # CONFIG_PKG_USING_MLX90393 is not set # CONFIG_PKG_USING_MLX90392 is not set # CONFIG_PKG_USING_MLX90394 is not set -# CONFIG_PKG_USING_MLX90396 is not set # CONFIG_PKG_USING_MLX90397 is not set # CONFIG_PKG_USING_MS5611 is not set # CONFIG_PKG_USING_MAX31865 is not set @@ -1514,7 +1517,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_P3T1755 is not set # CONFIG_PKG_USING_QMI8658 is not set # CONFIG_PKG_USING_ICM20948 is not set -# CONFIG_PKG_USING_SCD4X is not set # end of sensors drivers # @@ -1532,7 +1534,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_CST812T is not set # end of touch drivers -# CONFIG_PKG_USING_LCD_SPI_DRIVER is not set # CONFIG_PKG_USING_REALTEK_AMEBA is not set # CONFIG_PKG_USING_BUTTON is not set # CONFIG_PKG_USING_PCF8574 is not set @@ -1612,13 +1613,6 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y # CONFIG_PKG_USING_IC74HC165 is not set # CONFIG_PKG_USING_IST8310 is not set # CONFIG_PKG_USING_ST7789_SPI is not set -# CONFIG_PKG_USING_CAN_UDS is not set -# CONFIG_PKG_USING_ISOTP_C is not set -# CONFIG_PKG_USING_IKUNLED is not set -# CONFIG_PKG_USING_INS5T8025 is not set -# CONFIG_PKG_USING_IRUART is not set -# CONFIG_PKG_USING_ST7305 is not set -# CONFIG_PKG_USING_TM1668 is not set # CONFIG_PKG_USING_SPI_TOOLS is not set # end of peripheral libraries and drivers diff --git a/bsp/rockchip/rk3500/rtconfig.h b/bsp/rockchip/rk3500/rtconfig.h index e8e7204a04..bebf4706df 100644 --- a/bsp/rockchip/rk3500/rtconfig.h +++ b/bsp/rockchip/rk3500/rtconfig.h @@ -305,7 +305,23 @@ #define RT_FIRMWARE_ARM_SCMI #define RT_FIRMWARE_ARM_SCMI_TRANSPORT_MAILBOX #define RT_FIRMWARE_ARM_SCMI_TRANSPORT_SMC +#define RT_USING_DVFS +#define RT_USING_DVFS_EVENT +#define RT_USING_DVFS_OPP_RETRY_MAX 10 + +/* DVFS Event Drivers */ + +#define RT_DVFS_EVENT_ROCKCHIP_DFI + +/* DVFS CPUfreq Drivers */ + +#define RT_DVFS_ROCKCHIP_CPUFREQ + +/* DVFS Devfreq Drivers */ + +#define RT_DVFS_ROCKCHIP_DMC #define RT_USING_REGULATOR +#define RT_REGULATOR_FAN53555 #define RT_REGULATOR_FIXED #define RT_REGULATOR_GPIO #define RT_REGULATOR_SCMI @@ -326,6 +342,7 @@ /* Thermal Cool Drivers */ +#define RT_THERMAL_COOL_DVFS #define RT_THERMAL_COOL_PWM_FAN #define RT_USING_NVMEM #define RT_NVMEM_ROCKCHIP_OTP @@ -576,10 +593,22 @@ /* end of File System */ +/* Tmpfs Testcase */ + +/* end of Tmpfs Testcase */ + /* CPP11 */ /* end of CPP11 */ +/* LWP Testcase */ + +/* end of LWP Testcase */ + +/* Memory Management Subsytem Testcase */ + +/* end of Memory Management Subsytem Testcase */ + /* Network */ /* end of Network */ @@ -588,14 +617,6 @@ /* end of Utest Framework */ /* end of Kernel Components */ - -/* Memory Management Subsytem Testcase */ - -/* end of Memory Management Subsytem Testcase */ - -/* Tmpfs Testcase */ - -/* end of Tmpfs Testcase */ /* end of RT-Thread Utestcases */ /* RT-Thread online packages */ @@ -719,14 +740,6 @@ /* GD32 Drivers */ /* end of GD32 Drivers */ - -/* HPMicro SDK */ - -/* end of HPMicro SDK */ - -/* FT32 HAL & SDK Drivers */ - -/* end of FT32 HAL & SDK Drivers */ /* end of HAL & SDK Drivers */ /* sensors drivers */ diff --git a/components/drivers/regulator/Kconfig b/components/drivers/regulator/Kconfig index 17d2f5b17a..8767d59a7d 100644 --- a/components/drivers/regulator/Kconfig +++ b/components/drivers/regulator/Kconfig @@ -4,6 +4,12 @@ menuconfig RT_USING_REGULATOR select RT_USING_ADT_REF default n +config RT_REGULATOR_FAN53555 + bool "Fairchild FAN53555 / TCS4525 Regulator" + depends on RT_USING_REGULATOR + depends on RT_USING_I2C + default n + config RT_REGULATOR_FIXED bool "Fixed regulator support" depends on RT_USING_REGULATOR diff --git a/components/drivers/regulator/SConscript b/components/drivers/regulator/SConscript index 2069f50869..70245b9c49 100755 --- a/components/drivers/regulator/SConscript +++ b/components/drivers/regulator/SConscript @@ -10,6 +10,9 @@ CPPPATH = [cwd + '/../include'] src = ['regulator.c', 'regulator_dm.c', 'regulator_cmd.c'] +if GetDepend(['RT_REGULATOR_FAN53555']): + src += ['regulator-fan53555.c'] + if GetDepend(['RT_REGULATOR_FIXED']): src += ['regulator-fixed.c'] diff --git a/components/drivers/regulator/regulator-fan53555.c b/components/drivers/regulator/regulator-fan53555.c new file mode 100644 index 0000000000..3d5e58eb54 --- /dev/null +++ b/components/drivers/regulator/regulator-fan53555.c @@ -0,0 +1,1091 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-26 GuEe-GUI first version + */ + +#define DBG_TAG "regulator.fan53555" +#define DBG_LVL DBG_INFO +#include + +#include "regulator_dm.h" + +/* Voltage setting */ +#define FAN53555_VSEL0 0x00 +#define FAN53555_VSEL1 0x01 + +#define TCS4525_VSEL0 0x11 +#define TCS4525_VSEL1 0x10 +#define TCS4525_TIME 0x13 +#define TCS4525_COMMAND 0x14 +#define TCS4525_LIMCONF 0x16 + +#define RK8600_VSEL0 FAN53555_VSEL0 +#define RK8600_VSEL1 FAN53555_VSEL1 +#define RK8602_VSEL0 0x06 +#define RK8602_VSEL1 0x07 +#define RK860X_MAX_SET 0x08 + +/* Control register */ +#define FAN53555_CONTROL 0x02 +/* IC Type */ +#define FAN53555_ID1 0x03 +/* IC mask version */ +#define FAN53555_ID2 0x04 +/* Monitor register */ +#define FAN53555_MONITOR 0x05 + +/* VSEL bit definitions */ +#define VSEL_BUCK_EN RT_BIT(7) +#define VSEL_MODE RT_BIT(6) +#define RK8600_VSEL_NSEL_MASK 0x3f +#define RK8602_VSEL_NSEL_MASK 0xff +/* Chip ID */ +#define DIE_ID 0x0f +#define DIE_REV 0x0f +/* Control bit definitions */ +#define CTL_OUTPUT_DISCHG RT_BIT(7) +#define CTL_SLEW_MASK (0x7 << 4) +#define CTL_SLEW_SHIFT 4 +#define CTL_RESET RT_BIT(2) +#define CTL_MODE_VSEL0_MODE RT_BIT(0) +#define CTL_MODE_VSEL1_MODE RT_BIT(1) + +#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */ +#define FAN53526_NVOLTAGES 128 +#define RK8600_NVOLTAGES FAN53555_NVOLTAGES +#define RK8602_NVOLTAGES 160 + +#define TCS_VSEL0_MODE RT_BIT(7) +#define TCS_VSEL1_MODE RT_BIT(6) + +#define TCS_SLEW_MASK (0x3 << 3) +#define TCS_SLEW_SHIFT 3 + +/* VSEL ID */ +enum +{ + FAN53555_VSEL_ID_0 = 0, + FAN53555_VSEL_ID_1, +}; + +enum fan53555_vendor +{ + FAN53526_VENDOR_FAIRCHILD = 0, + FAN53555_VENDOR_FAIRCHILD, + RK8600_VENDOR_ROCKCHIP, /* RK8600, RK8601 */ + RK8602_VENDOR_ROCKCHIP, /* RK8602, RK8603 */ + FAN53555_VENDOR_SILERGY, + FAN53526_VENDOR_TCS, +}; + +enum +{ + FAN53526_CHIP_ID_01 = 1, +}; + +enum +{ + FAN53526_CHIP_REV_08 = 8, +}; + +/* IC Type */ +enum +{ + FAN53555_CHIP_ID_00 = 0, + FAN53555_CHIP_ID_01, + FAN53555_CHIP_ID_02, + FAN53555_CHIP_ID_03, + FAN53555_CHIP_ID_04, + FAN53555_CHIP_ID_05, + FAN53555_CHIP_ID_08 = 8, +}; + +enum +{ + RK8600_CHIP_ID_08 = 8, /* RK8600, RK8601 */ + RK8602_CHIP_ID_10 = 10, /* RK8602, RK8603 */ +}; + +enum +{ + TCS4525_CHIP_ID_12 = 12, +}; + +enum +{ + TCS4526_CHIP_ID_00 = 0, +}; + +/* IC mask revision */ +enum +{ + FAN53555_CHIP_REV_00 = 0x3, + FAN53555_CHIP_REV_13 = 0xf, +}; + +enum +{ + SILERGY_SYR82X = 8, + SILERGY_SYR83X = 9, +}; + +struct fan53555_regulator +{ + struct rt_regulator_node parent; + struct rt_regulator_param param; + + struct rt_i2c_client *client; + + rt_ubase_t vsel_pin; + + /* IC Type and Rev */ + int chip_id; + int chip_rev; + enum fan53555_vendor vendor; + + rt_uint32_t limit_volt; + + /* Voltage setting register */ + rt_uint32_t vol_reg; + rt_uint32_t vol_mask; + rt_uint32_t sleep_reg; + rt_uint32_t en_reg; + rt_uint32_t sleep_en_reg; + /* Mode */ + rt_uint32_t mode_reg; + rt_uint32_t mode_mask; + /* Slew rate */ + rt_uint32_t slew_reg; + rt_uint32_t slew_mask; + rt_uint32_t slew_shift; + /* Voltage range and step(linear) */ + rt_uint32_t vsel_min; + rt_uint32_t vsel_step; + rt_uint32_t vsel_count; + + /* Voltage slew rate limiting */ + rt_uint32_t slew_rate; + rt_uint32_t sleep_vsel_id; + + const int *ramp_delay_table; + int ramp_delay_nr; +}; + +#define raw_to_fan53555_regulator(raw) rt_container_of(raw, struct fan53555_regulator, parent) + +static rt_uint32_t fan53555_vol_mask(rt_uint32_t vsel_count) +{ + rt_uint32_t bit; + + if (!vsel_count) + { + return 0; + } + + bit = 32 - __rt_clz(vsel_count - 1); + + return (1U << bit) - 1; +} + +static const int slew_rates[] = +{ + 64000, 32000, 16000, 8000, 4000, 2000, 1000, 500, +}; + +static const int tcs_slew_rates[] = +{ + 18700, 9300, 4600, 2300, +}; + +static rt_err_t fan53555_regulator_write(struct fan53555_regulator *freg, + rt_uint8_t reg, rt_uint8_t value) +{ + rt_int32_t res; + struct rt_i2c_msg msg[1]; + rt_uint8_t data[sizeof(reg) + sizeof(value)] = { reg }; + struct rt_i2c_client *client = freg->client; + + rt_memcpy(&data[sizeof(reg)], &value, sizeof(value)); + + msg[0].buf = data; + msg[0].addr = client->client_addr; + msg[0].len = sizeof(data); + msg[0].flags = RT_I2C_WR; + + res = rt_i2c_transfer(client->bus, msg, 1); + + return res > 0 ? RT_EOK : res; +} + +static rt_err_t fan53555_regulator_read(struct fan53555_regulator *freg, + rt_uint8_t reg, rt_uint8_t *values) +{ + rt_int32_t res; + struct rt_i2c_msg msg[2]; + struct rt_i2c_client *client = freg->client; + + msg[0].buf = ® + msg[0].addr = client->client_addr; + msg[0].len = sizeof(reg); + msg[0].flags = RT_I2C_WR; + + msg[1].buf = (rt_uint8_t *)values; + msg[1].addr = client->client_addr; + msg[1].len = sizeof(*values); + msg[1].flags = RT_I2C_RD; + + res = rt_i2c_transfer(client->bus, msg, 2); + + return res >= 2 ? RT_EOK : (res < 0 ? (rt_err_t)res : -RT_EIO); +} + +static rt_err_t fan53555_regulator_update_bits(struct fan53555_regulator *freg, + rt_uint8_t reg, rt_uint8_t mask, rt_uint8_t data) +{ + rt_err_t err; + rt_uint8_t old, tmp; + + if ((err = fan53555_regulator_read(freg, reg, &old))) + { + return err; + } + + tmp = old & ~mask; + tmp |= (data & mask); + + return fan53555_regulator_write(freg, reg, tmp); +} + +static rt_err_t fan53555_regulator_enable(struct rt_regulator_node *reg_np) +{ + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_pin >= 0) + { + rt_pin_mode(freg->vsel_pin, PIN_MODE_OUTPUT); + rt_pin_write(freg->vsel_pin, !freg->sleep_vsel_id); + + return RT_EOK; + } + + return fan53555_regulator_update_bits(freg, freg->en_reg, VSEL_BUCK_EN, VSEL_BUCK_EN); +} + +static rt_err_t fan53555_regulator_disable(struct rt_regulator_node *reg_np) +{ + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_pin >= 0) + { + rt_pin_mode(freg->vsel_pin, PIN_MODE_OUTPUT); + rt_pin_write(freg->vsel_pin, freg->sleep_vsel_id); + + return RT_EOK; + } + + return fan53555_regulator_update_bits(freg, freg->en_reg, VSEL_BUCK_EN, 0); +} + +static rt_bool_t fan53555_regulator_is_enabled(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + rt_uint8_t val; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_pin >= 0) + { + rt_uint8_t pin_val; + + rt_pin_mode(freg->vsel_pin, PIN_MODE_INPUT); + pin_val = rt_pin_read(freg->vsel_pin); + + return freg->sleep_vsel_id ? !pin_val : pin_val; + } + + if ((err = fan53555_regulator_read(freg, freg->en_reg, &val))) + { + LOG_E("Read %s register error = %s", "enable", rt_strerror(err)); + + return RT_FALSE; + } + + if (val & VSEL_BUCK_EN) + { + return RT_TRUE; + } + + return RT_FALSE; +} + +static rt_err_t fan53555_regulator_set_voltage(struct rt_regulator_node *reg_np, + int min_uvolt, int max_uvolt) +{ + int uvolt; + rt_uint8_t selector; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if (freg->vsel_count == 1 && freg->vsel_step == 0) + { + if (min_uvolt <= freg->vsel_min && freg->vsel_min <= max_uvolt) + { + return 0; + } + else + { + return -RT_EINVAL; + } + } + + if (!freg->vsel_step) + { + return -RT_EINVAL; + } + + if (min_uvolt < freg->vsel_min) + { + min_uvolt = freg->vsel_min; + } + + selector = RT_DIV_ROUND_UP(min_uvolt - freg->vsel_min, freg->vsel_step); + + if ((rt_int8_t)selector < 0) + { + return selector; + } + + if (selector >= freg->vsel_count) + { + return -RT_EINVAL; + } + + uvolt = freg->vsel_min + (freg->vsel_step * selector); + + if (uvolt < min_uvolt || uvolt > max_uvolt) + { + return -RT_EINVAL; + } + + selector <<= __rt_ffs(freg->vol_mask) - 1; + + return fan53555_regulator_update_bits(freg, freg->vol_reg, freg->vol_mask, selector); +} + +static int fan53555_regulator_get_voltage(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + rt_uint8_t selector; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if ((err = fan53555_regulator_read(freg, freg->vol_reg, &selector))) + { + return err; + } + + selector = (selector & freg->vol_mask) >> (__rt_ffs(freg->vol_mask) - 1); + + if (selector >= freg->vsel_count) + { + return -RT_EINVAL; + } + + return freg->vsel_min + (freg->vsel_step * selector); +} + +static rt_err_t fan53555_regulator_set_mode(struct rt_regulator_node *reg_np, + rt_uint32_t mode) +{ + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + switch (mode) + { + case RT_REGULATOR_MODE_FAST: + fan53555_regulator_update_bits(freg, + freg->mode_reg, freg->mode_mask, freg->mode_mask); + break; + + case RT_REGULATOR_MODE_NORMAL: + fan53555_regulator_update_bits(freg, + freg->mode_reg, freg->mode_mask, 0); + break; + + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_int32_t fan53555_regulator_get_mode(struct rt_regulator_node *reg_np) +{ + rt_err_t err; + rt_uint8_t val; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + if ((err = fan53555_regulator_read(freg, freg->mode_reg, &val))) + { + LOG_E("Read %s register error = %s", "mode", rt_strerror(err)); + + return RT_FALSE; + } + + if (val & freg->mode_mask) + { + return RT_REGULATOR_MODE_FAST; + } + else + { + return RT_REGULATOR_MODE_NORMAL; + } +} + +static rt_err_t fan53555_regulator_set_ramp_delay(struct rt_regulator_node *reg_np, + int ramp) +{ + int regval = -1, ramp_delay_nr; + const int *ramp_delay_table; + struct fan53555_regulator *freg = raw_to_fan53555_regulator(reg_np); + + ramp_delay_nr = freg->ramp_delay_nr; + ramp_delay_table = freg->ramp_delay_table; + + for (int i = 0; i < ramp_delay_nr; ++i) + { + if (ramp <= ramp_delay_table[i]) + { + regval = i; + continue; + } + + break; + } + + if (regval < 0) + { + LOG_E("unsupported ramp value %d", ramp); + + return -RT_EINVAL; + } + + return fan53555_regulator_update_bits(freg, freg->slew_reg, + freg->slew_mask, regval << freg->slew_shift); +} + +static const struct rt_regulator_ops fan53555_regulator_switch_ops = +{ + .enable = fan53555_regulator_enable, + .disable = fan53555_regulator_disable, + .is_enabled = fan53555_regulator_is_enabled, + .set_voltage = fan53555_regulator_set_voltage, + .get_voltage = fan53555_regulator_get_voltage, + .set_mode = fan53555_regulator_set_mode, + .get_mode = fan53555_regulator_get_mode, + .set_ramp_delay = fan53555_regulator_set_ramp_delay, +}; + +#ifdef RT_USING_OFW +static rt_err_t fan53555_regulator_parse_ofw(struct fan53555_regulator *freg, + struct rt_device *dev) +{ + rt_err_t err; + struct rt_ofw_node *np = dev->ofw_node; + + if ((err = regulator_ofw_parse(np, &freg->param))) + { + return err; + } + + rt_ofw_prop_read_u32(np, "limit-microvolt", &freg->limit_volt); + + rt_ofw_prop_read_u32(np, "fcs,suspend-voltage-selector", &freg->sleep_vsel_id); + rt_ofw_prop_read_u32(np, "rockchip,suspend-voltage-selector", &freg->sleep_vsel_id); + + freg->vsel_pin = rt_ofw_get_named_pin(np, "vsel", 0, RT_NULL, RT_NULL); + if (freg->vsel_pin < 0) + { + freg->vsel_pin = PIN_NONE; + } + + return RT_EOK; +} +#else +static rt_err_t fan53555_regulator_parse_ofw(struct fan53555_regulator *freg, + struct rt_device *dev) +{ + return RT_EOK; +} +#endif /* RT_USING_OFW */ + +static rt_err_t fan53555_regulator_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + rt_uint8_t val; + rt_bool_t hw_always_on; + rt_uint32_t ramp_delay; + struct rt_regulator_node *rgp; + struct rt_device *dev = &client->parent; + struct fan53555_regulator *freg = rt_calloc(1, sizeof(*freg)); + + if (!freg) + { + return -RT_ENOMEM; + } + + if ((err = fan53555_regulator_parse_ofw(freg, dev))) + { + goto _fail; + } + + if (dev->ofw_node) + { + freg->vendor = (rt_ubase_t)client->ofw_id->data; + } + else + { + if (!freg->param.ramp_delay) + { + int slew_idx = freg->slew_rate; + + if (slew_idx > RT_ARRAY_SIZE(slew_rates)) + { + LOG_E("Invalid slew rate"); + + err = -RT_EINVAL; + goto _fail; + } + + freg->param.ramp_delay = slew_rates[slew_idx]; + } + + freg->vendor = (rt_ubase_t)rt_i2c_client_id_data(client); + } + freg->client = client; + + /* Get chip ID */ + if ((err = fan53555_regulator_read(freg, FAN53555_ID1, &val))) + { + LOG_E("%s: read ID1 failed: %s", client->name, rt_strerror(err)); + goto _fail; + } + freg->chip_id = val & DIE_ID; + + /* Get chip revision */ + if ((err = fan53555_regulator_read(freg, FAN53555_ID2, &val))) + { + LOG_E("%s: read ID2 failed: %s", client->name, rt_strerror(err)); + goto _fail; + } + freg->chip_rev = val & DIE_REV; + + if (freg->sleep_vsel_id > FAN53555_VSEL_ID_1) + { + LOG_E("Invalid vsel-id = %d", freg->sleep_vsel_id); + goto _fail; + } + + /* + * For 00,01 options: + * VOUT = 0.7125V + NSELx * 12.5mV, from 0.7125 to 1.5V. + * For 02,03 options: + * VOUT = 0.5V + NSELx * 6.25mV, from 0.5 to 1.5V. + */ + + /* Setup voltage control register */ + freg->vol_reg = 0xff; + switch (freg->vendor) + { + case FAN53526_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_SILERGY: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = FAN53555_VSEL0; + freg->vol_reg = FAN53555_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = FAN53555_VSEL1; + freg->vol_reg = FAN53555_VSEL0; + break; + + default: + break; + } + freg->sleep_en_reg = freg->sleep_reg; + freg->en_reg = freg->vol_reg; + break; + + case RK8600_VENDOR_ROCKCHIP: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = RK8600_VSEL0; + freg->vol_reg = RK8600_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = RK8600_VSEL1; + freg->vol_reg = RK8600_VSEL0; + break; + + default: + break; + } + freg->sleep_en_reg = freg->sleep_reg; + freg->en_reg = freg->vol_reg; + freg->vol_reg = RK8600_VSEL_NSEL_MASK; + break; + + case RK8602_VENDOR_ROCKCHIP: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = RK8602_VSEL0; + freg->vol_reg = RK8602_VSEL1; + freg->sleep_en_reg = RK8600_VSEL0; + freg->en_reg = RK8600_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = RK8602_VSEL1; + freg->vol_reg = RK8602_VSEL0; + freg->sleep_en_reg = RK8600_VSEL1; + freg->en_reg = RK8600_VSEL0; + break; + + default: + break; + } + freg->vol_reg = RK8602_VSEL_NSEL_MASK; + break; + + case FAN53526_VENDOR_TCS: + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->sleep_reg = TCS4525_VSEL0; + freg->vol_reg = TCS4525_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->sleep_reg = TCS4525_VSEL1; + freg->vol_reg = TCS4525_VSEL0; + break; + + default: + break; + } + freg->sleep_en_reg = freg->sleep_reg; + freg->en_reg = freg->vol_reg; + break; + + default: + break; + } + + /* Setup mode control register */ + switch (freg->vendor) + { + case FAN53526_VENDOR_FAIRCHILD: + freg->mode_reg = FAN53555_CONTROL; + + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->mode_mask = CTL_MODE_VSEL1_MODE; + break; + + case FAN53555_VSEL_ID_1: + freg->mode_mask = CTL_MODE_VSEL0_MODE; + break; + + default: + break; + } + break; + case FAN53555_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_SILERGY: + case RK8600_VENDOR_ROCKCHIP: + freg->mode_reg = freg->vol_reg; + freg->mode_mask = VSEL_MODE; + break; + + case RK8602_VENDOR_ROCKCHIP: + freg->mode_mask = VSEL_MODE; + + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->mode_reg = RK8600_VSEL1; + break; + + case FAN53555_VSEL_ID_1: + freg->mode_reg = RK8600_VSEL0; + break; + + default: + break; + } + break; + + case FAN53526_VENDOR_TCS: + freg->mode_reg = TCS4525_COMMAND; + + switch (freg->sleep_vsel_id) + { + case FAN53555_VSEL_ID_0: + freg->mode_mask = TCS_VSEL1_MODE; + break; + + case FAN53555_VSEL_ID_1: + freg->mode_mask = TCS_VSEL0_MODE; + break; + + default: + break; + } + break; + + default: + break; + } + + /* Setup voltage range */ + switch (freg->vendor) + { + case FAN53526_VENDOR_FAIRCHILD: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case FAN53526_CHIP_ID_01: + switch (freg->chip_rev) + { + case FAN53526_CHIP_REV_08: + freg->vsel_min = 600000; + freg->vsel_step = 6250; + break; + + default: + LOG_E("Chip ID %d with rev %d not supported", + freg->chip_id, freg->chip_rev); + err = -RT_EINVAL; + goto _fail; + } + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = FAN53526_NVOLTAGES; + break; + + case FAN53555_VENDOR_FAIRCHILD: + /* Init voltage range and step */ + switch (freg->chip_id) { + case FAN53555_CHIP_ID_00: + switch (freg->chip_rev) + { + case FAN53555_CHIP_REV_00: + freg->vsel_min = 600000; + freg->vsel_step = 10000; + break; + + case FAN53555_CHIP_REV_13: + freg->vsel_min = 800000; + freg->vsel_step = 10000; + break; + + default: + LOG_E("Chip ID %d with rev %d not supported", + freg->chip_id, freg->chip_rev); + err = -RT_EINVAL; + goto _fail; + } + break; + case FAN53555_CHIP_ID_01: + case FAN53555_CHIP_ID_03: + case FAN53555_CHIP_ID_05: + case FAN53555_CHIP_ID_08: + freg->vsel_min = 600000; + freg->vsel_step = 10000; + break; + + case FAN53555_CHIP_ID_04: + freg->vsel_min = 603000; + freg->vsel_step = 12826; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = FAN53555_NVOLTAGES; + break; + + case FAN53555_VENDOR_SILERGY: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case SILERGY_SYR82X: + case SILERGY_SYR83X: + freg->vsel_min = 712500; + freg->vsel_step = 12500; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = FAN53555_NVOLTAGES; + break; + + case RK8600_VENDOR_ROCKCHIP: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case RK8600_CHIP_ID_08: + freg->vsel_min = 712500; + freg->vsel_step = 12500; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = RK8600_NVOLTAGES; + break; + + case RK8602_VENDOR_ROCKCHIP: + /* Init voltage range and step */ + switch (freg->chip_id) + { + case RK8602_CHIP_ID_10: + freg->vsel_min = 500000; + freg->vsel_step = 6250; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + + freg->slew_reg = FAN53555_CONTROL; + freg->slew_mask = CTL_SLEW_MASK; + freg->slew_shift = CTL_SLEW_SHIFT; + freg->ramp_delay_table = slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(slew_rates); + freg->vsel_count = RK8602_NVOLTAGES; + break; + + case FAN53526_VENDOR_TCS: + switch (freg->chip_id) + { + case TCS4525_CHIP_ID_12: + case TCS4526_CHIP_ID_00: + freg->slew_reg = TCS4525_TIME; + freg->slew_mask = TCS_SLEW_MASK; + freg->slew_shift = TCS_SLEW_SHIFT; + freg->ramp_delay_table = tcs_slew_rates; + freg->ramp_delay_nr = RT_ARRAY_SIZE(tcs_slew_rates); + + /* Init voltage range and step */ + freg->vsel_min = 600000; + freg->vsel_step = 6250; + freg->vsel_count = FAN53526_NVOLTAGES; + break; + + default: + LOG_E("Chip ID %d not supported", freg->chip_id); + err = -RT_EINVAL; + goto _fail; + } + break; + + default: + break; + } + + if (freg->limit_volt) + { + if (freg->limit_volt < freg->vsel_min || freg->limit_volt > 1500000) + { + freg->limit_volt = 1500000; + } + + val = (freg->limit_volt - freg->vsel_min) / freg->vsel_step; + + if (freg->vendor == FAN53526_VENDOR_TCS) + { + err = fan53555_regulator_write(freg, TCS4525_LIMCONF, val); + } + else + { + err = fan53555_regulator_write(freg, RK860X_MAX_SET, val); + } + + if (err) + { + LOG_E("%s: Failed to set limit voltage", client->name); + goto _fail; + } + } + + freg->vol_mask = fan53555_vol_mask(freg->vsel_count); + + rgp = &freg->parent; + rgp->ops = &fan53555_regulator_switch_ops; + rgp->param = &freg->param; + rgp->dev = &client->parent; + rgp->supply_name = freg->param.name; + + freg->param.enable_delay = 400; + + client->parent.user_data = freg; + + /* + * U-Boot/loader already enables always-on rails (e.g. vdd_cpu). + * Skip boot-time enable/ramp I2C in rt_regulator_register() to avoid + * blocking early boot on a shared PMU I2C bus. + */ + hw_always_on = freg->param.always_on; + ramp_delay = freg->param.ramp_delay; + if (hw_always_on) + { + freg->param.boot_on = RT_FALSE; + freg->param.always_on = RT_FALSE; + freg->param.ramp_delay = 0; + freg->param.ramp_disable = RT_TRUE; + } + + if ((err = rt_regulator_register(rgp))) + { + goto _fail; + } + + if (hw_always_on) + { + rt_atomic_store(&rgp->enabled_count, 1); + } + + if (ramp_delay) + { + err = fan53555_regulator_set_ramp_delay(rgp, ramp_delay); + if (err) + { + LOG_W("%s: set ramp %u us failed: %s", client->name, + ramp_delay, rt_strerror(err)); + } + } + + return RT_EOK; + +_fail: + rt_free(freg); + + return err; +} + +static rt_err_t fan53555_regulator_shutdown(struct rt_i2c_client *client) +{ + rt_err_t err = RT_EOK; + struct fan53555_regulator *freg = client->parent.user_data; + + switch (freg->vendor) + { + case FAN53555_VENDOR_FAIRCHILD: + case FAN53555_VENDOR_SILERGY: + err = fan53555_regulator_update_bits(freg, freg->slew_reg, CTL_RESET, CTL_RESET); + break; + + case FAN53526_VENDOR_TCS: + err = fan53555_regulator_update_bits(freg, TCS4525_LIMCONF, CTL_RESET, CTL_RESET); + break; + + default: + break; + } + + if (err < 0) + { + LOG_E("Shutdown error = %s", rt_strerror(err)); + } + + return RT_EOK; +} + +static const struct rt_i2c_device_id fan53555_regulator_ids[] = +{ + { .name = "fan53526", .data = (void *)FAN53526_VENDOR_FAIRCHILD }, + { .name = "fan53555", .data = (void *)FAN53555_VENDOR_FAIRCHILD }, + { .name = "rk8600", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .name = "rk8601", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .name = "rk8602", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .name = "rk8603", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .name = "syr827", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .name = "syr828", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .name = "tcs4525", .data = (void *)FAN53526_VENDOR_TCS }, + { .name = "tcs4526", .data = (void *)FAN53526_VENDOR_TCS }, + { .name = "tcs452x", .data = (void *)FAN53526_VENDOR_TCS }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id fan53555_regulator_ofw_ids[] = +{ + { .compatible = "fcs,fan53526", .data = (void *)FAN53526_VENDOR_FAIRCHILD }, + { .compatible = "fcs,fan53555", .data = (void *)FAN53555_VENDOR_FAIRCHILD }, + { .compatible = "rockchip,rk8600", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .compatible = "rockchip,rk8601", .data = (void *)RK8600_VENDOR_ROCKCHIP }, + { .compatible = "rockchip,rk8602", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .compatible = "rockchip,rk8603", .data = (void *)RK8602_VENDOR_ROCKCHIP }, + { .compatible = "silergy,syr827", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .compatible = "silergy,syr828", .data = (void *)FAN53555_VENDOR_SILERGY }, + { .compatible = "tcs,tcs4525", .data = (void *)FAN53526_VENDOR_TCS }, + { .compatible = "tcs,tcs4526", .data = (void *)FAN53526_VENDOR_TCS }, + { .compatible = "tcs,tcs452x", .data = (void *)FAN53526_VENDOR_TCS }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver fan53555_regulator_driver = +{ + .ids = fan53555_regulator_ids, + .ofw_ids = fan53555_regulator_ofw_ids, + + .probe = fan53555_regulator_probe, + .shutdown = fan53555_regulator_shutdown, +}; +RT_I2C_DRIVER_EXPORT(fan53555_regulator_driver);