diff --git a/drivers/soc/rockchip/rockchip_opp_select.c b/drivers/soc/rockchip/rockchip_opp_select.c index aa01c1c234ee..4126e4b285c4 100644 --- a/drivers/soc/rockchip/rockchip_opp_select.c +++ b/drivers/soc/rockchip/rockchip_opp_select.c @@ -53,9 +53,11 @@ struct pvtm_config { unsigned int num; unsigned int err; unsigned int ref_temp; + unsigned int offset; int temp_prop[2]; const char *tz_name; struct thermal_zone_device *tz; + struct regmap *grf; }; struct lkg_conversion_table { @@ -300,17 +302,9 @@ static int rockchip_parse_pvtm_config(struct device_node *np, return -EINVAL; if (of_property_read_u32(np, "rockchip,pvtm-volt", &pvtm->volt)) return -EINVAL; - if (of_property_read_u32_array(np, "rockchip,pvtm-ch", pvtm->ch, 2)) - return -EINVAL; - if (pvtm->ch[0] >= PVTM_CH_MAX || pvtm->ch[1] >= PVTM_SUB_CH_MAX) - return -EINVAL; if (of_property_read_u32(np, "rockchip,pvtm-sample-time", &pvtm->sample_time)) return -EINVAL; - if (of_property_read_u32(np, "rockchip,pvtm-number", &pvtm->num)) - return -EINVAL; - if (of_property_read_u32(np, "rockchip,pvtm-error", &pvtm->err)) - return -EINVAL; if (of_property_read_u32(np, "rockchip,pvtm-ref-temp", &pvtm->ref_temp)) return -EINVAL; if (of_property_read_u32_array(np, "rockchip,pvtm-temp-prop", @@ -327,6 +321,23 @@ static int rockchip_parse_pvtm_config(struct device_node *np, return -EINVAL; if (!pvtm->tz->ops->get_temp) return -EINVAL; + if (of_property_read_bool(np, "rockchip,pvtm-pvtpll")) { + if (of_property_read_u32(np, "rockchip,pvtm-offset", + &pvtm->offset)) + return -EINVAL; + pvtm->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(pvtm->grf)) + return -EINVAL; + return 0; + } + if (of_property_read_u32_array(np, "rockchip,pvtm-ch", pvtm->ch, 2)) + return -EINVAL; + if (pvtm->ch[0] >= PVTM_CH_MAX || pvtm->ch[1] >= PVTM_SUB_CH_MAX) + return -EINVAL; + if (of_property_read_u32(np, "rockchip,pvtm-number", &pvtm->num)) + return -EINVAL; + if (of_property_read_u32(np, "rockchip,pvtm-error", &pvtm->err)) + return -EINVAL; return 0; } @@ -697,6 +708,82 @@ next: EXPORT_SYMBOL(rockchip_of_get_lkg_sel); +static int rockchip_get_pvtm_pvtpll(struct device *dev, struct device_node *np, + char *reg_name) +{ + struct regulator *reg; + struct clk *clk; + struct pvtm_config *pvtm; + unsigned long old_freq; + unsigned int old_volt; + int cur_temp, diff_temp, prop_temp, diff_value; + int pvtm_value = 0; + int ret = 0; + + pvtm = kzalloc(sizeof(*pvtm), GFP_KERNEL); + if (!pvtm) + return -ENOMEM; + + ret = rockchip_parse_pvtm_config(np, pvtm); + if (ret) + goto out; + + clk = clk_get(dev, NULL); + if (IS_ERR_OR_NULL(clk)) { + dev_warn(dev, "Failed to get clk\n"); + goto out; + } + + reg = regulator_get_optional(dev, reg_name); + if (IS_ERR_OR_NULL(reg)) { + dev_warn(dev, "Failed to get reg\n"); + clk_put(clk); + goto out; + } + old_freq = clk_get_rate(clk); + old_volt = regulator_get_voltage(reg); + + ret = clk_set_rate(clk, pvtm->freq * 1000); + if (ret) { + dev_err(dev, "Failed to set pvtm freq\n"); + goto put_reg; + } + ret = regulator_set_voltage(reg, pvtm->volt, pvtm->volt); + if (ret) { + dev_err(dev, "Failed to set pvtm_volt\n"); + goto restore_clk; + } + usleep_range(pvtm->sample_time, pvtm->sample_time + 100); + + ret = regmap_read(pvtm->grf, pvtm->offset, &pvtm_value); + if (ret < 0) { + dev_err(dev, "failed to get pvtm from 0x%x\n", pvtm->offset); + goto resetore_volt; + } + pvtm->tz->ops->get_temp(pvtm->tz, &cur_temp); + diff_temp = (cur_temp / 1000 - pvtm->ref_temp); + if (diff_temp < 0) + prop_temp = pvtm->temp_prop[0]; + else + prop_temp = pvtm->temp_prop[1]; + diff_value = diff_temp * prop_temp / 1000; + pvtm_value += diff_value; + + dev_info(dev, "pvtm=%d\n", pvtm_value); + +resetore_volt: + regulator_set_voltage(reg, old_volt, old_volt); +restore_clk: + clk_set_rate(clk, old_freq); +put_reg: + regulator_put(reg); + clk_put(clk); +out: + kfree(pvtm); + + return pvtm_value; +} + static int rockchip_get_pvtm(struct device *dev, struct device_node *np, char *reg_name) { @@ -752,7 +839,10 @@ void rockchip_of_get_pvtm_sel(struct device *dev, struct device_node *np, char name[NAME_MAX]; int pvtm, ret; - pvtm = rockchip_get_pvtm(dev, np, reg_name); + if (of_property_read_bool(np, "rockchip,pvtm-pvtpll")) + pvtm = rockchip_get_pvtm_pvtpll(dev, np, reg_name); + else + pvtm = rockchip_get_pvtm(dev, np, reg_name); if (pvtm <= 0) return; @@ -1263,6 +1353,11 @@ int rockchip_init_opp_table(struct device *dev, struct rockchip_opp_info *info, } } info->num_clks = num_clks; + ret = clk_bulk_prepare_enable(info->num_clks, info->clks); + if (ret) { + dev_err(dev, "failed to enable opp clks\n"); + goto out; + } } if (info->data && info->data->set_read_margin) { info->current_rm = UINT_MAX; @@ -1287,9 +1382,13 @@ next: ret = dev_pm_opp_of_add_table(dev); if (ret) { dev_err(dev, "Invalid operating-points in device tree.\n"); - goto out; + goto dis_opp_clk; } rockchip_adjust_power_scale(dev, scale); + +dis_opp_clk: + if (info && info->clks) + clk_bulk_disable_unprepare(info->num_clks, info->clks); out: of_node_put(np);