summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2026-06-15 15:10:18 +0800
committerRbb666 <[email protected]>2026-06-23 08:02:09 +0800
commitb2e1f13a05a880d536d3a72a391ff18303c30f89 (patch)
tree48c6d71a5b78e42d85684f5bd5c11faf5c751f1f
parent741d82c42b1ae96fe3634be4469fb2f33def2df7 (diff)
[bsp][rockchip] support DVFS for Rockchip
The I2C will timeout in fan53555, fixed it. Signed-off-by: GuEe-GUI <[email protected]>
-rwxr-xr-xbsp/rockchip/dm/Kconfig3
-rwxr-xr-xbsp/rockchip/dm/dvfs/SConscript21
-rwxr-xr-xbsp/rockchip/dm/dvfs/cpufreq/Kconfig10
-rw-r--r--bsp/rockchip/dm/dvfs/cpufreq/SConscript13
-rw-r--r--bsp/rockchip/dm/dvfs/cpufreq/dvfs-rockchip-cpufreq.c347
-rwxr-xr-xbsp/rockchip/dm/dvfs/devfreq/Kconfig10
-rw-r--r--bsp/rockchip/dm/dvfs/devfreq/SConscript13
-rwxr-xr-xbsp/rockchip/dm/dvfs/devfreq/dvfs-rockchip-dmc.c182
-rwxr-xr-xbsp/rockchip/dm/dvfs/event/Kconfig4
-rw-r--r--bsp/rockchip/dm/dvfs/event/SConscript13
-rwxr-xr-xbsp/rockchip/dm/dvfs/event/event-rockchip-dfi.c462
-rwxr-xr-xbsp/rockchip/dm/i2c/i2c-rk3x.c354
-rw-r--r--bsp/rockchip/dm/include/opp-select.h107
-rwxr-xr-xbsp/rockchip/dm/soc/SConscript3
-rw-r--r--bsp/rockchip/dm/soc/opp-select.c833
-rw-r--r--bsp/rockchip/rk3500/.config96
-rw-r--r--bsp/rockchip/rk3500/rtconfig.h45
-rw-r--r--components/drivers/regulator/Kconfig6
-rwxr-xr-xcomponents/drivers/regulator/SConscript3
-rw-r--r--components/drivers/regulator/regulator-fan53555.c1091
20 files changed, 3469 insertions, 147 deletions
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 <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "dvfs.cpufreq"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <opp-select.h>
+
+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 <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "dvfs.rockchip.dmc"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+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 <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "dvfs.dfi"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#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 <finsh.h>
+
+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 <device name>\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 */
+/* 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, 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;
- i2c_writel(i2c, REG_INT_START, REG_IEN);
+ 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);
-}
-
-/* IRQ handlers for individual states */
-static void rk3x_i2c_handle_start(struct rk3x_i2c *i2c, rt_uint32_t ipd)
-{
- if (!(ipd & REG_INT_START))
+ if (sendend)
{
- rk3x_i2c_stop(i2c, -RT_EIO);
- LOG_W("Unexpected irq in START: 0x%x", ipd);
- rk3x_i2c_clean_ipd(i2c);
-
- return;
+ i2c_writel(i2c, cnt, REG_MTXCNT);
}
- /* ack interrupt */
- i2c_writel(i2c, REG_INT_START, REG_IPD);
+ return cnt;
+}
- /* disable start bit */
- i2c_writel(i2c, i2c_readl(i2c, REG_CON) & ~REG_CON_START, REG_CON);
+static void rk3x_i2c_read_chunk(struct rk3x_i2c *i2c)
+{
+ rt_uint32_t val = 0;
+ rt_uint32_t len = i2c->msg->len - i2c->processed;
- /* enable appropriate interrupts and transition */
- if (i2c->mode == REG_CON_MOD_TX)
+ if (len > 32)
{
- i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
- i2c->state = STATE_WRITE;
- rk3x_i2c_fill_transmit_buf(i2c);
+ len = 32;
}
- else
+
+ for (rt_uint32_t i = 0; i < len; ++i)
{
- /* 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);
+ 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_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;
- }
-
- /* read the data from receive buffer */
- for (int i = 0; i < len; ++i)
- {
- rt_uint8_t byte;
+ rk3x_i2c_read_chunk(i2c);
- if (i % 4 == 0)
- {
- val = i2c_readl(i2c, RXBUFFER_BASE + (i / 4) * 4);
- }
+ rk3x_i2c_auto_stop(i2c);
- 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 <rtthread.h>
+#include <rtdevice.h>
+
+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 <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "dvfs.opp"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <opp-select.h>
+#ifdef RT_SOC_ROCKCHIP_PVTM
+#include <rockchip.h>
+#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
@@ -862,12 +886,31 @@ CONFIG_RT_UTEST_SMP_CALL_FUNC=y
# 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
#
# CONFIG_RT_UTEST_TC_USING_SAL is not set
@@ -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 <rtdbg.h>
+
+#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 = &reg;
+ 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);