162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/clk-provider.h> 862306a36Sopenharmony_ci#include <linux/cpufreq.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/interconnect.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_opp.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <linux/units.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define LUT_MAX_ENTRIES 40U 2262306a36Sopenharmony_ci#define LUT_SRC GENMASK(31, 30) 2362306a36Sopenharmony_ci#define LUT_L_VAL GENMASK(7, 0) 2462306a36Sopenharmony_ci#define LUT_CORE_COUNT GENMASK(18, 16) 2562306a36Sopenharmony_ci#define LUT_VOLT GENMASK(11, 0) 2662306a36Sopenharmony_ci#define CLK_HW_DIV 2 2762306a36Sopenharmony_ci#define LUT_TURBO_IND 1 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define GT_IRQ_STATUS BIT(2) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MAX_FREQ_DOMAINS 4 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct qcom_cpufreq_soc_data { 3462306a36Sopenharmony_ci u32 reg_enable; 3562306a36Sopenharmony_ci u32 reg_domain_state; 3662306a36Sopenharmony_ci u32 reg_dcvs_ctrl; 3762306a36Sopenharmony_ci u32 reg_freq_lut; 3862306a36Sopenharmony_ci u32 reg_volt_lut; 3962306a36Sopenharmony_ci u32 reg_intr_clr; 4062306a36Sopenharmony_ci u32 reg_current_vote; 4162306a36Sopenharmony_ci u32 reg_perf_state; 4262306a36Sopenharmony_ci u8 lut_row_size; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct qcom_cpufreq_data { 4662306a36Sopenharmony_ci void __iomem *base; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * Mutex to synchronize between de-init sequence and re-starting LMh 5062306a36Sopenharmony_ci * polling/interrupts 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci struct mutex throttle_lock; 5362306a36Sopenharmony_ci int throttle_irq; 5462306a36Sopenharmony_ci char irq_name[15]; 5562306a36Sopenharmony_ci bool cancel_throttle; 5662306a36Sopenharmony_ci struct delayed_work throttle_work; 5762306a36Sopenharmony_ci struct cpufreq_policy *policy; 5862306a36Sopenharmony_ci struct clk_hw cpu_clk; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci bool per_core_dcvs; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct { 6462306a36Sopenharmony_ci struct qcom_cpufreq_data *data; 6562306a36Sopenharmony_ci const struct qcom_cpufreq_soc_data *soc_data; 6662306a36Sopenharmony_ci} qcom_cpufreq; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic unsigned long cpu_hw_rate, xo_rate; 6962306a36Sopenharmony_cistatic bool icc_scaling_enabled; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, 7262306a36Sopenharmony_ci unsigned long freq_khz) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci unsigned long freq_hz = freq_khz * 1000; 7562306a36Sopenharmony_ci struct dev_pm_opp *opp; 7662306a36Sopenharmony_ci struct device *dev; 7762306a36Sopenharmony_ci int ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci dev = get_cpu_device(policy->cpu); 8062306a36Sopenharmony_ci if (!dev) 8162306a36Sopenharmony_ci return -ENODEV; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_exact(dev, freq_hz, true); 8462306a36Sopenharmony_ci if (IS_ERR(opp)) 8562306a36Sopenharmony_ci return PTR_ERR(opp); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = dev_pm_opp_set_opp(dev, opp); 8862306a36Sopenharmony_ci dev_pm_opp_put(opp); 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int qcom_cpufreq_update_opp(struct device *cpu_dev, 9362306a36Sopenharmony_ci unsigned long freq_khz, 9462306a36Sopenharmony_ci unsigned long volt) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci unsigned long freq_hz = freq_khz * 1000; 9762306a36Sopenharmony_ci int ret; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* Skip voltage update if the opp table is not available */ 10062306a36Sopenharmony_ci if (!icc_scaling_enabled) 10162306a36Sopenharmony_ci return dev_pm_opp_add(cpu_dev, freq_hz, volt); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = dev_pm_opp_adjust_voltage(cpu_dev, freq_hz, volt, volt, volt); 10462306a36Sopenharmony_ci if (ret) { 10562306a36Sopenharmony_ci dev_err(cpu_dev, "Voltage update failed freq=%ld\n", freq_khz); 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return dev_pm_opp_enable(cpu_dev, freq_hz); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, 11362306a36Sopenharmony_ci unsigned int index) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 11662306a36Sopenharmony_ci const struct qcom_cpufreq_soc_data *soc_data = qcom_cpufreq.soc_data; 11762306a36Sopenharmony_ci unsigned long freq = policy->freq_table[index].frequency; 11862306a36Sopenharmony_ci unsigned int i; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci writel_relaxed(index, data->base + soc_data->reg_perf_state); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (data->per_core_dcvs) 12362306a36Sopenharmony_ci for (i = 1; i < cpumask_weight(policy->related_cpus); i++) 12462306a36Sopenharmony_ci writel_relaxed(index, data->base + soc_data->reg_perf_state + i * 4); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (icc_scaling_enabled) 12762306a36Sopenharmony_ci qcom_cpufreq_set_bw(policy, freq); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic unsigned long qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned int lval; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (qcom_cpufreq.soc_data->reg_current_vote) 13762306a36Sopenharmony_ci lval = readl_relaxed(data->base + qcom_cpufreq.soc_data->reg_current_vote) & 0x3ff; 13862306a36Sopenharmony_ci else 13962306a36Sopenharmony_ci lval = readl_relaxed(data->base + qcom_cpufreq.soc_data->reg_domain_state) & 0xff; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return lval * xo_rate; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* Get the frequency requested by the cpufreq core for the CPU */ 14562306a36Sopenharmony_cistatic unsigned int qcom_cpufreq_get_freq(unsigned int cpu) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct qcom_cpufreq_data *data; 14862306a36Sopenharmony_ci const struct qcom_cpufreq_soc_data *soc_data; 14962306a36Sopenharmony_ci struct cpufreq_policy *policy; 15062306a36Sopenharmony_ci unsigned int index; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci policy = cpufreq_cpu_get_raw(cpu); 15362306a36Sopenharmony_ci if (!policy) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci data = policy->driver_data; 15762306a36Sopenharmony_ci soc_data = qcom_cpufreq.soc_data; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci index = readl_relaxed(data->base + soc_data->reg_perf_state); 16062306a36Sopenharmony_ci index = min(index, LUT_MAX_ENTRIES - 1); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return policy->freq_table[index].frequency; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic unsigned int qcom_cpufreq_hw_get(unsigned int cpu) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct qcom_cpufreq_data *data; 16862306a36Sopenharmony_ci struct cpufreq_policy *policy; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci policy = cpufreq_cpu_get_raw(cpu); 17162306a36Sopenharmony_ci if (!policy) 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci data = policy->driver_data; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (data->throttle_irq >= 0) 17762306a36Sopenharmony_ci return qcom_lmh_get_throttle_freq(data) / HZ_PER_KHZ; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return qcom_cpufreq_get_freq(cpu); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, 18362306a36Sopenharmony_ci unsigned int target_freq) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 18662306a36Sopenharmony_ci const struct qcom_cpufreq_soc_data *soc_data = qcom_cpufreq.soc_data; 18762306a36Sopenharmony_ci unsigned int index; 18862306a36Sopenharmony_ci unsigned int i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci index = policy->cached_resolved_idx; 19162306a36Sopenharmony_ci writel_relaxed(index, data->base + soc_data->reg_perf_state); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (data->per_core_dcvs) 19462306a36Sopenharmony_ci for (i = 1; i < cpumask_weight(policy->related_cpus); i++) 19562306a36Sopenharmony_ci writel_relaxed(index, data->base + soc_data->reg_perf_state + i * 4); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return policy->freq_table[index].frequency; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, 20162306a36Sopenharmony_ci struct cpufreq_policy *policy) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci u32 data, src, lval, i, core_count, prev_freq = 0, freq; 20462306a36Sopenharmony_ci u32 volt; 20562306a36Sopenharmony_ci struct cpufreq_frequency_table *table; 20662306a36Sopenharmony_ci struct dev_pm_opp *opp; 20762306a36Sopenharmony_ci unsigned long rate; 20862306a36Sopenharmony_ci int ret; 20962306a36Sopenharmony_ci struct qcom_cpufreq_data *drv_data = policy->driver_data; 21062306a36Sopenharmony_ci const struct qcom_cpufreq_soc_data *soc_data = qcom_cpufreq.soc_data; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL); 21362306a36Sopenharmony_ci if (!table) 21462306a36Sopenharmony_ci return -ENOMEM; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = dev_pm_opp_of_add_table(cpu_dev); 21762306a36Sopenharmony_ci if (!ret) { 21862306a36Sopenharmony_ci /* Disable all opps and cross-validate against LUT later */ 21962306a36Sopenharmony_ci icc_scaling_enabled = true; 22062306a36Sopenharmony_ci for (rate = 0; ; rate++) { 22162306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate); 22262306a36Sopenharmony_ci if (IS_ERR(opp)) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci dev_pm_opp_put(opp); 22662306a36Sopenharmony_ci dev_pm_opp_disable(cpu_dev, rate); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } else if (ret != -ENODEV) { 22962306a36Sopenharmony_ci dev_err(cpu_dev, "Invalid opp table in device tree\n"); 23062306a36Sopenharmony_ci kfree(table); 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci } else { 23362306a36Sopenharmony_ci policy->fast_switch_possible = true; 23462306a36Sopenharmony_ci icc_scaling_enabled = false; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (i = 0; i < LUT_MAX_ENTRIES; i++) { 23862306a36Sopenharmony_ci data = readl_relaxed(drv_data->base + soc_data->reg_freq_lut + 23962306a36Sopenharmony_ci i * soc_data->lut_row_size); 24062306a36Sopenharmony_ci src = FIELD_GET(LUT_SRC, data); 24162306a36Sopenharmony_ci lval = FIELD_GET(LUT_L_VAL, data); 24262306a36Sopenharmony_ci core_count = FIELD_GET(LUT_CORE_COUNT, data); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci data = readl_relaxed(drv_data->base + soc_data->reg_volt_lut + 24562306a36Sopenharmony_ci i * soc_data->lut_row_size); 24662306a36Sopenharmony_ci volt = FIELD_GET(LUT_VOLT, data) * 1000; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (src) 24962306a36Sopenharmony_ci freq = xo_rate * lval / 1000; 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci freq = cpu_hw_rate / 1000; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (freq != prev_freq && core_count != LUT_TURBO_IND) { 25462306a36Sopenharmony_ci if (!qcom_cpufreq_update_opp(cpu_dev, freq, volt)) { 25562306a36Sopenharmony_ci table[i].frequency = freq; 25662306a36Sopenharmony_ci dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i, 25762306a36Sopenharmony_ci freq, core_count); 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci dev_warn(cpu_dev, "failed to update OPP for freq=%d\n", freq); 26062306a36Sopenharmony_ci table[i].frequency = CPUFREQ_ENTRY_INVALID; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci } else if (core_count == LUT_TURBO_IND) { 26462306a36Sopenharmony_ci table[i].frequency = CPUFREQ_ENTRY_INVALID; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * Two of the same frequencies with the same core counts means 26962306a36Sopenharmony_ci * end of table 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci if (i > 0 && prev_freq == freq) { 27262306a36Sopenharmony_ci struct cpufreq_frequency_table *prev = &table[i - 1]; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Only treat the last frequency that might be a boost 27662306a36Sopenharmony_ci * as the boost frequency 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (prev->frequency == CPUFREQ_ENTRY_INVALID) { 27962306a36Sopenharmony_ci if (!qcom_cpufreq_update_opp(cpu_dev, prev_freq, volt)) { 28062306a36Sopenharmony_ci prev->frequency = prev_freq; 28162306a36Sopenharmony_ci prev->flags = CPUFREQ_BOOST_FREQ; 28262306a36Sopenharmony_ci } else { 28362306a36Sopenharmony_ci dev_warn(cpu_dev, "failed to update OPP for freq=%d\n", 28462306a36Sopenharmony_ci freq); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci prev_freq = freq; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci table[i].frequency = CPUFREQ_TABLE_END; 29562306a36Sopenharmony_ci policy->freq_table = table; 29662306a36Sopenharmony_ci dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic void qcom_get_related_cpus(int index, struct cpumask *m) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct device_node *cpu_np; 30462306a36Sopenharmony_ci struct of_phandle_args args; 30562306a36Sopenharmony_ci int cpu, ret; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 30862306a36Sopenharmony_ci cpu_np = of_cpu_device_node_get(cpu); 30962306a36Sopenharmony_ci if (!cpu_np) 31062306a36Sopenharmony_ci continue; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain", 31362306a36Sopenharmony_ci "#freq-domain-cells", 0, 31462306a36Sopenharmony_ci &args); 31562306a36Sopenharmony_ci of_node_put(cpu_np); 31662306a36Sopenharmony_ci if (ret < 0) 31762306a36Sopenharmony_ci continue; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (index == args.args[0]) 32062306a36Sopenharmony_ci cpumask_set_cpu(cpu, m); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct cpufreq_policy *policy = data->policy; 32762306a36Sopenharmony_ci int cpu = cpumask_first(policy->related_cpus); 32862306a36Sopenharmony_ci struct device *dev = get_cpu_device(cpu); 32962306a36Sopenharmony_ci unsigned long freq_hz, throttled_freq; 33062306a36Sopenharmony_ci struct dev_pm_opp *opp; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci * Get the h/w throttled frequency, normalize it using the 33462306a36Sopenharmony_ci * registered opp table and use it to calculate thermal pressure. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci freq_hz = qcom_lmh_get_throttle_freq(data); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_floor(dev, &freq_hz); 33962306a36Sopenharmony_ci if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE) 34062306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dev, &freq_hz); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (IS_ERR(opp)) { 34362306a36Sopenharmony_ci dev_warn(dev, "Can't find the OPP for throttling: %pe!\n", opp); 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci dev_pm_opp_put(opp); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci throttled_freq = freq_hz / HZ_PER_KHZ; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Update thermal pressure (the boost frequencies are accepted) */ 35162306a36Sopenharmony_ci arch_update_thermal_pressure(policy->related_cpus, throttled_freq); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * In the unlikely case policy is unregistered do not enable 35562306a36Sopenharmony_ci * polling or h/w interrupt 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci mutex_lock(&data->throttle_lock); 35862306a36Sopenharmony_ci if (data->cancel_throttle) 35962306a36Sopenharmony_ci goto out; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * If h/w throttled frequency is higher than what cpufreq has requested 36362306a36Sopenharmony_ci * for, then stop polling and switch back to interrupt mechanism. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci if (throttled_freq >= qcom_cpufreq_get_freq(cpu)) 36662306a36Sopenharmony_ci enable_irq(data->throttle_irq); 36762306a36Sopenharmony_ci else 36862306a36Sopenharmony_ci mod_delayed_work(system_highpri_wq, &data->throttle_work, 36962306a36Sopenharmony_ci msecs_to_jiffies(10)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ciout: 37262306a36Sopenharmony_ci mutex_unlock(&data->throttle_lock); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void qcom_lmh_dcvs_poll(struct work_struct *work) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct qcom_cpufreq_data *data; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci data = container_of(work, struct qcom_cpufreq_data, throttle_work.work); 38062306a36Sopenharmony_ci qcom_lmh_dcvs_notify(data); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct qcom_cpufreq_data *c_data = data; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Disable interrupt and enable polling */ 38862306a36Sopenharmony_ci disable_irq_nosync(c_data->throttle_irq); 38962306a36Sopenharmony_ci schedule_delayed_work(&c_data->throttle_work, 0); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (qcom_cpufreq.soc_data->reg_intr_clr) 39262306a36Sopenharmony_ci writel_relaxed(GT_IRQ_STATUS, 39362306a36Sopenharmony_ci c_data->base + qcom_cpufreq.soc_data->reg_intr_clr); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return IRQ_HANDLED; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic const struct qcom_cpufreq_soc_data qcom_soc_data = { 39962306a36Sopenharmony_ci .reg_enable = 0x0, 40062306a36Sopenharmony_ci .reg_dcvs_ctrl = 0xbc, 40162306a36Sopenharmony_ci .reg_freq_lut = 0x110, 40262306a36Sopenharmony_ci .reg_volt_lut = 0x114, 40362306a36Sopenharmony_ci .reg_current_vote = 0x704, 40462306a36Sopenharmony_ci .reg_perf_state = 0x920, 40562306a36Sopenharmony_ci .lut_row_size = 32, 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic const struct qcom_cpufreq_soc_data epss_soc_data = { 40962306a36Sopenharmony_ci .reg_enable = 0x0, 41062306a36Sopenharmony_ci .reg_domain_state = 0x20, 41162306a36Sopenharmony_ci .reg_dcvs_ctrl = 0xb0, 41262306a36Sopenharmony_ci .reg_freq_lut = 0x100, 41362306a36Sopenharmony_ci .reg_volt_lut = 0x200, 41462306a36Sopenharmony_ci .reg_intr_clr = 0x308, 41562306a36Sopenharmony_ci .reg_perf_state = 0x320, 41662306a36Sopenharmony_ci .lut_row_size = 4, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic const struct of_device_id qcom_cpufreq_hw_match[] = { 42062306a36Sopenharmony_ci { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data }, 42162306a36Sopenharmony_ci { .compatible = "qcom,cpufreq-epss", .data = &epss_soc_data }, 42262306a36Sopenharmony_ci {} 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 42962306a36Sopenharmony_ci struct platform_device *pdev = cpufreq_get_driver_data(); 43062306a36Sopenharmony_ci int ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 43362306a36Sopenharmony_ci * Look for LMh interrupt. If no interrupt line is specified / 43462306a36Sopenharmony_ci * if there is an error, allow cpufreq to be enabled as usual. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci data->throttle_irq = platform_get_irq_optional(pdev, index); 43762306a36Sopenharmony_ci if (data->throttle_irq == -ENXIO) 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci if (data->throttle_irq < 0) 44062306a36Sopenharmony_ci return data->throttle_irq; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci data->cancel_throttle = false; 44362306a36Sopenharmony_ci data->policy = policy; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci mutex_init(&data->throttle_lock); 44662306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&data->throttle_work, qcom_lmh_dcvs_poll); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci snprintf(data->irq_name, sizeof(data->irq_name), "dcvsh-irq-%u", policy->cpu); 44962306a36Sopenharmony_ci ret = request_threaded_irq(data->throttle_irq, NULL, qcom_lmh_dcvs_handle_irq, 45062306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_NO_AUTOEN, data->irq_name, data); 45162306a36Sopenharmony_ci if (ret) { 45262306a36Sopenharmony_ci dev_err(&pdev->dev, "Error registering %s: %d\n", data->irq_name, ret); 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = irq_set_affinity_and_hint(data->throttle_irq, policy->cpus); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set CPU affinity of %s[%d]\n", 45962306a36Sopenharmony_ci data->irq_name, data->throttle_irq); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int qcom_cpufreq_hw_cpu_online(struct cpufreq_policy *policy) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 46762306a36Sopenharmony_ci struct platform_device *pdev = cpufreq_get_driver_data(); 46862306a36Sopenharmony_ci int ret; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (data->throttle_irq <= 0) 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci mutex_lock(&data->throttle_lock); 47462306a36Sopenharmony_ci data->cancel_throttle = false; 47562306a36Sopenharmony_ci mutex_unlock(&data->throttle_lock); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci ret = irq_set_affinity_and_hint(data->throttle_irq, policy->cpus); 47862306a36Sopenharmony_ci if (ret) 47962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set CPU affinity of %s[%d]\n", 48062306a36Sopenharmony_ci data->irq_name, data->throttle_irq); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int qcom_cpufreq_hw_cpu_offline(struct cpufreq_policy *policy) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (data->throttle_irq <= 0) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci mutex_lock(&data->throttle_lock); 49362306a36Sopenharmony_ci data->cancel_throttle = true; 49462306a36Sopenharmony_ci mutex_unlock(&data->throttle_lock); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci cancel_delayed_work_sync(&data->throttle_work); 49762306a36Sopenharmony_ci irq_set_affinity_and_hint(data->throttle_irq, NULL); 49862306a36Sopenharmony_ci disable_irq_nosync(data->throttle_irq); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci if (data->throttle_irq <= 0) 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci free_irq(data->throttle_irq, data); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct platform_device *pdev = cpufreq_get_driver_data(); 51462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 51562306a36Sopenharmony_ci struct of_phandle_args args; 51662306a36Sopenharmony_ci struct device_node *cpu_np; 51762306a36Sopenharmony_ci struct device *cpu_dev; 51862306a36Sopenharmony_ci struct qcom_cpufreq_data *data; 51962306a36Sopenharmony_ci int ret, index; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci cpu_dev = get_cpu_device(policy->cpu); 52262306a36Sopenharmony_ci if (!cpu_dev) { 52362306a36Sopenharmony_ci pr_err("%s: failed to get cpu%d device\n", __func__, 52462306a36Sopenharmony_ci policy->cpu); 52562306a36Sopenharmony_ci return -ENODEV; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci cpu_np = of_cpu_device_node_get(policy->cpu); 52962306a36Sopenharmony_ci if (!cpu_np) 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ret = of_parse_phandle_with_args(cpu_np, "qcom,freq-domain", 53362306a36Sopenharmony_ci "#freq-domain-cells", 0, &args); 53462306a36Sopenharmony_ci of_node_put(cpu_np); 53562306a36Sopenharmony_ci if (ret) 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci index = args.args[0]; 53962306a36Sopenharmony_ci data = &qcom_cpufreq.data[index]; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* HW should be in enabled state to proceed */ 54262306a36Sopenharmony_ci if (!(readl_relaxed(data->base + qcom_cpufreq.soc_data->reg_enable) & 0x1)) { 54362306a36Sopenharmony_ci dev_err(dev, "Domain-%d cpufreq hardware not enabled\n", index); 54462306a36Sopenharmony_ci return -ENODEV; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (readl_relaxed(data->base + qcom_cpufreq.soc_data->reg_dcvs_ctrl) & 0x1) 54862306a36Sopenharmony_ci data->per_core_dcvs = true; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci qcom_get_related_cpus(index, policy->cpus); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci policy->driver_data = data; 55362306a36Sopenharmony_ci policy->dvfs_possible_from_any_cpu = true; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy); 55662306a36Sopenharmony_ci if (ret) { 55762306a36Sopenharmony_ci dev_err(dev, "Domain-%d failed to read LUT\n", index); 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci ret = dev_pm_opp_get_opp_count(cpu_dev); 56262306a36Sopenharmony_ci if (ret <= 0) { 56362306a36Sopenharmony_ci dev_err(cpu_dev, "Failed to add OPPs\n"); 56462306a36Sopenharmony_ci return -ENODEV; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (policy_has_boost_freq(policy)) { 56862306a36Sopenharmony_ci ret = cpufreq_enable_boost_support(); 56962306a36Sopenharmony_ci if (ret) 57062306a36Sopenharmony_ci dev_warn(cpu_dev, "failed to enable boost: %d\n", ret); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return qcom_cpufreq_hw_lmh_init(policy, index); 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct device *cpu_dev = get_cpu_device(policy->cpu); 57962306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci dev_pm_opp_remove_all_dynamic(cpu_dev); 58262306a36Sopenharmony_ci dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); 58362306a36Sopenharmony_ci qcom_cpufreq_hw_lmh_exit(data); 58462306a36Sopenharmony_ci kfree(policy->freq_table); 58562306a36Sopenharmony_ci kfree(data); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void qcom_cpufreq_ready(struct cpufreq_policy *policy) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct qcom_cpufreq_data *data = policy->driver_data; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (data->throttle_irq >= 0) 59562306a36Sopenharmony_ci enable_irq(data->throttle_irq); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic struct freq_attr *qcom_cpufreq_hw_attr[] = { 59962306a36Sopenharmony_ci &cpufreq_freq_attr_scaling_available_freqs, 60062306a36Sopenharmony_ci &cpufreq_freq_attr_scaling_boost_freqs, 60162306a36Sopenharmony_ci NULL 60262306a36Sopenharmony_ci}; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic struct cpufreq_driver cpufreq_qcom_hw_driver = { 60562306a36Sopenharmony_ci .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK | 60662306a36Sopenharmony_ci CPUFREQ_HAVE_GOVERNOR_PER_POLICY | 60762306a36Sopenharmony_ci CPUFREQ_IS_COOLING_DEV, 60862306a36Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 60962306a36Sopenharmony_ci .target_index = qcom_cpufreq_hw_target_index, 61062306a36Sopenharmony_ci .get = qcom_cpufreq_hw_get, 61162306a36Sopenharmony_ci .init = qcom_cpufreq_hw_cpu_init, 61262306a36Sopenharmony_ci .exit = qcom_cpufreq_hw_cpu_exit, 61362306a36Sopenharmony_ci .online = qcom_cpufreq_hw_cpu_online, 61462306a36Sopenharmony_ci .offline = qcom_cpufreq_hw_cpu_offline, 61562306a36Sopenharmony_ci .register_em = cpufreq_register_em_with_opp, 61662306a36Sopenharmony_ci .fast_switch = qcom_cpufreq_hw_fast_switch, 61762306a36Sopenharmony_ci .name = "qcom-cpufreq-hw", 61862306a36Sopenharmony_ci .attr = qcom_cpufreq_hw_attr, 61962306a36Sopenharmony_ci .ready = qcom_cpufreq_ready, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic unsigned long qcom_cpufreq_hw_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci struct qcom_cpufreq_data *data = container_of(hw, struct qcom_cpufreq_data, cpu_clk); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return qcom_lmh_get_throttle_freq(data); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic const struct clk_ops qcom_cpufreq_hw_clk_ops = { 63062306a36Sopenharmony_ci .recalc_rate = qcom_cpufreq_hw_recalc_rate, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data; 63662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 63762306a36Sopenharmony_ci struct device *cpu_dev; 63862306a36Sopenharmony_ci struct clk *clk; 63962306a36Sopenharmony_ci int ret, i, num_domains; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci clk = clk_get(dev, "xo"); 64262306a36Sopenharmony_ci if (IS_ERR(clk)) 64362306a36Sopenharmony_ci return PTR_ERR(clk); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci xo_rate = clk_get_rate(clk); 64662306a36Sopenharmony_ci clk_put(clk); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci clk = clk_get(dev, "alternate"); 64962306a36Sopenharmony_ci if (IS_ERR(clk)) 65062306a36Sopenharmony_ci return PTR_ERR(clk); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci cpu_hw_rate = clk_get_rate(clk) / CLK_HW_DIV; 65362306a36Sopenharmony_ci clk_put(clk); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci cpufreq_qcom_hw_driver.driver_data = pdev; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Check for optional interconnect paths on CPU0 */ 65862306a36Sopenharmony_ci cpu_dev = get_cpu_device(0); 65962306a36Sopenharmony_ci if (!cpu_dev) 66062306a36Sopenharmony_ci return -EPROBE_DEFER; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL); 66362306a36Sopenharmony_ci if (ret) 66462306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Failed to find icc paths\n"); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci for (num_domains = 0; num_domains < MAX_FREQ_DOMAINS; num_domains++) 66762306a36Sopenharmony_ci if (!platform_get_resource(pdev, IORESOURCE_MEM, num_domains)) 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci qcom_cpufreq.data = devm_kzalloc(dev, sizeof(struct qcom_cpufreq_data) * num_domains, 67162306a36Sopenharmony_ci GFP_KERNEL); 67262306a36Sopenharmony_ci if (!qcom_cpufreq.data) 67362306a36Sopenharmony_ci return -ENOMEM; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci qcom_cpufreq.soc_data = of_device_get_match_data(dev); 67662306a36Sopenharmony_ci if (!qcom_cpufreq.soc_data) 67762306a36Sopenharmony_ci return -ENODEV; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, num_domains), GFP_KERNEL); 68062306a36Sopenharmony_ci if (!clk_data) 68162306a36Sopenharmony_ci return -ENOMEM; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci clk_data->num = num_domains; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = 0; i < num_domains; i++) { 68662306a36Sopenharmony_ci struct qcom_cpufreq_data *data = &qcom_cpufreq.data[i]; 68762306a36Sopenharmony_ci struct clk_init_data clk_init = {}; 68862306a36Sopenharmony_ci void __iomem *base; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, i); 69162306a36Sopenharmony_ci if (IS_ERR(base)) { 69262306a36Sopenharmony_ci dev_err(dev, "Failed to map resource index %d\n", i); 69362306a36Sopenharmony_ci return PTR_ERR(base); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci data->base = base; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Register CPU clock for each frequency domain */ 69962306a36Sopenharmony_ci clk_init.name = kasprintf(GFP_KERNEL, "qcom_cpufreq%d", i); 70062306a36Sopenharmony_ci if (!clk_init.name) 70162306a36Sopenharmony_ci return -ENOMEM; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci clk_init.flags = CLK_GET_RATE_NOCACHE; 70462306a36Sopenharmony_ci clk_init.ops = &qcom_cpufreq_hw_clk_ops; 70562306a36Sopenharmony_ci data->cpu_clk.init = &clk_init; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &data->cpu_clk); 70862306a36Sopenharmony_ci if (ret < 0) { 70962306a36Sopenharmony_ci dev_err(dev, "Failed to register clock %d: %d\n", i, ret); 71062306a36Sopenharmony_ci kfree(clk_init.name); 71162306a36Sopenharmony_ci return ret; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci clk_data->hws[i] = &data->cpu_clk; 71562306a36Sopenharmony_ci kfree(clk_init.name); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); 71962306a36Sopenharmony_ci if (ret < 0) { 72062306a36Sopenharmony_ci dev_err(dev, "Failed to add clock provider\n"); 72162306a36Sopenharmony_ci return ret; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ret = cpufreq_register_driver(&cpufreq_qcom_hw_driver); 72562306a36Sopenharmony_ci if (ret) 72662306a36Sopenharmony_ci dev_err(dev, "CPUFreq HW driver failed to register\n"); 72762306a36Sopenharmony_ci else 72862306a36Sopenharmony_ci dev_dbg(dev, "QCOM CPUFreq HW driver initialized\n"); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return ret; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void qcom_cpufreq_hw_driver_remove(struct platform_device *pdev) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci cpufreq_unregister_driver(&cpufreq_qcom_hw_driver); 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic struct platform_driver qcom_cpufreq_hw_driver = { 73962306a36Sopenharmony_ci .probe = qcom_cpufreq_hw_driver_probe, 74062306a36Sopenharmony_ci .remove_new = qcom_cpufreq_hw_driver_remove, 74162306a36Sopenharmony_ci .driver = { 74262306a36Sopenharmony_ci .name = "qcom-cpufreq-hw", 74362306a36Sopenharmony_ci .of_match_table = qcom_cpufreq_hw_match, 74462306a36Sopenharmony_ci }, 74562306a36Sopenharmony_ci}; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic int __init qcom_cpufreq_hw_init(void) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci return platform_driver_register(&qcom_cpufreq_hw_driver); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_cipostcore_initcall(qcom_cpufreq_hw_init); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic void __exit qcom_cpufreq_hw_exit(void) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci platform_driver_unregister(&qcom_cpufreq_hw_driver); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_cimodule_exit(qcom_cpufreq_hw_exit); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ciMODULE_DESCRIPTION("QCOM CPUFREQ HW Driver"); 76062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 761