Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 1 | /* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. |
| 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | * |
| 12 | */ |
| 13 | |
| 14 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
| 15 | |
| 16 | #include <linux/kernel.h> |
| 17 | #include <linux/err.h> |
| 18 | #include <linux/string.h> |
| 19 | #include <linux/clk/msm-clock-generic.h> |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 20 | #include <linux/vmalloc.h> |
| 21 | #include <linux/memblock.h> |
| 22 | |
| 23 | #include "mdss-pll.h" |
| 24 | |
| 25 | int mdss_pll_util_resource_init(struct platform_device *pdev, |
| 26 | struct mdss_pll_resources *pll_res) |
| 27 | { |
| 28 | int rc = 0; |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 29 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 30 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 31 | rc = msm_mdss_config_vreg(&pdev->dev, |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 32 | mp->vreg_config, mp->num_vreg, 1); |
| 33 | if (rc) { |
| 34 | pr_err("Vreg config failed rc=%d\n", rc); |
| 35 | goto vreg_err; |
| 36 | } |
| 37 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 38 | rc = msm_mdss_get_clk(&pdev->dev, mp->clk_config, mp->num_clk); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 39 | if (rc) { |
| 40 | pr_err("Clock get failed rc=%d\n", rc); |
| 41 | goto clk_err; |
| 42 | } |
| 43 | |
| 44 | return rc; |
| 45 | |
| 46 | clk_err: |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 47 | msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 48 | vreg_err: |
| 49 | return rc; |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name |
| 54 | *@pll_res: Pointer to the PLL resource |
| 55 | *@name: Regulator name as specified in the pll dtsi |
| 56 | * |
| 57 | * This is a helper function to retrieve the regulator information |
| 58 | * for each pll resource. |
| 59 | */ |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 60 | struct mdss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 61 | , char *name) |
| 62 | { |
| 63 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 64 | struct mdss_vreg *regulator = NULL; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 65 | int i; |
| 66 | |
| 67 | if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) { |
| 68 | pr_err("%s Invalid PLL resource\n", __func__); |
| 69 | goto error; |
| 70 | } |
| 71 | |
| 72 | regulator = pll_res->mp.vreg_config; |
| 73 | |
| 74 | for (i = 0; i < pll_res->mp.num_vreg; i++) { |
| 75 | if (!strcmp(name, regulator->vreg_name)) { |
| 76 | pr_debug("Found regulator match for %s\n", name); |
| 77 | break; |
| 78 | } |
| 79 | regulator++; |
| 80 | } |
| 81 | |
| 82 | error: |
| 83 | return regulator; |
| 84 | } |
| 85 | |
| 86 | void mdss_pll_util_resource_deinit(struct platform_device *pdev, |
| 87 | struct mdss_pll_resources *pll_res) |
| 88 | { |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 89 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 90 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 91 | msm_mdss_put_clk(mp->clk_config, mp->num_clk); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 92 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 93 | msm_mdss_config_vreg(&pdev->dev, mp->vreg_config, mp->num_vreg, 0); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | void mdss_pll_util_resource_release(struct platform_device *pdev, |
| 97 | struct mdss_pll_resources *pll_res) |
| 98 | { |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 99 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 100 | |
| 101 | devm_kfree(&pdev->dev, mp->clk_config); |
| 102 | devm_kfree(&pdev->dev, mp->vreg_config); |
| 103 | mp->num_vreg = 0; |
| 104 | mp->num_clk = 0; |
| 105 | } |
| 106 | |
| 107 | int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res, |
| 108 | bool enable) |
| 109 | { |
| 110 | int rc = 0; |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 111 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 112 | |
| 113 | if (enable) { |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 114 | rc = msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, |
| 115 | enable); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 116 | if (rc) { |
| 117 | pr_err("Failed to enable vregs rc=%d\n", rc); |
| 118 | goto vreg_err; |
| 119 | } |
| 120 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 121 | rc = msm_mdss_clk_set_rate(mp->clk_config, mp->num_clk); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 122 | if (rc) { |
| 123 | pr_err("Failed to set clock rate rc=%d\n", rc); |
| 124 | goto clk_err; |
| 125 | } |
| 126 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 127 | rc = msm_mdss_enable_clk(mp->clk_config, mp->num_clk, enable); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 128 | if (rc) { |
| 129 | pr_err("clock enable failed rc:%d\n", rc); |
| 130 | goto clk_err; |
| 131 | } |
| 132 | } else { |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 133 | msm_mdss_enable_clk(mp->clk_config, mp->num_clk, enable); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 134 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 135 | msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, enable); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | return rc; |
| 139 | |
| 140 | clk_err: |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 141 | msm_mdss_enable_vreg(mp->vreg_config, mp->num_vreg, 0); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 142 | vreg_err: |
| 143 | return rc; |
| 144 | } |
| 145 | |
| 146 | static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev, |
| 147 | struct mdss_pll_resources *pll_res) |
| 148 | { |
| 149 | int i = 0, rc = 0; |
| 150 | u32 tmp = 0; |
| 151 | struct device_node *of_node = NULL, *supply_root_node = NULL; |
| 152 | struct device_node *supply_node = NULL; |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 153 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 154 | |
| 155 | of_node = pdev->dev.of_node; |
| 156 | |
| 157 | mp->num_vreg = 0; |
| 158 | supply_root_node = of_get_child_by_name(of_node, |
| 159 | "qcom,platform-supply-entries"); |
| 160 | if (!supply_root_node) { |
| 161 | pr_err("no supply entry present\n"); |
| 162 | return rc; |
| 163 | } |
| 164 | |
| 165 | for_each_child_of_node(supply_root_node, supply_node) { |
| 166 | mp->num_vreg++; |
| 167 | } |
| 168 | |
| 169 | if (mp->num_vreg == 0) { |
| 170 | pr_debug("no vreg\n"); |
| 171 | return rc; |
| 172 | } |
| 173 | pr_debug("vreg found. count=%d\n", mp->num_vreg); |
| 174 | |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 175 | mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct mdss_vreg) * |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 176 | mp->num_vreg, GFP_KERNEL); |
| 177 | if (!mp->vreg_config) { |
| 178 | rc = -ENOMEM; |
| 179 | return rc; |
| 180 | } |
| 181 | |
| 182 | for_each_child_of_node(supply_root_node, supply_node) { |
| 183 | |
| 184 | const char *st = NULL; |
| 185 | |
| 186 | rc = of_property_read_string(supply_node, |
| 187 | "qcom,supply-name", &st); |
| 188 | if (rc) { |
| 189 | pr_err(":error reading name. rc=%d\n", rc); |
| 190 | goto error; |
| 191 | } |
| 192 | |
| 193 | strlcpy(mp->vreg_config[i].vreg_name, st, |
| 194 | sizeof(mp->vreg_config[i].vreg_name)); |
| 195 | |
| 196 | rc = of_property_read_u32(supply_node, |
| 197 | "qcom,supply-min-voltage", &tmp); |
| 198 | if (rc) { |
| 199 | pr_err(": error reading min volt. rc=%d\n", rc); |
| 200 | goto error; |
| 201 | } |
| 202 | mp->vreg_config[i].min_voltage = tmp; |
| 203 | |
| 204 | rc = of_property_read_u32(supply_node, |
| 205 | "qcom,supply-max-voltage", &tmp); |
| 206 | if (rc) { |
| 207 | pr_err(": error reading max volt. rc=%d\n", rc); |
| 208 | goto error; |
| 209 | } |
| 210 | mp->vreg_config[i].max_voltage = tmp; |
| 211 | |
| 212 | rc = of_property_read_u32(supply_node, |
| 213 | "qcom,supply-enable-load", &tmp); |
| 214 | if (rc) { |
| 215 | pr_err(": error reading enable load. rc=%d\n", rc); |
| 216 | goto error; |
| 217 | } |
| 218 | mp->vreg_config[i].load[DSS_REG_MODE_ENABLE] = tmp; |
| 219 | |
| 220 | rc = of_property_read_u32(supply_node, |
| 221 | "qcom,supply-disable-load", &tmp); |
| 222 | if (rc) { |
| 223 | pr_err(": error reading disable load. rc=%d\n", rc); |
| 224 | goto error; |
| 225 | } |
| 226 | mp->vreg_config[i].load[DSS_REG_MODE_DISABLE] = tmp; |
| 227 | |
| 228 | rc = of_property_read_u32(supply_node, |
| 229 | "qcom,supply-ulp-load", &tmp); |
| 230 | if (rc) |
| 231 | pr_warn(": error reading ulp load. rc=%d\n", rc); |
| 232 | |
| 233 | mp->vreg_config[i].load[DSS_REG_MODE_ULP] = (!rc ? tmp : |
| 234 | mp->vreg_config[i].load[DSS_REG_MODE_ENABLE]); |
| 235 | |
| 236 | rc = of_property_read_u32(supply_node, |
| 237 | "qcom,supply-pre-on-sleep", &tmp); |
| 238 | if (rc) |
| 239 | pr_debug("error reading supply pre sleep value. rc=%d\n", |
| 240 | rc); |
| 241 | |
| 242 | mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0); |
| 243 | |
| 244 | rc = of_property_read_u32(supply_node, |
| 245 | "qcom,supply-pre-off-sleep", &tmp); |
| 246 | if (rc) |
| 247 | pr_debug("error reading supply pre sleep value. rc=%d\n", |
| 248 | rc); |
| 249 | |
| 250 | mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0); |
| 251 | |
| 252 | rc = of_property_read_u32(supply_node, |
| 253 | "qcom,supply-post-on-sleep", &tmp); |
| 254 | if (rc) |
| 255 | pr_debug("error reading supply post sleep value. rc=%d\n", |
| 256 | rc); |
| 257 | |
| 258 | mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0); |
| 259 | |
| 260 | rc = of_property_read_u32(supply_node, |
| 261 | "qcom,supply-post-off-sleep", &tmp); |
| 262 | if (rc) |
| 263 | pr_debug("error reading supply post sleep value. rc=%d\n", |
| 264 | rc); |
| 265 | |
| 266 | mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0); |
| 267 | |
| 268 | pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, ulp=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", |
| 269 | mp->vreg_config[i].vreg_name, |
| 270 | mp->vreg_config[i].min_voltage, |
| 271 | mp->vreg_config[i].max_voltage, |
| 272 | mp->vreg_config[i].load[DSS_REG_MODE_ENABLE], |
| 273 | mp->vreg_config[i].load[DSS_REG_MODE_DISABLE], |
| 274 | mp->vreg_config[i].load[DSS_REG_MODE_ULP], |
| 275 | mp->vreg_config[i].pre_on_sleep, |
| 276 | mp->vreg_config[i].post_on_sleep, |
| 277 | mp->vreg_config[i].pre_off_sleep, |
| 278 | mp->vreg_config[i].post_off_sleep); |
| 279 | ++i; |
| 280 | |
| 281 | rc = 0; |
| 282 | } |
| 283 | |
| 284 | return rc; |
| 285 | |
| 286 | error: |
| 287 | if (mp->vreg_config) { |
| 288 | devm_kfree(&pdev->dev, mp->vreg_config); |
| 289 | mp->vreg_config = NULL; |
| 290 | mp->num_vreg = 0; |
| 291 | } |
| 292 | |
| 293 | return rc; |
| 294 | } |
| 295 | |
| 296 | static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev, |
| 297 | struct mdss_pll_resources *pll_res) |
| 298 | { |
| 299 | u32 i = 0, rc = 0; |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 300 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 301 | const char *clock_name; |
| 302 | u32 clock_rate; |
| 303 | |
| 304 | mp->num_clk = of_property_count_strings(pdev->dev.of_node, |
| 305 | "clock-names"); |
| 306 | if (mp->num_clk <= 0) { |
| 307 | pr_err("clocks are not defined\n"); |
| 308 | goto clk_err; |
| 309 | } |
| 310 | |
| 311 | mp->clk_config = devm_kzalloc(&pdev->dev, |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 312 | sizeof(struct mdss_clk) * mp->num_clk, GFP_KERNEL); |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 313 | if (!mp->clk_config) { |
| 314 | rc = -ENOMEM; |
| 315 | mp->num_clk = 0; |
| 316 | goto clk_err; |
| 317 | } |
| 318 | |
| 319 | for (i = 0; i < mp->num_clk; i++) { |
| 320 | of_property_read_string_index(pdev->dev.of_node, "clock-names", |
| 321 | i, &clock_name); |
| 322 | strlcpy(mp->clk_config[i].clk_name, clock_name, |
| 323 | sizeof(mp->clk_config[i].clk_name)); |
| 324 | |
| 325 | of_property_read_u32_index(pdev->dev.of_node, "clock-rate", |
| 326 | i, &clock_rate); |
| 327 | mp->clk_config[i].rate = clock_rate; |
| 328 | |
| 329 | if (!clock_rate) |
| 330 | mp->clk_config[i].type = DSS_CLK_AHB; |
| 331 | else |
| 332 | mp->clk_config[i].type = DSS_CLK_PCLK; |
| 333 | } |
| 334 | |
| 335 | clk_err: |
| 336 | return rc; |
| 337 | } |
| 338 | |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 339 | int mdss_pll_util_resource_parse(struct platform_device *pdev, |
| 340 | struct mdss_pll_resources *pll_res) |
| 341 | { |
| 342 | int rc = 0; |
Sachin Bhayare | 5076e25 | 2018-01-18 14:56:45 +0530 | [diff] [blame] | 343 | struct mdss_module_power *mp = &pll_res->mp; |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 344 | |
| 345 | rc = mdss_pll_util_parse_dt_supply(pdev, pll_res); |
| 346 | if (rc) { |
| 347 | pr_err("vreg parsing failed rc=%d\n", rc); |
| 348 | goto end; |
| 349 | } |
| 350 | |
| 351 | rc = mdss_pll_util_parse_dt_clock(pdev, pll_res); |
| 352 | if (rc) { |
| 353 | pr_err("clock name parsing failed rc=%d", rc); |
| 354 | goto clk_err; |
| 355 | } |
| 356 | |
Sachin Bhayare | cf8460a | 2018-01-03 18:34:30 +0530 | [diff] [blame] | 357 | return rc; |
| 358 | |
| 359 | clk_err: |
| 360 | devm_kfree(&pdev->dev, mp->vreg_config); |
| 361 | mp->num_vreg = 0; |
| 362 | end: |
| 363 | return rc; |
| 364 | } |