.. | .. |
---|
12 | 12 | #include <linux/nvmem-consumer.h> |
---|
13 | 13 | #include <linux/regmap.h> |
---|
14 | 14 | #include <linux/regulator/consumer.h> |
---|
| 15 | +#include <linux/rockchip/rockchip_sip.h> |
---|
15 | 16 | #include <linux/slab.h> |
---|
16 | 17 | #include <linux/soc/rockchip/pvtm.h> |
---|
17 | 18 | #include <linux/thermal.h> |
---|
.. | .. |
---|
1004 | 1005 | } |
---|
1005 | 1006 | |
---|
1006 | 1007 | if (of_property_read_u32(np, "rockchip,pvtpll-len-min-rate", &min_rate)) |
---|
1007 | | - return; |
---|
| 1008 | + goto out; |
---|
1008 | 1009 | if (of_property_read_u32(np, "rockchip,pvtpll-len-max-rate", &max_rate)) |
---|
1009 | | - return; |
---|
| 1010 | + goto out; |
---|
1010 | 1011 | if (of_property_read_u32(np, "rockchip,pvtpll-len-margin", &margin)) |
---|
1011 | | - return; |
---|
| 1012 | + goto out; |
---|
1012 | 1013 | |
---|
1013 | 1014 | opp_table = dev_pm_opp_get_opp_table(info->dev); |
---|
1014 | 1015 | if (!opp_table) |
---|
1015 | | - return; |
---|
| 1016 | + goto out; |
---|
1016 | 1017 | old_rate = clk_get_rate(opp_table->clk); |
---|
1017 | 1018 | opp_flag = OPP_ADD_LENGTH | ((margin & OPP_LENGTH_MASK) << OPP_LENGTH_SHIFT); |
---|
1018 | 1019 | |
---|
.. | .. |
---|
1033 | 1034 | clk_set_rate(opp_table->clk, old_rate); |
---|
1034 | 1035 | |
---|
1035 | 1036 | dev_pm_opp_put_opp_table(opp_table); |
---|
| 1037 | +out: |
---|
| 1038 | + of_node_put(np); |
---|
1036 | 1039 | } |
---|
1037 | 1040 | EXPORT_SYMBOL(rockchip_pvtpll_add_length); |
---|
| 1041 | + |
---|
| 1042 | +void rockchip_init_pvtpll_table(struct rockchip_opp_info *info, int bin) |
---|
| 1043 | +{ |
---|
| 1044 | + struct device_node *np = NULL; |
---|
| 1045 | + struct property *prop = NULL; |
---|
| 1046 | + struct of_phandle_args clkspec = { 0 }; |
---|
| 1047 | + struct arm_smccc_res res; |
---|
| 1048 | + char prop_name[NAME_MAX]; |
---|
| 1049 | + u32 *value; |
---|
| 1050 | + int count; |
---|
| 1051 | + int ret, i; |
---|
| 1052 | + |
---|
| 1053 | + if (!info) |
---|
| 1054 | + return; |
---|
| 1055 | + |
---|
| 1056 | + np = of_parse_phandle(info->dev->of_node, "operating-points-v2", 0); |
---|
| 1057 | + if (!np) { |
---|
| 1058 | + dev_warn(info->dev, "OPP-v2 not supported\n"); |
---|
| 1059 | + return; |
---|
| 1060 | + } |
---|
| 1061 | + |
---|
| 1062 | + ret = of_parse_phandle_with_args(info->dev->of_node, "clocks", |
---|
| 1063 | + "#clock-cells", 0, &clkspec); |
---|
| 1064 | + if (ret) |
---|
| 1065 | + goto out; |
---|
| 1066 | + info->pvtpll_clk_id = clkspec.args[0]; |
---|
| 1067 | + of_node_put(clkspec.np); |
---|
| 1068 | + |
---|
| 1069 | + res = sip_smc_get_pvtpll_info(PVTPLL_GET_INFO, info->pvtpll_clk_id); |
---|
| 1070 | + if (res.a0) |
---|
| 1071 | + goto out; |
---|
| 1072 | + if (!res.a1) |
---|
| 1073 | + info->pvtpll_low_temp = true; |
---|
| 1074 | + |
---|
| 1075 | + if (bin > 0) { |
---|
| 1076 | + snprintf(prop_name, sizeof(prop_name), |
---|
| 1077 | + "rockchip,pvtpll-table-B%d", bin); |
---|
| 1078 | + prop = of_find_property(np, prop_name, NULL); |
---|
| 1079 | + } |
---|
| 1080 | + if (!prop) |
---|
| 1081 | + sprintf(prop_name, "rockchip,pvtpll-table"); |
---|
| 1082 | + |
---|
| 1083 | + prop = of_find_property(np, prop_name, NULL); |
---|
| 1084 | + if (!prop) |
---|
| 1085 | + goto out; |
---|
| 1086 | + |
---|
| 1087 | + count = of_property_count_u32_elems(np, prop_name); |
---|
| 1088 | + if (count < 0) { |
---|
| 1089 | + dev_err(info->dev, "%s: Invalid %s property (%d)\n", |
---|
| 1090 | + __func__, prop_name, count); |
---|
| 1091 | + goto out; |
---|
| 1092 | + } else if (count % 5) { |
---|
| 1093 | + dev_err(info->dev, "Invalid count of %s\n", prop_name); |
---|
| 1094 | + goto out; |
---|
| 1095 | + } |
---|
| 1096 | + |
---|
| 1097 | + value = kmalloc_array(count, sizeof(*value), GFP_KERNEL); |
---|
| 1098 | + if (!value) |
---|
| 1099 | + goto out; |
---|
| 1100 | + ret = of_property_read_u32_array(np, prop_name, value, count); |
---|
| 1101 | + if (ret) { |
---|
| 1102 | + dev_err(info->dev, "%s: error parsing %s: %d\n", |
---|
| 1103 | + __func__, prop_name, ret); |
---|
| 1104 | + goto free_value; |
---|
| 1105 | + } |
---|
| 1106 | + |
---|
| 1107 | + for (i = 0; i < count; i += 5) { |
---|
| 1108 | + res = sip_smc_pvtpll_config(PVTPLL_ADJUST_TABLE, |
---|
| 1109 | + info->pvtpll_clk_id, value[i], |
---|
| 1110 | + value[i + 1], value[i + 2], |
---|
| 1111 | + value[i + 3], value[i + 4]); |
---|
| 1112 | + if (res.a0) { |
---|
| 1113 | + dev_err(info->dev, |
---|
| 1114 | + "%s: error cfg clk_id=%u %u %u %u %u %u (%d)\n", |
---|
| 1115 | + __func__, info->pvtpll_clk_id, value[i], |
---|
| 1116 | + value[i + 1], value[i + 2], value[i + 3], |
---|
| 1117 | + value[i + 4], (int)res.a0); |
---|
| 1118 | + goto free_value; |
---|
| 1119 | + } |
---|
| 1120 | + } |
---|
| 1121 | + |
---|
| 1122 | +free_value: |
---|
| 1123 | + kfree(value); |
---|
| 1124 | +out: |
---|
| 1125 | + of_node_put(np); |
---|
| 1126 | +} |
---|
| 1127 | +EXPORT_SYMBOL(rockchip_init_pvtpll_table); |
---|
1038 | 1128 | |
---|
1039 | 1129 | static int rockchip_get_pvtm_pvtpll(struct device *dev, struct device_node *np, |
---|
1040 | 1130 | char *reg_name) |
---|
.. | .. |
---|
1186 | 1276 | snprintf(name, sizeof(name), |
---|
1187 | 1277 | "rockchip,p%d-pvtm-voltage-sel", process); |
---|
1188 | 1278 | prop = of_find_property(np, name, NULL); |
---|
1189 | | - } else if (bin >= 0) { |
---|
| 1279 | + } else if (bin > 0) { |
---|
1190 | 1280 | of_property_read_u32(np, "rockchip,pvtm-hw", &hw); |
---|
1191 | 1281 | if (hw && (hw & BIT(bin))) { |
---|
1192 | 1282 | sprintf(name, "rockchip,pvtm-voltage-sel-hw"); |
---|
| 1283 | + prop = of_find_property(np, name, NULL); |
---|
| 1284 | + } |
---|
| 1285 | + if (!prop) { |
---|
| 1286 | + snprintf(name, sizeof(name), |
---|
| 1287 | + "rockchip,pvtm-voltage-sel-B%d", bin); |
---|
1193 | 1288 | prop = of_find_property(np, name, NULL); |
---|
1194 | 1289 | } |
---|
1195 | 1290 | } |
---|
.. | .. |
---|
1633 | 1728 | rockchip_adjust_opp_by_irdrop(dev, np, &safe_rate, &max_rate); |
---|
1634 | 1729 | |
---|
1635 | 1730 | dev_info(dev, "avs=%d\n", avs); |
---|
| 1731 | + |
---|
| 1732 | + if (!safe_rate && !scale) |
---|
| 1733 | + goto out_np; |
---|
| 1734 | + |
---|
1636 | 1735 | clk = of_clk_get_by_name(np, NULL); |
---|
1637 | 1736 | if (IS_ERR(clk)) { |
---|
1638 | 1737 | if (!safe_rate) |
---|
.. | .. |
---|
1646 | 1745 | |
---|
1647 | 1746 | if (safe_rate) |
---|
1648 | 1747 | irdrop_scale = rockchip_pll_clk_rate_to_scale(clk, safe_rate); |
---|
1649 | | - if (max_rate) |
---|
1650 | | - opp_scale = rockchip_pll_clk_rate_to_scale(clk, max_rate); |
---|
1651 | 1748 | target_scale = max(irdrop_scale, scale); |
---|
1652 | 1749 | if (target_scale <= 0) |
---|
1653 | 1750 | goto out_clk; |
---|
1654 | 1751 | dev_dbg(dev, "target_scale=%d, irdrop_scale=%d, scale=%d\n", |
---|
1655 | 1752 | target_scale, irdrop_scale, scale); |
---|
1656 | 1753 | |
---|
| 1754 | + if (max_rate) |
---|
| 1755 | + opp_scale = rockchip_pll_clk_rate_to_scale(clk, max_rate); |
---|
1657 | 1756 | if (avs == AVS_SCALING_RATE) { |
---|
1658 | 1757 | ret = rockchip_pll_clk_adaptive_scaling(clk, target_scale); |
---|
1659 | 1758 | if (ret) |
---|
.. | .. |
---|
1838 | 1937 | } |
---|
1839 | 1938 | EXPORT_SYMBOL(rockchip_set_intermediate_rate); |
---|
1840 | 1939 | |
---|
| 1940 | +static int rockchip_get_opp_clk(struct device *dev, struct device_node *np, |
---|
| 1941 | + struct rockchip_opp_info *info) |
---|
| 1942 | +{ |
---|
| 1943 | + struct clk_bulk_data *clks; |
---|
| 1944 | + struct of_phandle_args clkspec; |
---|
| 1945 | + int ret = 0, num_clks = 0, i; |
---|
| 1946 | + |
---|
| 1947 | + if (of_find_property(np, "rockchip,opp-clocks", NULL)) { |
---|
| 1948 | + num_clks = of_count_phandle_with_args(np, "rockchip,opp-clocks", |
---|
| 1949 | + "#clock-cells"); |
---|
| 1950 | + if (num_clks <= 0) |
---|
| 1951 | + return 0; |
---|
| 1952 | + clks = devm_kcalloc(dev, num_clks, sizeof(*clks), GFP_KERNEL); |
---|
| 1953 | + if (!clks) |
---|
| 1954 | + return -ENOMEM; |
---|
| 1955 | + for (i = 0; i < num_clks; i++) { |
---|
| 1956 | + ret = of_parse_phandle_with_args(np, |
---|
| 1957 | + "rockchip,opp-clocks", |
---|
| 1958 | + "#clock-cells", i, |
---|
| 1959 | + &clkspec); |
---|
| 1960 | + if (ret < 0) { |
---|
| 1961 | + dev_err(dev, "%s: failed to parse opp clk %d\n", |
---|
| 1962 | + np->name, i); |
---|
| 1963 | + goto error; |
---|
| 1964 | + } |
---|
| 1965 | + clks[i].clk = of_clk_get_from_provider(&clkspec); |
---|
| 1966 | + of_node_put(clkspec.np); |
---|
| 1967 | + if (IS_ERR(clks[i].clk)) { |
---|
| 1968 | + ret = PTR_ERR(clks[i].clk); |
---|
| 1969 | + clks[i].clk = NULL; |
---|
| 1970 | + dev_err(dev, "%s: failed to get opp clk %d\n", |
---|
| 1971 | + np->name, i); |
---|
| 1972 | + goto error; |
---|
| 1973 | + } |
---|
| 1974 | + } |
---|
| 1975 | + } else { |
---|
| 1976 | + num_clks = of_clk_get_parent_count(np); |
---|
| 1977 | + if (num_clks <= 0) |
---|
| 1978 | + return 0; |
---|
| 1979 | + clks = devm_kcalloc(dev, num_clks, sizeof(*clks), GFP_KERNEL); |
---|
| 1980 | + if (!clks) |
---|
| 1981 | + return -ENOMEM; |
---|
| 1982 | + for (i = 0; i < num_clks; i++) { |
---|
| 1983 | + clks[i].clk = of_clk_get(np, i); |
---|
| 1984 | + if (IS_ERR(clks[i].clk)) { |
---|
| 1985 | + ret = PTR_ERR(clks[i].clk); |
---|
| 1986 | + clks[i].clk = NULL; |
---|
| 1987 | + dev_err(dev, "%s: failed to get clk %d\n", |
---|
| 1988 | + np->name, i); |
---|
| 1989 | + goto error; |
---|
| 1990 | + } |
---|
| 1991 | + } |
---|
| 1992 | + } |
---|
| 1993 | + info->clks = clks; |
---|
| 1994 | + info->num_clks = num_clks; |
---|
| 1995 | + |
---|
| 1996 | + return 0; |
---|
| 1997 | +error: |
---|
| 1998 | + while (--i >= 0) |
---|
| 1999 | + clk_put(clks[i].clk); |
---|
| 2000 | + devm_kfree(dev, clks); |
---|
| 2001 | + |
---|
| 2002 | + return ret; |
---|
| 2003 | +} |
---|
| 2004 | + |
---|
1841 | 2005 | int rockchip_init_opp_table(struct device *dev, struct rockchip_opp_info *info, |
---|
1842 | 2006 | char *lkg_name, char *reg_name) |
---|
1843 | 2007 | { |
---|
1844 | 2008 | struct device_node *np; |
---|
1845 | 2009 | int bin = -EINVAL, process = -EINVAL; |
---|
1846 | 2010 | int scale = 0, volt_sel = -EINVAL; |
---|
1847 | | - int ret = 0, num_clks = 0, i; |
---|
| 2011 | + int ret = 0; |
---|
1848 | 2012 | u32 freq; |
---|
1849 | 2013 | |
---|
1850 | 2014 | /* Get OPP descriptor node */ |
---|
.. | .. |
---|
1857 | 2021 | goto next; |
---|
1858 | 2022 | info->dev = dev; |
---|
1859 | 2023 | |
---|
1860 | | - num_clks = of_clk_get_parent_count(np); |
---|
1861 | | - if (num_clks > 0) { |
---|
1862 | | - info->clks = devm_kcalloc(dev, num_clks, sizeof(*info->clks), |
---|
1863 | | - GFP_KERNEL); |
---|
1864 | | - if (!info->clks) { |
---|
1865 | | - ret = -ENOMEM; |
---|
1866 | | - goto out; |
---|
1867 | | - } |
---|
1868 | | - for (i = 0; i < num_clks; i++) { |
---|
1869 | | - info->clks[i].clk = of_clk_get(np, i); |
---|
1870 | | - if (IS_ERR(info->clks[i].clk)) { |
---|
1871 | | - ret = PTR_ERR(info->clks[i].clk); |
---|
1872 | | - dev_err(dev, "%s: failed to get clk %d\n", |
---|
1873 | | - np->name, i); |
---|
1874 | | - goto out; |
---|
1875 | | - } |
---|
1876 | | - } |
---|
1877 | | - info->num_clks = num_clks; |
---|
| 2024 | + ret = rockchip_get_opp_clk(dev, np, info); |
---|
| 2025 | + if (ret) |
---|
| 2026 | + goto out; |
---|
| 2027 | + if (info->clks) { |
---|
1878 | 2028 | ret = clk_bulk_prepare_enable(info->num_clks, info->clks); |
---|
1879 | 2029 | if (ret) { |
---|
1880 | 2030 | dev_err(dev, "failed to enable opp clks\n"); |
---|
.. | .. |
---|
1900 | 2050 | |
---|
1901 | 2051 | next: |
---|
1902 | 2052 | rockchip_get_soc_info(dev, np, &bin, &process); |
---|
| 2053 | + rockchip_init_pvtpll_table(info, bin); |
---|
1903 | 2054 | rockchip_get_scale_volt_sel(dev, lkg_name, reg_name, bin, process, |
---|
1904 | 2055 | &scale, &volt_sel); |
---|
1905 | 2056 | if (info && info->data && info->data->set_soc_info) |
---|
.. | .. |
---|
1925 | 2076 | } |
---|
1926 | 2077 | EXPORT_SYMBOL(rockchip_init_opp_table); |
---|
1927 | 2078 | |
---|
| 2079 | +void rockchip_uninit_opp_table(struct device *dev, struct rockchip_opp_info *info) |
---|
| 2080 | +{ |
---|
| 2081 | + struct opp_table *opp_table; |
---|
| 2082 | + |
---|
| 2083 | + if (info) { |
---|
| 2084 | + kfree(info->opp_table); |
---|
| 2085 | + info->opp_table = NULL; |
---|
| 2086 | + devm_kfree(dev, info->clks); |
---|
| 2087 | + info->clks = NULL; |
---|
| 2088 | + devm_kfree(dev, info->volt_rm_tbl); |
---|
| 2089 | + info->volt_rm_tbl = NULL; |
---|
| 2090 | + } |
---|
| 2091 | + |
---|
| 2092 | + opp_table = dev_pm_opp_get_opp_table(dev); |
---|
| 2093 | + if (IS_ERR(opp_table)) |
---|
| 2094 | + return; |
---|
| 2095 | + dev_pm_opp_of_remove_table(dev); |
---|
| 2096 | + if (opp_table->prop_name) |
---|
| 2097 | + dev_pm_opp_put_prop_name(opp_table); |
---|
| 2098 | + if (opp_table->supported_hw) |
---|
| 2099 | + dev_pm_opp_put_supported_hw(opp_table); |
---|
| 2100 | + dev_pm_opp_put_opp_table(opp_table); |
---|
| 2101 | +} |
---|
| 2102 | +EXPORT_SYMBOL(rockchip_uninit_opp_table); |
---|
| 2103 | + |
---|
1928 | 2104 | MODULE_DESCRIPTION("ROCKCHIP OPP Select"); |
---|
1929 | 2105 | MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>, Liang Chen <cl@rock-chips.com>"); |
---|
1930 | 2106 | MODULE_LICENSE("GPL"); |
---|