static int a6xx_gmu_build_freq_table(struct device *dev, unsigned long *freqs, u32 size) { int count = dev_pm_opp_get_opp_count(dev); struct dev_pm_opp *opp; int i, index = 0; unsigned long freq = 1; /* * The OPP table doesn't contain the "off" frequency level so we need to * add 1 to the table size to account for it */ if (WARN(count + 1 > size, "The GMU frequency table is being truncated\n")) count = size - 1; /* Set the "off" frequency */ freqs[index++] = 0; for (i = 0; i < count; i++) { opp = dev_pm_opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) break; dev_pm_opp_put(opp); freqs[index++] = freq++; } return index; }
/** * partition_enable_opps() - disable all opps above a given state * @dfc: Pointer to devfreq we are operating on * @cdev_state: cooling device state we're setting * * Go through the OPPs of the device, enabling all OPPs until * @cdev_state and disabling those frequencies above it. */ static int partition_enable_opps(struct devfreq_cooling_device *dfc, unsigned long cdev_state) { int i; struct device *dev = dfc->devfreq->dev.parent; for (i = 0; i < dfc->freq_table_size; i++) { struct dev_pm_opp *opp; int ret = 0; unsigned int freq = dfc->freq_table[i]; bool want_enable = i >= cdev_state ? true : false; opp = dev_pm_opp_find_freq_exact(dev, freq, !want_enable); if (PTR_ERR(opp) == -ERANGE) continue; else if (IS_ERR(opp)) return PTR_ERR(opp); dev_pm_opp_put(opp); if (want_enable) ret = dev_pm_opp_enable(dev, freq); else ret = dev_pm_opp_disable(dev, freq); if (ret) return ret; } return 0; }
static unsigned long get_voltage(struct devfreq *df, unsigned long freq) { struct device *dev = df->dev.parent; unsigned long voltage; struct dev_pm_opp *opp; opp = dev_pm_opp_find_freq_exact(dev, freq, true); if (PTR_ERR(opp) == -ERANGE) opp = dev_pm_opp_find_freq_exact(dev, freq, false); if (IS_ERR(opp)) { dev_err_ratelimited(dev, "Failed to find OPP for frequency %lu: %ld\n", freq, PTR_ERR(opp)); return 0; } voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */ dev_pm_opp_put(opp); if (voltage == 0) { dev_err_ratelimited(dev, "Failed to get voltage for frequency %lu\n", freq); } return voltage; }
int panfrost_devfreq_init(struct panfrost_device *pfdev) { int ret; struct dev_pm_opp *opp; if (!pfdev->regulator) return 0; ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev); if (ret) return ret; panfrost_devfreq_reset(pfdev); pfdev->devfreq.cur_freq = clk_get_rate(pfdev->clock); opp = devfreq_recommended_opp(&pfdev->pdev->dev, &pfdev->devfreq.cur_freq, 0); if (IS_ERR(opp)) return PTR_ERR(opp); panfrost_devfreq_profile.initial_freq = pfdev->devfreq.cur_freq; dev_pm_opp_put(opp); pfdev->devfreq.devfreq = devm_devfreq_add_device(&pfdev->pdev->dev, &panfrost_devfreq_profile, "simple_ondemand", NULL); if (IS_ERR(pfdev->devfreq.devfreq)) { DRM_DEV_ERROR(&pfdev->pdev->dev, "Couldn't initialize GPU devfreq\n"); ret = PTR_ERR(pfdev->devfreq.devfreq); pfdev->devfreq.devfreq = NULL; return ret; } return 0; }
static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); struct dev_pm_opp *opp; unsigned long old_clk_rate = pfdev->devfreq.cur_freq; unsigned long target_volt, target_rate; int err; opp = devfreq_recommended_opp(dev, freq, flags); if (IS_ERR(opp)) return PTR_ERR(opp); target_rate = dev_pm_opp_get_freq(opp); target_volt = dev_pm_opp_get_voltage(opp); dev_pm_opp_put(opp); if (old_clk_rate == target_rate) return 0; /* * If frequency scaling from low to high, adjust voltage first. * If frequency scaling from high to low, adjust frequency first. */ if (old_clk_rate < target_rate) { err = regulator_set_voltage(pfdev->regulator, target_volt, target_volt); if (err) { dev_err(dev, "Cannot set voltage %lu uV\n", target_volt); return err; } } err = clk_set_rate(pfdev->clock, target_rate); if (err) { dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, err); regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt, pfdev->devfreq.cur_volt); return err; } if (old_clk_rate > target_rate) { err = regulator_set_voltage(pfdev->regulator, target_volt, target_volt); if (err) dev_err(dev, "Cannot set voltage %lu uV\n", target_volt); } pfdev->devfreq.cur_freq = target_rate; pfdev->devfreq.cur_volt = target_volt; return 0; }
/* Return the 'arc-level' for the given frequency */ static unsigned int a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) { struct dev_pm_opp *opp; unsigned int val; if (!freq) return 0; opp = dev_pm_opp_find_freq_exact(dev, freq, true); if (IS_ERR(opp)) return 0; val = dev_pm_opp_get_level(opp); dev_pm_opp_put(opp); return val; }
/* Return the 'arc-level' for the given frequency */ static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) { struct dev_pm_opp *opp; struct device_node *np; u32 val = 0; if (!freq) return 0; opp = dev_pm_opp_find_freq_exact(dev, freq, true); if (IS_ERR(opp)) return 0; np = dev_pm_opp_get_of_node(opp); if (np) { of_property_read_u32(np, "qcom,level", &val); of_node_put(np); } dev_pm_opp_put(opp); return val; }
static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) { struct dev_pm_opp *opp; unsigned long freq_hz, volt, volt_old; unsigned int old_freq, new_freq; bool pll1_sys_temp_enabled = false; int ret; new_freq = freq_table[index].frequency; freq_hz = new_freq * 1000; old_freq = clk_get_rate(arm_clk) / 1000; opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz); if (IS_ERR(opp)) { dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz); return PTR_ERR(opp); } volt = dev_pm_opp_get_voltage(opp); dev_pm_opp_put(opp); volt_old = regulator_get_voltage(arm_reg); dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", old_freq / 1000, volt_old / 1000, new_freq / 1000, volt / 1000); /* scaling up? scale voltage before frequency */ if (new_freq > old_freq) { if (!IS_ERR(pu_reg)) { ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0); if (ret) { dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret); return ret; } } ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); if (ret) { dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret); return ret; } ret = regulator_set_voltage_tol(arm_reg, volt, 0); if (ret) { dev_err(cpu_dev, "failed to scale vddarm up: %d\n", ret); return ret; } } /* * The setpoints are selected per PLL/PDF frequencies, so we need to * reprogram PLL for frequency scaling. The procedure of reprogramming * PLL1 is as below. * For i.MX6UL, it has a secondary clk mux, the cpu frequency change * flow is slightly different from other i.MX6 OSC. * The cpu frequeny change flow for i.MX6(except i.MX6UL) is as below: * - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it * - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it * - Disable pll2_pfd2_396m_clk */ if (of_machine_is_compatible("fsl,imx6ul") || of_machine_is_compatible("fsl,imx6ull")) { /* * When changing pll1_sw_clk's parent to pll1_sys_clk, * CPU may run at higher than 528MHz, this will lead to * the system unstable if the voltage is lower than the * voltage of 528MHz, so lower the CPU frequency to one * half before changing CPU frequency. */ clk_set_rate(arm_clk, (old_freq >> 1) * 1000); clk_set_parent(pll1_sw_clk, pll1_sys_clk); if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) clk_set_parent(secondary_sel_clk, pll2_bus_clk); else clk_set_parent(secondary_sel_clk, pll2_pfd2_396m_clk); clk_set_parent(step_clk, secondary_sel_clk); clk_set_parent(pll1_sw_clk, step_clk); } else {
/** * devfreq_cooling_gen_tables() - Generate power and freq tables. * @dfc: Pointer to devfreq cooling device. * * Generate power and frequency tables: the power table hold the * device's maximum power usage at each cooling state (OPP). The * static and dynamic power using the appropriate voltage and * frequency for the state, is acquired from the struct * devfreq_cooling_power, and summed to make the maximum power draw. * * The frequency table holds the frequencies in descending order. * That way its indexed by cooling device state. * * The tables are malloced, and pointers put in dfc. They must be * freed when unregistering the devfreq cooling device. * * Return: 0 on success, negative error code on failure. */ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc) { struct devfreq *df = dfc->devfreq; struct device *dev = df->dev.parent; int ret, num_opps; unsigned long freq; u32 *power_table = NULL; u32 *freq_table; int i; num_opps = dev_pm_opp_get_opp_count(dev); if (dfc->power_ops) { power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); if (!power_table) return -ENOMEM; } freq_table = kcalloc(num_opps, sizeof(*freq_table), GFP_KERNEL); if (!freq_table) { ret = -ENOMEM; goto free_power_table; } for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { unsigned long power, voltage; struct dev_pm_opp *opp; opp = dev_pm_opp_find_freq_floor(dev, &freq); if (IS_ERR(opp)) { ret = PTR_ERR(opp); goto free_tables; } voltage = dev_pm_opp_get_voltage(opp) / 1000; /* mV */ dev_pm_opp_put(opp); if (dfc->power_ops) { if (dfc->power_ops->get_real_power) power = get_total_power(dfc, freq, voltage); else power = get_dynamic_power(dfc, freq, voltage); dev_dbg(dev, "Power table: %lu MHz @ %lu mV: %lu = %lu mW\n", freq / 1000000, voltage, power, power); power_table[i] = power; } freq_table[i] = freq; } if (dfc->power_ops) dfc->power_table = power_table; dfc->freq_table = freq_table; dfc->freq_table_size = num_opps; return 0; free_tables: kfree(freq_table); free_power_table: kfree(power_table); return ret; }
static int omap_target(struct cpufreq_policy *policy, unsigned int index) { int r, ret; struct dev_pm_opp *opp; unsigned long freq, volt = 0, volt_old = 0, tol = 0; unsigned int old_freq, new_freq; old_freq = policy->cur; new_freq = freq_table[index].frequency; freq = new_freq * 1000; ret = clk_round_rate(policy->clk, freq); if (ret < 0) { dev_warn(mpu_dev, "CPUfreq: Cannot find matching frequency for %lu\n", freq); return ret; } freq = ret; if (mpu_reg) { opp = dev_pm_opp_find_freq_ceil(mpu_dev, &freq); if (IS_ERR(opp)) { dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n", __func__, new_freq); return -EINVAL; } volt = dev_pm_opp_get_voltage(opp); dev_pm_opp_put(opp); tol = volt * OPP_TOLERANCE / 100; volt_old = regulator_get_voltage(mpu_reg); } dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n", old_freq / 1000, volt_old ? volt_old / 1000 : -1, new_freq / 1000, volt ? volt / 1000 : -1); /* scaling up? scale voltage before frequency */ if (mpu_reg && (new_freq > old_freq)) { r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); if (r < 0) { dev_warn(mpu_dev, "%s: unable to scale voltage up.\n", __func__); return r; } } ret = clk_set_rate(policy->clk, new_freq * 1000); /* scaling down? scale voltage after frequency */ if (mpu_reg && (new_freq < old_freq)) { r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); if (r < 0) { dev_warn(mpu_dev, "%s: unable to scale voltage down.\n", __func__); clk_set_rate(policy->clk, old_freq * 1000); return r; } } return ret; }