.. | .. |
---|
6 | 6 | #include <linux/clk.h> |
---|
7 | 7 | #include <linux/cpufreq.h> |
---|
8 | 8 | #include <linux/devfreq.h> |
---|
| 9 | +#include <linux/mfd/syscon.h> |
---|
9 | 10 | #include <linux/module.h> |
---|
10 | 11 | #include <linux/nvmem-consumer.h> |
---|
| 12 | +#include <linux/regmap.h> |
---|
11 | 13 | #include <linux/regulator/consumer.h> |
---|
12 | 14 | #include <linux/slab.h> |
---|
13 | 15 | #include <linux/soc/rockchip/pvtm.h> |
---|
.. | .. |
---|
51 | 53 | unsigned int num; |
---|
52 | 54 | unsigned int err; |
---|
53 | 55 | unsigned int ref_temp; |
---|
| 56 | + unsigned int offset; |
---|
54 | 57 | int temp_prop[2]; |
---|
55 | 58 | const char *tz_name; |
---|
56 | 59 | struct thermal_zone_device *tz; |
---|
| 60 | + struct regmap *grf; |
---|
57 | 61 | }; |
---|
58 | 62 | |
---|
59 | 63 | struct lkg_conversion_table { |
---|
60 | 64 | int temp; |
---|
61 | 65 | int conv; |
---|
62 | 66 | }; |
---|
| 67 | + |
---|
| 68 | +struct otp_opp_info { |
---|
| 69 | + u16 min_freq; |
---|
| 70 | + u16 max_freq; |
---|
| 71 | + u8 volt; |
---|
| 72 | + u8 length; |
---|
| 73 | +} __packed; |
---|
63 | 74 | |
---|
64 | 75 | #define PVTM_CH_MAX 8 |
---|
65 | 76 | #define PVTM_SUB_CH_MAX 8 |
---|
.. | .. |
---|
298 | 309 | return -EINVAL; |
---|
299 | 310 | if (of_property_read_u32(np, "rockchip,pvtm-volt", &pvtm->volt)) |
---|
300 | 311 | return -EINVAL; |
---|
301 | | - if (of_property_read_u32_array(np, "rockchip,pvtm-ch", pvtm->ch, 2)) |
---|
302 | | - return -EINVAL; |
---|
303 | | - if (pvtm->ch[0] >= PVTM_CH_MAX || pvtm->ch[1] >= PVTM_SUB_CH_MAX) |
---|
304 | | - return -EINVAL; |
---|
305 | 312 | if (of_property_read_u32(np, "rockchip,pvtm-sample-time", |
---|
306 | 313 | &pvtm->sample_time)) |
---|
307 | | - return -EINVAL; |
---|
308 | | - if (of_property_read_u32(np, "rockchip,pvtm-number", &pvtm->num)) |
---|
309 | | - return -EINVAL; |
---|
310 | | - if (of_property_read_u32(np, "rockchip,pvtm-error", &pvtm->err)) |
---|
311 | 314 | return -EINVAL; |
---|
312 | 315 | if (of_property_read_u32(np, "rockchip,pvtm-ref-temp", &pvtm->ref_temp)) |
---|
313 | 316 | return -EINVAL; |
---|
.. | .. |
---|
324 | 327 | if (IS_ERR(pvtm->tz)) |
---|
325 | 328 | return -EINVAL; |
---|
326 | 329 | if (!pvtm->tz->ops->get_temp) |
---|
| 330 | + return -EINVAL; |
---|
| 331 | + if (of_property_read_bool(np, "rockchip,pvtm-pvtpll")) { |
---|
| 332 | + if (of_property_read_u32(np, "rockchip,pvtm-offset", |
---|
| 333 | + &pvtm->offset)) |
---|
| 334 | + return -EINVAL; |
---|
| 335 | + pvtm->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); |
---|
| 336 | + if (IS_ERR(pvtm->grf)) |
---|
| 337 | + return -EINVAL; |
---|
| 338 | + return 0; |
---|
| 339 | + } |
---|
| 340 | + if (of_property_read_u32_array(np, "rockchip,pvtm-ch", pvtm->ch, 2)) |
---|
| 341 | + return -EINVAL; |
---|
| 342 | + if (pvtm->ch[0] >= PVTM_CH_MAX || pvtm->ch[1] >= PVTM_SUB_CH_MAX) |
---|
| 343 | + return -EINVAL; |
---|
| 344 | + if (of_property_read_u32(np, "rockchip,pvtm-number", &pvtm->num)) |
---|
| 345 | + return -EINVAL; |
---|
| 346 | + if (of_property_read_u32(np, "rockchip,pvtm-error", &pvtm->err)) |
---|
327 | 347 | return -EINVAL; |
---|
328 | 348 | |
---|
329 | 349 | return 0; |
---|
.. | .. |
---|
415 | 435 | cur_temp, *target_value, avg_value, diff_value); |
---|
416 | 436 | |
---|
417 | 437 | resetore_volt: |
---|
418 | | - regulator_set_voltage(reg, old_volt, old_volt); |
---|
| 438 | + regulator_set_voltage(reg, old_volt, INT_MAX); |
---|
419 | 439 | restore_clk: |
---|
420 | 440 | clk_set_rate(clk, old_freq); |
---|
421 | 441 | pvtm_value_out: |
---|
.. | .. |
---|
694 | 714 | } |
---|
695 | 715 | EXPORT_SYMBOL(rockchip_of_get_lkg_sel); |
---|
696 | 716 | |
---|
| 717 | +static int rockchip_get_pvtm_pvtpll(struct device *dev, struct device_node *np, |
---|
| 718 | + char *reg_name) |
---|
| 719 | +{ |
---|
| 720 | + struct regulator *reg; |
---|
| 721 | + struct clk *clk; |
---|
| 722 | + struct pvtm_config *pvtm; |
---|
| 723 | + unsigned long old_freq; |
---|
| 724 | + unsigned int old_volt; |
---|
| 725 | + int cur_temp, diff_temp, prop_temp, diff_value; |
---|
| 726 | + int pvtm_value = 0; |
---|
| 727 | + int ret = 0; |
---|
| 728 | + |
---|
| 729 | + pvtm = kzalloc(sizeof(*pvtm), GFP_KERNEL); |
---|
| 730 | + if (!pvtm) |
---|
| 731 | + return -ENOMEM; |
---|
| 732 | + |
---|
| 733 | + ret = rockchip_parse_pvtm_config(np, pvtm); |
---|
| 734 | + if (ret) |
---|
| 735 | + goto out; |
---|
| 736 | + |
---|
| 737 | + clk = clk_get(dev, NULL); |
---|
| 738 | + if (IS_ERR_OR_NULL(clk)) { |
---|
| 739 | + dev_warn(dev, "Failed to get clk\n"); |
---|
| 740 | + goto out; |
---|
| 741 | + } |
---|
| 742 | + |
---|
| 743 | + reg = regulator_get_optional(dev, reg_name); |
---|
| 744 | + if (IS_ERR_OR_NULL(reg)) { |
---|
| 745 | + dev_warn(dev, "Failed to get reg\n"); |
---|
| 746 | + clk_put(clk); |
---|
| 747 | + goto out; |
---|
| 748 | + } |
---|
| 749 | + old_freq = clk_get_rate(clk); |
---|
| 750 | + old_volt = regulator_get_voltage(reg); |
---|
| 751 | + |
---|
| 752 | + ret = clk_set_rate(clk, pvtm->freq * 1000); |
---|
| 753 | + if (ret) { |
---|
| 754 | + dev_err(dev, "Failed to set pvtm freq\n"); |
---|
| 755 | + goto put_reg; |
---|
| 756 | + } |
---|
| 757 | + ret = regulator_set_voltage(reg, pvtm->volt, INT_MAX); |
---|
| 758 | + if (ret) { |
---|
| 759 | + dev_err(dev, "Failed to set pvtm_volt\n"); |
---|
| 760 | + goto restore_clk; |
---|
| 761 | + } |
---|
| 762 | + usleep_range(pvtm->sample_time, pvtm->sample_time + 100); |
---|
| 763 | + |
---|
| 764 | + ret = regmap_read(pvtm->grf, pvtm->offset, &pvtm_value); |
---|
| 765 | + if (ret < 0) { |
---|
| 766 | + dev_err(dev, "failed to get pvtm from 0x%x\n", pvtm->offset); |
---|
| 767 | + goto resetore_volt; |
---|
| 768 | + } |
---|
| 769 | + pvtm->tz->ops->get_temp(pvtm->tz, &cur_temp); |
---|
| 770 | + diff_temp = (cur_temp / 1000 - pvtm->ref_temp); |
---|
| 771 | + if (diff_temp < 0) |
---|
| 772 | + prop_temp = pvtm->temp_prop[0]; |
---|
| 773 | + else |
---|
| 774 | + prop_temp = pvtm->temp_prop[1]; |
---|
| 775 | + diff_value = diff_temp * prop_temp / 1000; |
---|
| 776 | + pvtm_value += diff_value; |
---|
| 777 | + |
---|
| 778 | + dev_info(dev, "pvtm=%d\n", pvtm_value); |
---|
| 779 | + |
---|
| 780 | +resetore_volt: |
---|
| 781 | + regulator_set_voltage(reg, old_volt, INT_MAX); |
---|
| 782 | +restore_clk: |
---|
| 783 | + clk_set_rate(clk, old_freq); |
---|
| 784 | +put_reg: |
---|
| 785 | + regulator_put(reg); |
---|
| 786 | + clk_put(clk); |
---|
| 787 | +out: |
---|
| 788 | + kfree(pvtm); |
---|
| 789 | + |
---|
| 790 | + return pvtm_value; |
---|
| 791 | +} |
---|
697 | 792 | |
---|
698 | 793 | static int rockchip_get_pvtm(struct device *dev, struct device_node *np, |
---|
699 | 794 | char *reg_name) |
---|
.. | .. |
---|
750 | 845 | char name[NAME_MAX]; |
---|
751 | 846 | int pvtm, ret; |
---|
752 | 847 | |
---|
753 | | - pvtm = rockchip_get_pvtm(dev, np, reg_name); |
---|
| 848 | + if (of_property_read_bool(np, "rockchip,pvtm-pvtpll")) |
---|
| 849 | + pvtm = rockchip_get_pvtm_pvtpll(dev, np, reg_name); |
---|
| 850 | + else |
---|
| 851 | + pvtm = rockchip_get_pvtm(dev, np, reg_name); |
---|
754 | 852 | if (pvtm <= 0) |
---|
755 | 853 | return; |
---|
756 | 854 | |
---|
.. | .. |
---|
910 | 1008 | struct sel_table *irdrop_table = NULL; |
---|
911 | 1009 | struct opp_table *opp_table; |
---|
912 | 1010 | struct dev_pm_opp *opp; |
---|
| 1011 | + unsigned long tmp_safe_rate = 0; |
---|
913 | 1012 | int evb_irdrop = 0, board_irdrop, delta_irdrop; |
---|
914 | | - int tmp_safe_rate = 0, opp_rate, i, ret = 0; |
---|
| 1013 | + int opp_rate, i, ret = 0; |
---|
915 | 1014 | u32 max_volt = UINT_MAX; |
---|
916 | 1015 | bool reach_max_volt = false; |
---|
917 | 1016 | |
---|
.. | .. |
---|
1003 | 1102 | mutex_unlock(&opp_table->lock); |
---|
1004 | 1103 | } |
---|
1005 | 1104 | |
---|
| 1105 | +static void rockchip_adjust_opp_by_otp(struct device *dev, |
---|
| 1106 | + struct device_node *np) |
---|
| 1107 | +{ |
---|
| 1108 | + struct dev_pm_opp *opp; |
---|
| 1109 | + struct opp_table *opp_table; |
---|
| 1110 | + struct otp_opp_info opp_info = {}; |
---|
| 1111 | + int ret; |
---|
| 1112 | + |
---|
| 1113 | + ret = rockchip_nvmem_cell_read_common(np, "opp-info", &opp_info, |
---|
| 1114 | + sizeof(opp_info)); |
---|
| 1115 | + if (ret || !opp_info.volt) |
---|
| 1116 | + return; |
---|
| 1117 | + |
---|
| 1118 | + dev_info(dev, "adjust opp-table by otp: min=%uM, max=%uM, volt=%umV\n", |
---|
| 1119 | + opp_info.min_freq, opp_info.max_freq, opp_info.volt); |
---|
| 1120 | + |
---|
| 1121 | + opp_table = dev_pm_opp_get_opp_table(dev); |
---|
| 1122 | + if (!opp_table) |
---|
| 1123 | + return; |
---|
| 1124 | + |
---|
| 1125 | + mutex_lock(&opp_table->lock); |
---|
| 1126 | + list_for_each_entry(opp, &opp_table->opp_list, node) { |
---|
| 1127 | + if (!opp->available) |
---|
| 1128 | + continue; |
---|
| 1129 | + if (opp->rate < opp_info.min_freq * 1000000) |
---|
| 1130 | + continue; |
---|
| 1131 | + if (opp->rate > opp_info.max_freq * 1000000) |
---|
| 1132 | + continue; |
---|
| 1133 | + |
---|
| 1134 | + opp->supplies->u_volt += opp_info.volt * 1000; |
---|
| 1135 | + if (opp->supplies->u_volt > opp->supplies->u_volt_max) |
---|
| 1136 | + opp->supplies->u_volt = opp->supplies->u_volt_max; |
---|
| 1137 | + } |
---|
| 1138 | + mutex_unlock(&opp_table->lock); |
---|
| 1139 | + |
---|
| 1140 | + dev_pm_opp_put_opp_table(opp_table); |
---|
| 1141 | +} |
---|
| 1142 | + |
---|
1006 | 1143 | static int rockchip_adjust_opp_table(struct device *dev, |
---|
1007 | 1144 | unsigned long scale_rate) |
---|
1008 | 1145 | { |
---|
.. | .. |
---|
1049 | 1186 | of_property_read_u32(np, "rockchip,avs-enable", &avs); |
---|
1050 | 1187 | of_property_read_u32(np, "rockchip,avs", &avs); |
---|
1051 | 1188 | of_property_read_u32(np, "rockchip,avs-scale", &avs_scale); |
---|
| 1189 | + rockchip_adjust_opp_by_otp(dev, np); |
---|
1052 | 1190 | rockchip_adjust_opp_by_mbist_vmin(dev, np); |
---|
1053 | 1191 | rockchip_adjust_opp_by_irdrop(dev, np, &safe_rate, &max_rate); |
---|
1054 | 1192 | |
---|