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