static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) { if (!IS_ERR(info->proc_reg)) regulator_put(info->proc_reg); if (!IS_ERR(info->sram_reg)) regulator_put(info->sram_reg); if (!IS_ERR(info->cpu_clk)) clk_put(info->cpu_clk); if (!IS_ERR(info->inter_clk)) clk_put(info->inter_clk); dev_pm_opp_of_remove_table(info->cpu_dev); }
void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) { struct device *cpu_dev; int cpu; WARN_ON(cpumask_empty(cpumask)); for_each_cpu(cpu, cpumask) { cpu_dev = get_cpu_device(cpu); if (!cpu_dev) { pr_err("%s: failed to get cpu%d device\n", __func__, cpu); continue; } dev_pm_opp_of_remove_table(cpu_dev); }
static int imx6q_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&imx6q_cpufreq_driver); dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); if (free_opp) dev_pm_opp_of_remove_table(cpu_dev); regulator_put(arm_reg); if (!IS_ERR(pu_reg)) regulator_put(pu_reg); regulator_put(soc_reg); clk_put(arm_clk); clk_put(pll1_sys_clk); clk_put(pll1_sw_clk); clk_put(step_clk); clk_put(pll2_pfd2_396m_clk); return 0; }
static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) { struct device *cpu_dev; struct regulator *proc_reg = ERR_PTR(-ENODEV); struct regulator *sram_reg = ERR_PTR(-ENODEV); struct clk *cpu_clk = ERR_PTR(-ENODEV); struct clk *inter_clk = ERR_PTR(-ENODEV); struct dev_pm_opp *opp; unsigned long rate; int ret; cpu_dev = get_cpu_device(cpu); if (!cpu_dev) { pr_err("failed to get cpu%d device\n", cpu); return -ENODEV; } cpu_clk = clk_get(cpu_dev, "cpu"); if (IS_ERR(cpu_clk)) { if (PTR_ERR(cpu_clk) == -EPROBE_DEFER) pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu); else pr_err("failed to get cpu clk for cpu%d\n", cpu); ret = PTR_ERR(cpu_clk); return ret; } inter_clk = clk_get(cpu_dev, "intermediate"); if (IS_ERR(inter_clk)) { if (PTR_ERR(inter_clk) == -EPROBE_DEFER) pr_warn("intermediate clk for cpu%d not ready, retry.\n", cpu); else pr_err("failed to get intermediate clk for cpu%d\n", cpu); ret = PTR_ERR(inter_clk); goto out_free_resources; } proc_reg = regulator_get_exclusive(cpu_dev, "proc"); if (IS_ERR(proc_reg)) { if (PTR_ERR(proc_reg) == -EPROBE_DEFER) pr_warn("proc regulator for cpu%d not ready, retry.\n", cpu); else pr_err("failed to get proc regulator for cpu%d\n", cpu); ret = PTR_ERR(proc_reg); goto out_free_resources; } /* Both presence and absence of sram regulator are valid cases. */ sram_reg = regulator_get_exclusive(cpu_dev, "sram"); ret = dev_pm_opp_of_add_table(cpu_dev); if (ret) { pr_warn("no OPP table for cpu%d\n", cpu); goto out_free_resources; } /* Search a safe voltage for intermediate frequency. */ rate = clk_get_rate(inter_clk); rcu_read_lock(); opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); if (IS_ERR(opp)) { rcu_read_unlock(); pr_err("failed to get intermediate opp for cpu%d\n", cpu); ret = PTR_ERR(opp); goto out_free_opp_table; } info->intermediate_voltage = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); info->cpu_dev = cpu_dev; info->proc_reg = proc_reg; info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg; info->cpu_clk = cpu_clk; info->inter_clk = inter_clk; /* * If SRAM regulator is present, software "voltage tracking" is needed * for this CPU power domain. */ info->need_voltage_tracking = !IS_ERR(sram_reg); return 0; out_free_opp_table: dev_pm_opp_of_remove_table(cpu_dev); out_free_resources: if (!IS_ERR(proc_reg)) regulator_put(proc_reg); if (!IS_ERR(sram_reg)) regulator_put(sram_reg); if (!IS_ERR(cpu_clk)) clk_put(cpu_clk); if (!IS_ERR(inter_clk)) clk_put(inter_clk); return ret; }
static int imx6q_cpufreq_probe(struct platform_device *pdev) { struct device_node *np; struct dev_pm_opp *opp; unsigned long min_volt, max_volt; int num, ret; const struct property *prop; const __be32 *val; u32 nr, i, j; cpu_dev = get_cpu_device(0); if (!cpu_dev) { pr_err("failed to get cpu0 device\n"); return -ENODEV; } np = of_node_get(cpu_dev->of_node); if (!np) { dev_err(cpu_dev, "failed to find cpu0 node\n"); return -ENOENT; } arm_clk = clk_get(cpu_dev, "arm"); pll1_sys_clk = clk_get(cpu_dev, "pll1_sys"); pll1_sw_clk = clk_get(cpu_dev, "pll1_sw"); step_clk = clk_get(cpu_dev, "step"); pll2_pfd2_396m_clk = clk_get(cpu_dev, "pll2_pfd2_396m"); if (IS_ERR(arm_clk) || IS_ERR(pll1_sys_clk) || IS_ERR(pll1_sw_clk) || IS_ERR(step_clk) || IS_ERR(pll2_pfd2_396m_clk)) { dev_err(cpu_dev, "failed to get clocks\n"); ret = -ENOENT; goto put_clk; } arm_reg = regulator_get(cpu_dev, "arm"); pu_reg = regulator_get_optional(cpu_dev, "pu"); soc_reg = regulator_get(cpu_dev, "soc"); if (IS_ERR(arm_reg) || IS_ERR(soc_reg)) { dev_err(cpu_dev, "failed to get regulators\n"); ret = -ENOENT; goto put_reg; } /* * We expect an OPP table supplied by platform. * Just, incase the platform did not supply the OPP * table, it will try to get it. */ num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { ret = dev_pm_opp_of_add_table(cpu_dev); if (ret < 0) { dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); goto put_reg; } /* Because we have added the OPPs here, we must free them */ free_opp = true; num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { ret = num; dev_err(cpu_dev, "no OPP table is found: %d\n", ret); goto out_free_opp; } } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto put_reg; } /* Make imx6_soc_volt array's size same as arm opp number */ imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL); if (imx6_soc_volt == NULL) { ret = -ENOMEM; goto free_freq_table; } prop = of_find_property(np, "fsl,soc-operating-points", NULL); if (!prop || !prop->value) goto soc_opp_out; /* * Each OPP is a set of tuples consisting of frequency and * voltage like <freq-kHz vol-uV>. */ nr = prop->length / sizeof(u32); if (nr % 2 || (nr / 2) < num) goto soc_opp_out; for (j = 0; j < num; j++) { val = prop->value; for (i = 0; i < nr / 2; i++) { unsigned long freq = be32_to_cpup(val++); unsigned long volt = be32_to_cpup(val++); if (freq_table[j].frequency == freq) { imx6_soc_volt[soc_opp_count++] = volt; break; } } } soc_opp_out: /* use fixed soc opp volt if no valid soc opp info found in dtb */ if (soc_opp_count != num) { dev_warn(cpu_dev, "can NOT find valid fsl,soc-operating-points property in dtb, use default value!\n"); for (j = 0; j < num; j++) imx6_soc_volt[j] = PU_SOC_VOLTAGE_NORMAL; if (freq_table[num - 1].frequency * 1000 == FREQ_1P2_GHZ) imx6_soc_volt[num - 1] = PU_SOC_VOLTAGE_HIGH; } if (of_property_read_u32(np, "clock-latency", &transition_latency)) transition_latency = CPUFREQ_ETERNAL; /* * Calculate the ramp time for max voltage change in the * VDDSOC and VDDPU regulators. */ ret = regulator_set_voltage_time(soc_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); if (ret > 0) transition_latency += ret * 1000; if (!IS_ERR(pu_reg)) { ret = regulator_set_voltage_time(pu_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]); if (ret > 0) transition_latency += ret * 1000; } /* * OPP is maintained in order of increasing frequency, and * freq_table initialised from OPP is therefore sorted in the * same order. */ rcu_read_lock(); opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[0].frequency * 1000, true); min_volt = dev_pm_opp_get_voltage(opp); opp = dev_pm_opp_find_freq_exact(cpu_dev, freq_table[--num].frequency * 1000, true); max_volt = dev_pm_opp_get_voltage(opp); rcu_read_unlock(); ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt); if (ret > 0) transition_latency += ret * 1000; ret = cpufreq_register_driver(&imx6q_cpufreq_driver); if (ret) { dev_err(cpu_dev, "failed register driver: %d\n", ret); goto free_freq_table; } of_node_put(np); return 0; free_freq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_opp: if (free_opp) dev_pm_opp_of_remove_table(cpu_dev); put_reg: if (!IS_ERR(arm_reg)) regulator_put(arm_reg); if (!IS_ERR(pu_reg)) regulator_put(pu_reg); if (!IS_ERR(soc_reg)) regulator_put(soc_reg); put_clk: if (!IS_ERR(arm_clk)) clk_put(arm_clk); if (!IS_ERR(pll1_sys_clk)) clk_put(pll1_sys_clk); if (!IS_ERR(pll1_sw_clk)) clk_put(pll1_sw_clk); if (!IS_ERR(step_clk)) clk_put(step_clk); if (!IS_ERR(pll2_pfd2_396m_clk)) clk_put(pll2_pfd2_396m_clk); of_node_put(np); return ret; }