// SPDX-License-Identifier: GPL-2.0-only /* * Rockchip PVTM support. * * Copyright (c) 2016 Rockchip Electronics Co., Ltd. * Author: Finley Xiao */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define wr_mask_bit(v, off, mask) ((v) << (off) | (mask) << (16 + off)) #define PVTM(_id, _name, _num_rings, _start, _en, _cal, _done, _freq) \ { \ .id = _id, \ .name = _name, \ .num_rings = _num_rings, \ .bit_start = _start, \ .bit_en = _en, \ .reg_cal = _cal, \ .bit_freq_done = _done, \ .reg_freq = _freq, \ } struct rockchip_pvtm; struct rockchip_pvtm_ops { u32 (*get_value)(struct rockchip_pvtm *pvtm, unsigned int ring_sel, unsigned int time_us); void (*set_ring_sel)(struct rockchip_pvtm *pvtm, unsigned int ring_sel); }; struct rockchip_pvtm_info { u32 reg_cal; u32 reg_freq; unsigned char id; unsigned char *name; unsigned int num_rings; unsigned int bit_start; unsigned int bit_en; unsigned int bit_freq_done; }; struct rockchip_pvtm_data { u32 con; u32 sta; unsigned int num_pvtms; const struct rockchip_pvtm_info *infos; const struct rockchip_pvtm_ops ops; }; struct rockchip_pvtm { u32 con; u32 sta; struct list_head node; struct device *dev; struct regmap *grf; void __iomem *base; int num_clks; struct clk_bulk_data *clks; struct reset_control *rst; struct thermal_zone_device *tz; const struct rockchip_pvtm_info *info; const struct rockchip_pvtm_ops *ops; struct dentry *dentry; }; static LIST_HEAD(pvtm_list); #ifdef CONFIG_DEBUG_FS static struct dentry *rockchip_pvtm_debugfs_root; static int pvtm_value_show(struct seq_file *s, void *data) { struct rockchip_pvtm *pvtm = (struct rockchip_pvtm *)s->private; u32 value; int i, ret, cur_temp; if (!pvtm || !pvtm->ops->get_value) { seq_puts(s, "unsupported\n"); return 0; } if (pvtm->tz && pvtm->tz->ops && pvtm->tz->ops->get_temp) { ret = pvtm->tz->ops->get_temp(pvtm->tz, &cur_temp); if (ret) dev_err(pvtm->dev, "debug failed to get temp\n"); else seq_printf(s, "temp: %d ", cur_temp); } seq_puts(s, "pvtm: "); for (i = 0; i < pvtm->info->num_rings; i++) { value = pvtm->ops->get_value(pvtm, i, 1000); seq_printf(s, "%d ", value); } seq_puts(s, "\n"); return 0; } static int pvtm_value_open(struct inode *inode, struct file *file) { return single_open(file, pvtm_value_show, inode->i_private); } static const struct file_operations pvtm_value_fops = { .open = pvtm_value_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int rockchip_pvtm_debugfs_init(void) { rockchip_pvtm_debugfs_root = debugfs_create_dir("pvtm", NULL); if (IS_ERR_OR_NULL(rockchip_pvtm_debugfs_root)) { pr_err("Failed to create pvtm debug directory\n"); rockchip_pvtm_debugfs_root = NULL; return -ENOMEM; } return 0; } static void rockchip_pvtm_debugfs_exit(void) { debugfs_remove_recursive(rockchip_pvtm_debugfs_root); } static int rockchip_pvtm_add_debugfs(struct rockchip_pvtm *pvtm) { struct dentry *d; if (!rockchip_pvtm_debugfs_root) return 0; pvtm->dentry = debugfs_create_dir(pvtm->info->name, rockchip_pvtm_debugfs_root); if (!pvtm->dentry) { dev_err(pvtm->dev, "failed to create pvtm %s debug dir\n", pvtm->info->name); return -ENOMEM; } d = debugfs_create_file("value", 0444, pvtm->dentry, (void *)pvtm, &pvtm_value_fops); if (!d) { dev_err(pvtm->dev, "failed to pvtm %s value node\n", pvtm->info->name); debugfs_remove_recursive(pvtm->dentry); return -ENOMEM; } return 0; } #else static inline int rockchip_pvtm_debugfs_init(void) { return 0; } static inline void rockchip_pvtm_debugfs_exit(void) { } static inline int rockchip_pvtm_add_debugfs(struct rockchip_pvtm *pvtm) { return 0; } #endif static int rockchip_pvtm_reset(struct rockchip_pvtm *pvtm) { int ret; ret = reset_control_assert(pvtm->rst); if (ret) { dev_err(pvtm->dev, "failed to assert pvtm %d\n", ret); return ret; } udelay(2); ret = reset_control_deassert(pvtm->rst); if (ret) { dev_err(pvtm->dev, "failed to deassert pvtm %d\n", ret); return ret; } return 0; } u32 rockchip_get_pvtm_value(unsigned int id, unsigned int ring_sel, unsigned int time_us) { struct rockchip_pvtm *p, *pvtm = NULL; if (list_empty(&pvtm_list)) { pr_err("pvtm list NULL\n"); return -EINVAL; } list_for_each_entry(p, &pvtm_list, node) { if (p->info->id == id) { pvtm = p; break; } } if (!pvtm) { pr_err("invalid pvtm id %d\n", id); return -EINVAL; } if (ring_sel >= pvtm->info->num_rings) { pr_err("invalid pvtm ring %d\n", ring_sel); return -EINVAL; } return pvtm->ops->get_value(pvtm, ring_sel, time_us); } EXPORT_SYMBOL(rockchip_get_pvtm_value); static void rockchip_pvtm_delay(unsigned int delay) { unsigned int ms = delay / 1000; unsigned int us = delay % 1000; if (ms > 0) { if (ms < 20) us += ms * 1000; else msleep(ms); } if (us >= 10) usleep_range(us, us + 100); else udelay(us); } static void px30_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm, unsigned int ring_sel) { unsigned int id = pvtm->info->id; regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(ring_sel, (id * 0x4 + 0x2), 0x3)); } static void rk1808_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm, unsigned int ring_sel) { regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(ring_sel, 0x2, 0x7)); } static void rk3399_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm, unsigned int ring_sel) { unsigned int id = pvtm->info->id; if (id == 1) { regmap_write(pvtm->grf, pvtm->con + 0x14, wr_mask_bit(ring_sel >> 0x3, 0, 0x1)); ring_sel &= 0x3; } if (id != 4) regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(ring_sel, (id * 0x4 + 0x2), 0x3)); } static u32 rockchip_pvtm_get_value(struct rockchip_pvtm *pvtm, unsigned int ring_sel, unsigned int time_us) { const struct rockchip_pvtm_info *info = pvtm->info; unsigned int clk_cnt, check_cnt = 100; u32 sta, val = 0; int ret; ret = clk_bulk_prepare_enable(pvtm->num_clks, pvtm->clks); if (ret < 0) { dev_err(pvtm->dev, "failed to prepare/enable pvtm clks\n"); return 0; } ret = rockchip_pvtm_reset(pvtm); if (ret) { dev_err(pvtm->dev, "failed to reset pvtm\n"); goto disable_clks; } /* if last status is enabled, stop calculating cycles first*/ regmap_read(pvtm->grf, pvtm->con, &sta); if (sta & BIT(info->bit_en)) regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(0, info->bit_start, 0x1)); regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(0x1, info->bit_en, 0x1)); if (pvtm->ops->set_ring_sel) pvtm->ops->set_ring_sel(pvtm, ring_sel); /* clk = 24 Mhz, T = 1 / 24 us */ clk_cnt = time_us * 24; regmap_write(pvtm->grf, pvtm->con + info->reg_cal, clk_cnt); regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(0x1, info->bit_start, 0x1)); rockchip_pvtm_delay(time_us); while (check_cnt) { regmap_read(pvtm->grf, pvtm->sta, &sta); if (sta & BIT(info->bit_freq_done)) break; udelay(4); check_cnt--; } if (check_cnt) { regmap_read(pvtm->grf, pvtm->sta + info->reg_freq, &val); } else { dev_err(pvtm->dev, "wait pvtm_done timeout!\n"); val = 0; } regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(0, info->bit_start, 0x1)); regmap_write(pvtm->grf, pvtm->con, wr_mask_bit(0, info->bit_en, 0x1)); disable_clks: clk_bulk_disable_unprepare(pvtm->num_clks, pvtm->clks); return val; } static void rv1106_core_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm, unsigned int ring_sel) { writel_relaxed(wr_mask_bit(ring_sel + 4, 0x2, 0x7), pvtm->base + pvtm->con); } static void rv1126_pvtm_set_ring_sel(struct rockchip_pvtm *pvtm, unsigned int ring_sel) { writel_relaxed(wr_mask_bit(ring_sel, 0x2, 0x7), pvtm->base + pvtm->con); } static u32 rv1126_pvtm_get_value(struct rockchip_pvtm *pvtm, unsigned int ring_sel, unsigned int time_us) { const struct rockchip_pvtm_info *info = pvtm->info; unsigned int clk_cnt, check_cnt = 100; u32 sta, val = 0; int ret; ret = clk_bulk_prepare_enable(pvtm->num_clks, pvtm->clks); if (ret < 0) { dev_err(pvtm->dev, "failed to prepare/enable pvtm clks\n"); return 0; } ret = rockchip_pvtm_reset(pvtm); if (ret) { dev_err(pvtm->dev, "failed to reset pvtm\n"); goto disable_clks; } /* if last status is enabled, stop calculating cycles first*/ sta = readl_relaxed(pvtm->base + pvtm->con); if (sta & BIT(info->bit_en)) writel_relaxed(wr_mask_bit(0, info->bit_start, 0x1), pvtm->base + pvtm->con); writel_relaxed(wr_mask_bit(0x1, info->bit_en, 0x1), pvtm->base + pvtm->con); if (pvtm->ops->set_ring_sel) pvtm->ops->set_ring_sel(pvtm, ring_sel); /* clk = 24 Mhz, T = 1 / 24 us */ clk_cnt = time_us * 24; writel_relaxed(clk_cnt, pvtm->base + pvtm->con + info->reg_cal); writel_relaxed(wr_mask_bit(0x1, info->bit_start, 0x1), pvtm->base + pvtm->con); rockchip_pvtm_delay(time_us); while (check_cnt) { sta = readl_relaxed(pvtm->base + pvtm->sta); if (sta & BIT(info->bit_freq_done)) break; udelay(4); check_cnt--; } if (check_cnt) { val = readl_relaxed(pvtm->base + pvtm->sta + info->reg_freq); } else { dev_err(pvtm->dev, "wait pvtm_done timeout!\n"); val = 0; } writel_relaxed(wr_mask_bit(0, info->bit_start, 0x1), pvtm->base + pvtm->con); writel_relaxed(wr_mask_bit(0, info->bit_en, 0x1), pvtm->base + pvtm->con); disable_clks: clk_bulk_disable_unprepare(pvtm->num_clks, pvtm->clks); return val; } static const struct rockchip_pvtm_info px30_pvtm_infos[] = { PVTM(0, "core", 3, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data px30_pvtm = { .con = 0x80, .sta = 0x88, .num_pvtms = ARRAY_SIZE(px30_pvtm_infos), .infos = px30_pvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, .set_ring_sel = px30_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info px30_pmupvtm_infos[] = { PVTM(1, "pmu", 1, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data px30_pmupvtm = { .con = 0x180, .sta = 0x190, .num_pvtms = ARRAY_SIZE(px30_pmupvtm_infos), .infos = px30_pmupvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, }, }; static const struct rockchip_pvtm_info rk1808_pvtm_infos[] = { PVTM(0, "core", 5, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk1808_pvtm = { .con = 0x80, .sta = 0x88, .num_pvtms = ARRAY_SIZE(rk1808_pvtm_infos), .infos = rk1808_pvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, .set_ring_sel = rk1808_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk1808_pmupvtm_infos[] = { PVTM(1, "pmu", 1, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk1808_pmupvtm = { .con = 0x180, .sta = 0x190, .num_pvtms = ARRAY_SIZE(rk1808_pmupvtm_infos), .infos = rk1808_pmupvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, }, }; static const struct rockchip_pvtm_info rk1808_npupvtm_infos[] = { PVTM(2, "npu", 5, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk1808_npupvtm = { .con = 0x780, .sta = 0x788, .num_pvtms = ARRAY_SIZE(rk1808_npupvtm_infos), .infos = rk1808_npupvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, .set_ring_sel = rk1808_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3288_pvtm_infos[] = { PVTM(0, "core", 1, 0, 1, 0x4, 1, 0x4), PVTM(1, "gpu", 1, 8, 9, 0x8, 0, 0x8), }; static const struct rockchip_pvtm_data rk3288_pvtm = { .con = 0x368, .sta = 0x374, .num_pvtms = ARRAY_SIZE(rk3288_pvtm_infos), .infos = rk3288_pvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, }, }; static const struct rockchip_pvtm_data rk3308_pmupvtm = { .con = 0x440, .sta = 0x448, .num_pvtms = ARRAY_SIZE(px30_pmupvtm_infos), .infos = px30_pmupvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, }, }; static const struct rockchip_pvtm_info rk3399_pvtm_infos[] = { PVTM(0, "core_l", 4, 0, 1, 0x4, 0, 0x4), PVTM(1, "core_b", 6, 4, 5, 0x8, 1, 0x8), PVTM(2, "ddr", 4, 8, 9, 0xc, 3, 0x10), PVTM(3, "gpu", 4, 12, 13, 0x10, 2, 0xc), }; static const struct rockchip_pvtm_data rk3399_pvtm = { .con = 0xe600, .sta = 0xe620, .num_pvtms = ARRAY_SIZE(rk3399_pvtm_infos), .infos = rk3399_pvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, .set_ring_sel = rk3399_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3399_pmupvtm_infos[] = { PVTM(4, "pmu", 1, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3399_pmupvtm = { .con = 0x240, .sta = 0x248, .num_pvtms = ARRAY_SIZE(rk3399_pmupvtm_infos), .infos = rk3399_pmupvtm_infos, .ops = { .get_value = rockchip_pvtm_get_value, }, }; static const struct rockchip_pvtm_info rk3568_corepvtm_infos[] = { PVTM(0, "core", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3568_corepvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3568_corepvtm_infos), .infos = rk3568_corepvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3568_gpupvtm_infos[] = { PVTM(1, "gpu", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3568_gpupvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3568_gpupvtm_infos), .infos = rk3568_gpupvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3568_npupvtm_infos[] = { PVTM(2, "npu", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3568_npupvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3568_npupvtm_infos), .infos = rk3568_npupvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3588_bigcore0_pvtm_infos[] = { PVTM(0, "bigcore0", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3588_bigcore0_pvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3588_bigcore0_pvtm_infos), .infos = rk3588_bigcore0_pvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3588_bigcore1_pvtm_infos[] = { PVTM(1, "bigcore1", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3588_bigcore1_pvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3588_bigcore1_pvtm_infos), .infos = rk3588_bigcore1_pvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3588_litcore_pvtm_infos[] = { PVTM(2, "litcore", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3588_litcore_pvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3588_litcore_pvtm_infos), .infos = rk3588_litcore_pvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3588_npu_pvtm_infos[] = { PVTM(3, "npu", 2, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3588_npu_pvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3588_npu_pvtm_infos), .infos = rk3588_npu_pvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3588_gpu_pvtm_infos[] = { PVTM(4, "gpu", 2, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3588_gpu_pvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3588_gpu_pvtm_infos), .infos = rk3588_gpu_pvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rk3588_pmu_pvtm_infos[] = { PVTM(5, "pmu", 1, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rk3588_pmu_pvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rk3588_pmu_pvtm_infos), .infos = rk3588_pmu_pvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, }, }; static const struct rockchip_pvtm_info rv1106_corepvtm_infos[] = { PVTM(0, "core", 2, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rv1106_corepvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rv1106_corepvtm_infos), .infos = rv1106_corepvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1106_core_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rv1106_pmupvtm_infos[] = { PVTM(1, "pmu", 1, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rv1106_pmupvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rv1106_pmupvtm_infos), .infos = rv1106_pmupvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, }, }; static const struct rockchip_pvtm_info rv1126_cpupvtm_infos[] = { PVTM(0, "cpu", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rv1126_cpupvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rv1126_cpupvtm_infos), .infos = rv1126_cpupvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rv1126_npupvtm_infos[] = { PVTM(1, "npu", 7, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rv1126_npupvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rv1126_npupvtm_infos), .infos = rv1126_npupvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, .set_ring_sel = rv1126_pvtm_set_ring_sel, }, }; static const struct rockchip_pvtm_info rv1126_pmupvtm_infos[] = { PVTM(2, "pmu", 1, 0, 1, 0x4, 0, 0x4), }; static const struct rockchip_pvtm_data rv1126_pmupvtm = { .con = 0x4, .sta = 0x80, .num_pvtms = ARRAY_SIZE(rv1126_pmupvtm_infos), .infos = rv1126_pmupvtm_infos, .ops = { .get_value = rv1126_pvtm_get_value, }, }; static const struct of_device_id rockchip_pvtm_match[] = { #ifdef CONFIG_CPU_PX30 { .compatible = "rockchip,px30-pvtm", .data = (void *)&px30_pvtm, }, { .compatible = "rockchip,px30-pmu-pvtm", .data = (void *)&px30_pmupvtm, }, #endif #ifdef CONFIG_CPU_RK1808 { .compatible = "rockchip,rk1808-pvtm", .data = (void *)&rk1808_pvtm, }, { .compatible = "rockchip,rk1808-pmu-pvtm", .data = (void *)&rk1808_pmupvtm, }, { .compatible = "rockchip,rk1808-npu-pvtm", .data = (void *)&rk1808_npupvtm, }, #endif #ifdef CONFIG_CPU_RK3288 { .compatible = "rockchip,rk3288-pvtm", .data = (void *)&rk3288_pvtm, }, #endif #ifdef CONFIG_CPU_RK3308 { .compatible = "rockchip,rk3308-pvtm", .data = (void *)&px30_pvtm, }, { .compatible = "rockchip,rk3308-pmu-pvtm", .data = (void *)&rk3308_pmupvtm, }, #endif #ifdef CONFIG_CPU_RK3399 { .compatible = "rockchip,rk3399-pvtm", .data = (void *)&rk3399_pvtm, }, { .compatible = "rockchip,rk3399-pmu-pvtm", .data = (void *)&rk3399_pmupvtm, }, #endif #ifdef CONFIG_CPU_RK3568 { .compatible = "rockchip,rK3568-core-pvtm", .data = (void *)&rk3568_corepvtm, }, { .compatible = "rockchip,rk3568-gpu-pvtm", .data = (void *)&rk3568_gpupvtm, }, { .compatible = "rockchip,rk3568-npu-pvtm", .data = (void *)&rk3568_npupvtm, }, #endif #ifdef CONFIG_CPU_RK3588 { .compatible = "rockchip,rk3588-bigcore0-pvtm", .data = (void *)&rk3588_bigcore0_pvtm, }, { .compatible = "rockchip,rk3588-bigcore1-pvtm", .data = (void *)&rk3588_bigcore1_pvtm, }, { .compatible = "rockchip,rk3588-litcore-pvtm", .data = (void *)&rk3588_litcore_pvtm, }, { .compatible = "rockchip,rk3588-gpu-pvtm", .data = (void *)&rk3588_gpu_pvtm, }, { .compatible = "rockchip,rk3588-npu-pvtm", .data = (void *)&rk3588_npu_pvtm, }, { .compatible = "rockchip,rk3588-pmu-pvtm", .data = (void *)&rk3588_pmu_pvtm, }, #endif #ifdef CONFIG_CPU_RV1106 { .compatible = "rockchip,rv1106-core-pvtm", .data = (void *)&rv1106_corepvtm, }, { .compatible = "rockchip,rv1106-pmu-pvtm", .data = (void *)&rv1106_pmupvtm, }, #endif #ifdef CONFIG_CPU_RV1126 { .compatible = "rockchip,rv1126-cpu-pvtm", .data = (void *)&rv1126_cpupvtm, }, { .compatible = "rockchip,rv1126-npu-pvtm", .data = (void *)&rv1126_npupvtm, }, { .compatible = "rockchip,rv1126-pmu-pvtm", .data = (void *)&rv1126_pmupvtm, }, #endif { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, rockchip_pvtm_match); static int rockchip_pvtm_get_index(const struct rockchip_pvtm_data *data, u32 ch, u32 *index) { int i; for (i = 0; i < data->num_pvtms; i++) { if (ch == data->infos[i].id) { *index = i; return 0; } } return -EINVAL; } static struct rockchip_pvtm * rockchip_pvtm_init(struct device *dev, struct device_node *node, const struct rockchip_pvtm_data *data, struct regmap *grf, void __iomem *base) { struct rockchip_pvtm *pvtm; const char *tz_name; u32 id, index; int i; if (of_property_read_u32(node, "reg", &id)) { dev_err(dev, "%s: failed to retrieve pvtm id\n", node->name); return NULL; } if (rockchip_pvtm_get_index(data, id, &index)) { dev_err(dev, "%s: invalid pvtm id %d\n", node->name, id); return NULL; } pvtm = devm_kzalloc(dev, sizeof(*pvtm), GFP_KERNEL); if (!pvtm) return NULL; pvtm->dev = dev; pvtm->grf = grf; pvtm->base = base; pvtm->con = data->con; pvtm->sta = data->sta; pvtm->ops = &data->ops; pvtm->info = &data->infos[index]; if (!of_property_read_string(node, "thermal-zone", &tz_name)) { pvtm->tz = thermal_zone_get_zone_by_name(tz_name); if (IS_ERR(pvtm->tz)) { dev_err(pvtm->dev, "failed to retrieve pvtm_tz\n"); pvtm->tz = NULL; } } pvtm->num_clks = of_clk_get_parent_count(node); if (pvtm->num_clks <= 0) { dev_err(dev, "%s: does not have clocks\n", node->name); goto clk_num_err; } pvtm->clks = devm_kcalloc(dev, pvtm->num_clks, sizeof(*pvtm->clks), GFP_KERNEL); if (!pvtm->clks) goto clk_num_err; for (i = 0; i < pvtm->num_clks; i++) { pvtm->clks[i].clk = of_clk_get(node, i); if (IS_ERR(pvtm->clks[i].clk)) { dev_err(dev, "%s: failed to get clk at index %d\n", node->name, i); goto clk_err; } } pvtm->rst = devm_reset_control_array_get_optional_exclusive(dev); if (IS_ERR(pvtm->rst)) dev_dbg(dev, "%s: failed to get reset\n", node->name); rockchip_pvtm_add_debugfs(pvtm); return pvtm; clk_err: while (--i >= 0) clk_put(pvtm->clks[i].clk); devm_kfree(dev, pvtm->clks); clk_num_err: devm_kfree(dev, pvtm); return NULL; } static int rockchip_pvtm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct device_node *node; const struct of_device_id *match; struct rockchip_pvtm *pvtm; struct regmap *grf = NULL; void __iomem *base = NULL; match = of_match_device(dev->driver->of_match_table, dev); if (!match || !match->data) { dev_err(dev, "missing pvtm data\n"); return -EINVAL; } if (dev->parent && dev->parent->of_node) { grf = syscon_node_to_regmap(dev->parent->of_node); if (IS_ERR(grf)) return PTR_ERR(grf); } else { base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); } for_each_available_child_of_node(np, node) { pvtm = rockchip_pvtm_init(dev, node, match->data, grf, base); if (!pvtm) { dev_err(dev, "failed to handle node %s\n", node->full_name); continue; } list_add(&pvtm->node, &pvtm_list); dev_info(dev, "%s probed\n", node->full_name); } return 0; } static struct platform_driver rockchip_pvtm_driver = { .probe = rockchip_pvtm_probe, .driver = { .name = "rockchip-pvtm", .of_match_table = rockchip_pvtm_match, }, }; static int __init rockchip_pvtm_module_init(void) { rockchip_pvtm_debugfs_init(); return platform_driver_register(&rockchip_pvtm_driver); } module_init(rockchip_pvtm_module_init); static void __exit rockchip_pvtm_module_exit(void) { rockchip_pvtm_debugfs_exit(); platform_driver_unregister(&rockchip_pvtm_driver); } module_exit(rockchip_pvtm_module_exit); MODULE_DESCRIPTION("Rockchip PVTM driver"); MODULE_AUTHOR("Finley Xiao "); MODULE_LICENSE("GPL v2");