162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2020 - 2022, NVIDIA CORPORATION. All rights reserved
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/cpu.h>
762306a36Sopenharmony_ci#include <linux/cpufreq.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_platform.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/units.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/smp_plat.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <soc/tegra/bpmp.h>
2062306a36Sopenharmony_ci#include <soc/tegra/bpmp-abi.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define KHZ                     1000
2362306a36Sopenharmony_ci#define REF_CLK_MHZ             408 /* 408 MHz */
2462306a36Sopenharmony_ci#define US_DELAY                500
2562306a36Sopenharmony_ci#define CPUFREQ_TBL_STEP_HZ     (50 * KHZ * KHZ)
2662306a36Sopenharmony_ci#define MAX_CNT                 ~0U
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define NDIV_MASK              0x1FF
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define CORE_OFFSET(cpu)			(cpu * 8)
3162306a36Sopenharmony_ci#define CMU_CLKS_BASE				0x2000
3262306a36Sopenharmony_ci#define SCRATCH_FREQ_CORE_REG(data, cpu)	(data->regs + CMU_CLKS_BASE + CORE_OFFSET(cpu))
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define MMCRAB_CLUSTER_BASE(cl)			(0x30000 + (cl * 0x10000))
3562306a36Sopenharmony_ci#define CLUSTER_ACTMON_BASE(data, cl) \
3662306a36Sopenharmony_ci			(data->regs + (MMCRAB_CLUSTER_BASE(cl) + data->soc->actmon_cntr_base))
3762306a36Sopenharmony_ci#define CORE_ACTMON_CNTR_REG(data, cl, cpu)	(CLUSTER_ACTMON_BASE(data, cl) + CORE_OFFSET(cpu))
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* cpufreq transisition latency */
4062306a36Sopenharmony_ci#define TEGRA_CPUFREQ_TRANSITION_LATENCY (300 * 1000) /* unit in nanoseconds */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct tegra_cpu_ctr {
4362306a36Sopenharmony_ci	u32 cpu;
4462306a36Sopenharmony_ci	u32 coreclk_cnt, last_coreclk_cnt;
4562306a36Sopenharmony_ci	u32 refclk_cnt, last_refclk_cnt;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct read_counters_work {
4962306a36Sopenharmony_ci	struct work_struct work;
5062306a36Sopenharmony_ci	struct tegra_cpu_ctr c;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct tegra_cpufreq_ops {
5462306a36Sopenharmony_ci	void (*read_counters)(struct tegra_cpu_ctr *c);
5562306a36Sopenharmony_ci	void (*set_cpu_ndiv)(struct cpufreq_policy *policy, u64 ndiv);
5662306a36Sopenharmony_ci	void (*get_cpu_cluster_id)(u32 cpu, u32 *cpuid, u32 *clusterid);
5762306a36Sopenharmony_ci	int (*get_cpu_ndiv)(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv);
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistruct tegra_cpufreq_soc {
6162306a36Sopenharmony_ci	struct tegra_cpufreq_ops *ops;
6262306a36Sopenharmony_ci	int maxcpus_per_cluster;
6362306a36Sopenharmony_ci	unsigned int num_clusters;
6462306a36Sopenharmony_ci	phys_addr_t actmon_cntr_base;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistruct tegra194_cpufreq_data {
6862306a36Sopenharmony_ci	void __iomem *regs;
6962306a36Sopenharmony_ci	struct cpufreq_frequency_table **bpmp_luts;
7062306a36Sopenharmony_ci	const struct tegra_cpufreq_soc *soc;
7162306a36Sopenharmony_ci	bool icc_dram_bw_scaling;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic struct workqueue_struct *read_counters_wq;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int tegra_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
7962306a36Sopenharmony_ci	struct dev_pm_opp *opp;
8062306a36Sopenharmony_ci	struct device *dev;
8162306a36Sopenharmony_ci	int ret;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	dev = get_cpu_device(policy->cpu);
8462306a36Sopenharmony_ci	if (!dev)
8562306a36Sopenharmony_ci		return -ENODEV;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	opp = dev_pm_opp_find_freq_exact(dev, freq_khz * KHZ, true);
8862306a36Sopenharmony_ci	if (IS_ERR(opp))
8962306a36Sopenharmony_ci		return PTR_ERR(opp);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	ret = dev_pm_opp_set_opp(dev, opp);
9262306a36Sopenharmony_ci	if (ret)
9362306a36Sopenharmony_ci		data->icc_dram_bw_scaling = false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	dev_pm_opp_put(opp);
9662306a36Sopenharmony_ci	return ret;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void tegra_get_cpu_mpidr(void *mpidr)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	*((u64 *)mpidr) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void tegra234_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	u64 mpidr;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	smp_call_function_single(cpu, tegra_get_cpu_mpidr, &mpidr, true);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (cpuid)
11162306a36Sopenharmony_ci		*cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
11262306a36Sopenharmony_ci	if (clusterid)
11362306a36Sopenharmony_ci		*clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 2);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int tegra234_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
11962306a36Sopenharmony_ci	void __iomem *freq_core_reg;
12062306a36Sopenharmony_ci	u64 mpidr_id;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* use physical id to get address of per core frequency register */
12362306a36Sopenharmony_ci	mpidr_id = (clusterid * data->soc->maxcpus_per_cluster) + cpuid;
12462306a36Sopenharmony_ci	freq_core_reg = SCRATCH_FREQ_CORE_REG(data, mpidr_id);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	*ndiv = readl(freq_core_reg) & NDIV_MASK;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return 0;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void tegra234_set_cpu_ndiv(struct cpufreq_policy *policy, u64 ndiv)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
13462306a36Sopenharmony_ci	void __iomem *freq_core_reg;
13562306a36Sopenharmony_ci	u32 cpu, cpuid, clusterid;
13662306a36Sopenharmony_ci	u64 mpidr_id;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	for_each_cpu_and(cpu, policy->cpus, cpu_online_mask) {
13962306a36Sopenharmony_ci		data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		/* use physical id to get address of per core frequency register */
14262306a36Sopenharmony_ci		mpidr_id = (clusterid * data->soc->maxcpus_per_cluster) + cpuid;
14362306a36Sopenharmony_ci		freq_core_reg = SCRATCH_FREQ_CORE_REG(data, mpidr_id);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		writel(ndiv, freq_core_reg);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * This register provides access to two counter values with a single
15162306a36Sopenharmony_ci * 64-bit read. The counter values are used to determine the average
15262306a36Sopenharmony_ci * actual frequency a core has run at over a period of time.
15362306a36Sopenharmony_ci *     [63:32] PLLP counter: Counts at fixed frequency (408 MHz)
15462306a36Sopenharmony_ci *     [31:0] Core clock counter: Counts on every core clock cycle
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistatic void tegra234_read_counters(struct tegra_cpu_ctr *c)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
15962306a36Sopenharmony_ci	void __iomem *actmon_reg;
16062306a36Sopenharmony_ci	u32 cpuid, clusterid;
16162306a36Sopenharmony_ci	u64 val;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	data->soc->ops->get_cpu_cluster_id(c->cpu, &cpuid, &clusterid);
16462306a36Sopenharmony_ci	actmon_reg = CORE_ACTMON_CNTR_REG(data, clusterid, cpuid);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	val = readq(actmon_reg);
16762306a36Sopenharmony_ci	c->last_refclk_cnt = upper_32_bits(val);
16862306a36Sopenharmony_ci	c->last_coreclk_cnt = lower_32_bits(val);
16962306a36Sopenharmony_ci	udelay(US_DELAY);
17062306a36Sopenharmony_ci	val = readq(actmon_reg);
17162306a36Sopenharmony_ci	c->refclk_cnt = upper_32_bits(val);
17262306a36Sopenharmony_ci	c->coreclk_cnt = lower_32_bits(val);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct tegra_cpufreq_ops tegra234_cpufreq_ops = {
17662306a36Sopenharmony_ci	.read_counters = tegra234_read_counters,
17762306a36Sopenharmony_ci	.get_cpu_cluster_id = tegra234_get_cpu_cluster_id,
17862306a36Sopenharmony_ci	.get_cpu_ndiv = tegra234_get_cpu_ndiv,
17962306a36Sopenharmony_ci	.set_cpu_ndiv = tegra234_set_cpu_ndiv,
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic const struct tegra_cpufreq_soc tegra234_cpufreq_soc = {
18362306a36Sopenharmony_ci	.ops = &tegra234_cpufreq_ops,
18462306a36Sopenharmony_ci	.actmon_cntr_base = 0x9000,
18562306a36Sopenharmony_ci	.maxcpus_per_cluster = 4,
18662306a36Sopenharmony_ci	.num_clusters = 3,
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic const struct tegra_cpufreq_soc tegra239_cpufreq_soc = {
19062306a36Sopenharmony_ci	.ops = &tegra234_cpufreq_ops,
19162306a36Sopenharmony_ci	.actmon_cntr_base = 0x4000,
19262306a36Sopenharmony_ci	.maxcpus_per_cluster = 8,
19362306a36Sopenharmony_ci	.num_clusters = 1,
19462306a36Sopenharmony_ci};
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void tegra194_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	u64 mpidr;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	smp_call_function_single(cpu, tegra_get_cpu_mpidr, &mpidr, true);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (cpuid)
20362306a36Sopenharmony_ci		*cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
20462306a36Sopenharmony_ci	if (clusterid)
20562306a36Sopenharmony_ci		*clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*
20962306a36Sopenharmony_ci * Read per-core Read-only system register NVFREQ_FEEDBACK_EL1.
21062306a36Sopenharmony_ci * The register provides frequency feedback information to
21162306a36Sopenharmony_ci * determine the average actual frequency a core has run at over
21262306a36Sopenharmony_ci * a period of time.
21362306a36Sopenharmony_ci *	[31:0] PLLP counter: Counts at fixed frequency (408 MHz)
21462306a36Sopenharmony_ci *	[63:32] Core clock counter: counts on every core clock cycle
21562306a36Sopenharmony_ci *			where the core is architecturally clocking
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_cistatic u64 read_freq_feedback(void)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	u64 val = 0;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	asm volatile("mrs %0, s3_0_c15_c0_5" : "=r" (val) : );
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return val;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_response
22762306a36Sopenharmony_ci				   *nltbl, u16 ndiv)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	return nltbl->ref_clk_hz / KHZ * ndiv / (nltbl->pdiv * nltbl->mdiv);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void tegra194_read_counters(struct tegra_cpu_ctr *c)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	u64 val;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	val = read_freq_feedback();
23762306a36Sopenharmony_ci	c->last_refclk_cnt = lower_32_bits(val);
23862306a36Sopenharmony_ci	c->last_coreclk_cnt = upper_32_bits(val);
23962306a36Sopenharmony_ci	udelay(US_DELAY);
24062306a36Sopenharmony_ci	val = read_freq_feedback();
24162306a36Sopenharmony_ci	c->refclk_cnt = lower_32_bits(val);
24262306a36Sopenharmony_ci	c->coreclk_cnt = upper_32_bits(val);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void tegra_read_counters(struct work_struct *work)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
24862306a36Sopenharmony_ci	struct read_counters_work *read_counters_work;
24962306a36Sopenharmony_ci	struct tegra_cpu_ctr *c;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/*
25262306a36Sopenharmony_ci	 * ref_clk_counter(32 bit counter) runs on constant clk,
25362306a36Sopenharmony_ci	 * pll_p(408MHz).
25462306a36Sopenharmony_ci	 * It will take = 2 ^ 32 / 408 MHz to overflow ref clk counter
25562306a36Sopenharmony_ci	 *              = 10526880 usec = 10.527 sec to overflow
25662306a36Sopenharmony_ci	 *
25762306a36Sopenharmony_ci	 * Like wise core_clk_counter(32 bit counter) runs on core clock.
25862306a36Sopenharmony_ci	 * It's synchronized to crab_clk (cpu_crab_clk) which runs at
25962306a36Sopenharmony_ci	 * freq of cluster. Assuming max cluster clock ~2000MHz,
26062306a36Sopenharmony_ci	 * It will take = 2 ^ 32 / 2000 MHz to overflow core clk counter
26162306a36Sopenharmony_ci	 *              = ~2.147 sec to overflow
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	read_counters_work = container_of(work, struct read_counters_work,
26462306a36Sopenharmony_ci					  work);
26562306a36Sopenharmony_ci	c = &read_counters_work->c;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	data->soc->ops->read_counters(c);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/*
27162306a36Sopenharmony_ci * Return instantaneous cpu speed
27262306a36Sopenharmony_ci * Instantaneous freq is calculated as -
27362306a36Sopenharmony_ci * -Takes sample on every query of getting the freq.
27462306a36Sopenharmony_ci *	- Read core and ref clock counters;
27562306a36Sopenharmony_ci *	- Delay for X us
27662306a36Sopenharmony_ci *	- Read above cycle counters again
27762306a36Sopenharmony_ci *	- Calculates freq by subtracting current and previous counters
27862306a36Sopenharmony_ci *	  divided by the delay time or eqv. of ref_clk_counter in delta time
27962306a36Sopenharmony_ci *	- Return Kcycles/second, freq in KHz
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci *	delta time period = x sec
28262306a36Sopenharmony_ci *			  = delta ref_clk_counter / (408 * 10^6) sec
28362306a36Sopenharmony_ci *	freq in Hz = cycles/sec
28462306a36Sopenharmony_ci *		   = (delta cycles / x sec
28562306a36Sopenharmony_ci *		   = (delta cycles * 408 * 10^6) / delta ref_clk_counter
28662306a36Sopenharmony_ci *	in KHz	   = (delta cycles * 408 * 10^3) / delta ref_clk_counter
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * @cpu - logical cpu whose freq to be updated
28962306a36Sopenharmony_ci * Returns freq in KHz on success, 0 if cpu is offline
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic unsigned int tegra194_calculate_speed(u32 cpu)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct read_counters_work read_counters_work;
29462306a36Sopenharmony_ci	struct tegra_cpu_ctr c;
29562306a36Sopenharmony_ci	u32 delta_refcnt;
29662306a36Sopenharmony_ci	u32 delta_ccnt;
29762306a36Sopenharmony_ci	u32 rate_mhz;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/*
30062306a36Sopenharmony_ci	 * udelay() is required to reconstruct cpu frequency over an
30162306a36Sopenharmony_ci	 * observation window. Using workqueue to call udelay() with
30262306a36Sopenharmony_ci	 * interrupts enabled.
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci	read_counters_work.c.cpu = cpu;
30562306a36Sopenharmony_ci	INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters);
30662306a36Sopenharmony_ci	queue_work_on(cpu, read_counters_wq, &read_counters_work.work);
30762306a36Sopenharmony_ci	flush_work(&read_counters_work.work);
30862306a36Sopenharmony_ci	c = read_counters_work.c;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (c.coreclk_cnt < c.last_coreclk_cnt)
31162306a36Sopenharmony_ci		delta_ccnt = c.coreclk_cnt + (MAX_CNT - c.last_coreclk_cnt);
31262306a36Sopenharmony_ci	else
31362306a36Sopenharmony_ci		delta_ccnt = c.coreclk_cnt - c.last_coreclk_cnt;
31462306a36Sopenharmony_ci	if (!delta_ccnt)
31562306a36Sopenharmony_ci		return 0;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* ref clock is 32 bits */
31862306a36Sopenharmony_ci	if (c.refclk_cnt < c.last_refclk_cnt)
31962306a36Sopenharmony_ci		delta_refcnt = c.refclk_cnt + (MAX_CNT - c.last_refclk_cnt);
32062306a36Sopenharmony_ci	else
32162306a36Sopenharmony_ci		delta_refcnt = c.refclk_cnt - c.last_refclk_cnt;
32262306a36Sopenharmony_ci	if (!delta_refcnt) {
32362306a36Sopenharmony_ci		pr_debug("cpufreq: %d is idle, delta_refcnt: 0\n", cpu);
32462306a36Sopenharmony_ci		return 0;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	rate_mhz = ((unsigned long)(delta_ccnt * REF_CLK_MHZ)) / delta_refcnt;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return (rate_mhz * KHZ); /* in KHz */
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void tegra194_get_cpu_ndiv_sysreg(void *ndiv)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	u64 ndiv_val;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	asm volatile("mrs %0, s3_0_c15_c0_4" : "=r" (ndiv_val) : );
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	*(u64 *)ndiv = ndiv_val;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int tegra194_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, &ndiv, true);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void tegra194_set_cpu_ndiv_sysreg(void *data)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	u64 ndiv_val = *(u64 *)data;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void tegra194_set_cpu_ndiv(struct cpufreq_policy *policy, u64 ndiv)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	on_each_cpu_mask(policy->cpus, tegra194_set_cpu_ndiv_sysreg, &ndiv, true);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic unsigned int tegra194_get_speed(u32 cpu)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
36062306a36Sopenharmony_ci	struct cpufreq_frequency_table *pos;
36162306a36Sopenharmony_ci	u32 cpuid, clusterid;
36262306a36Sopenharmony_ci	unsigned int rate;
36362306a36Sopenharmony_ci	u64 ndiv;
36462306a36Sopenharmony_ci	int ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	data->soc->ops->get_cpu_cluster_id(cpu, &cpuid, &clusterid);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* reconstruct actual cpu freq using counters */
36962306a36Sopenharmony_ci	rate = tegra194_calculate_speed(cpu);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* get last written ndiv value */
37262306a36Sopenharmony_ci	ret = data->soc->ops->get_cpu_ndiv(cpu, cpuid, clusterid, &ndiv);
37362306a36Sopenharmony_ci	if (WARN_ON_ONCE(ret))
37462306a36Sopenharmony_ci		return rate;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/*
37762306a36Sopenharmony_ci	 * If the reconstructed frequency has acceptable delta from
37862306a36Sopenharmony_ci	 * the last written value, then return freq corresponding
37962306a36Sopenharmony_ci	 * to the last written ndiv value from freq_table. This is
38062306a36Sopenharmony_ci	 * done to return consistent value.
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	cpufreq_for_each_valid_entry(pos, data->bpmp_luts[clusterid]) {
38362306a36Sopenharmony_ci		if (pos->driver_data != ndiv)
38462306a36Sopenharmony_ci			continue;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (abs(pos->frequency - rate) > 115200) {
38762306a36Sopenharmony_ci			pr_warn("cpufreq: cpu%d,cur:%u,set:%u,set ndiv:%llu\n",
38862306a36Sopenharmony_ci				cpu, rate, pos->frequency, ndiv);
38962306a36Sopenharmony_ci		} else {
39062306a36Sopenharmony_ci			rate = pos->frequency;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci		break;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	return rate;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
39862306a36Sopenharmony_ci					    struct cpufreq_frequency_table *bpmp_lut,
39962306a36Sopenharmony_ci					    struct cpufreq_frequency_table **opp_table)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
40262306a36Sopenharmony_ci	struct cpufreq_frequency_table *freq_table = NULL;
40362306a36Sopenharmony_ci	struct cpufreq_frequency_table *pos;
40462306a36Sopenharmony_ci	struct device *cpu_dev;
40562306a36Sopenharmony_ci	struct dev_pm_opp *opp;
40662306a36Sopenharmony_ci	unsigned long rate;
40762306a36Sopenharmony_ci	int ret, max_opps;
40862306a36Sopenharmony_ci	int j = 0;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	cpu_dev = get_cpu_device(policy->cpu);
41162306a36Sopenharmony_ci	if (!cpu_dev) {
41262306a36Sopenharmony_ci		pr_err("%s: failed to get cpu%d device\n", __func__, policy->cpu);
41362306a36Sopenharmony_ci		return -ENODEV;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Initialize OPP table mentioned in operating-points-v2 property in DT */
41762306a36Sopenharmony_ci	ret = dev_pm_opp_of_add_table_indexed(cpu_dev, 0);
41862306a36Sopenharmony_ci	if (!ret) {
41962306a36Sopenharmony_ci		max_opps = dev_pm_opp_get_opp_count(cpu_dev);
42062306a36Sopenharmony_ci		if (max_opps <= 0) {
42162306a36Sopenharmony_ci			dev_err(cpu_dev, "Failed to add OPPs\n");
42262306a36Sopenharmony_ci			return max_opps;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		/* Disable all opps and cross-validate against LUT later */
42662306a36Sopenharmony_ci		for (rate = 0; ; rate++) {
42762306a36Sopenharmony_ci			opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
42862306a36Sopenharmony_ci			if (IS_ERR(opp))
42962306a36Sopenharmony_ci				break;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci			dev_pm_opp_put(opp);
43262306a36Sopenharmony_ci			dev_pm_opp_disable(cpu_dev, rate);
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci	} else {
43562306a36Sopenharmony_ci		dev_err(cpu_dev, "Invalid or empty opp table in device tree\n");
43662306a36Sopenharmony_ci		data->icc_dram_bw_scaling = false;
43762306a36Sopenharmony_ci		return ret;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
44162306a36Sopenharmony_ci	if (!freq_table)
44262306a36Sopenharmony_ci		return -ENOMEM;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/*
44562306a36Sopenharmony_ci	 * Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
44662306a36Sopenharmony_ci	 * Enable only those DT OPP's which are present in LUT also.
44762306a36Sopenharmony_ci	 */
44862306a36Sopenharmony_ci	cpufreq_for_each_valid_entry(pos, bpmp_lut) {
44962306a36Sopenharmony_ci		opp = dev_pm_opp_find_freq_exact(cpu_dev, pos->frequency * KHZ, false);
45062306a36Sopenharmony_ci		if (IS_ERR(opp))
45162306a36Sopenharmony_ci			continue;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		dev_pm_opp_put(opp);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		ret = dev_pm_opp_enable(cpu_dev, pos->frequency * KHZ);
45662306a36Sopenharmony_ci		if (ret < 0)
45762306a36Sopenharmony_ci			return ret;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		freq_table[j].driver_data = pos->driver_data;
46062306a36Sopenharmony_ci		freq_table[j].frequency = pos->frequency;
46162306a36Sopenharmony_ci		j++;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	freq_table[j].driver_data = pos->driver_data;
46562306a36Sopenharmony_ci	freq_table[j].frequency = CPUFREQ_TABLE_END;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	*opp_table = &freq_table[0];
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return ret;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int tegra194_cpufreq_init(struct cpufreq_policy *policy)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
47762306a36Sopenharmony_ci	int maxcpus_per_cluster = data->soc->maxcpus_per_cluster;
47862306a36Sopenharmony_ci	struct cpufreq_frequency_table *freq_table;
47962306a36Sopenharmony_ci	struct cpufreq_frequency_table *bpmp_lut;
48062306a36Sopenharmony_ci	u32 start_cpu, cpu;
48162306a36Sopenharmony_ci	u32 clusterid;
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	data->soc->ops->get_cpu_cluster_id(policy->cpu, NULL, &clusterid);
48562306a36Sopenharmony_ci	if (clusterid >= data->soc->num_clusters || !data->bpmp_luts[clusterid])
48662306a36Sopenharmony_ci		return -EINVAL;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	start_cpu = rounddown(policy->cpu, maxcpus_per_cluster);
48962306a36Sopenharmony_ci	/* set same policy for all cpus in a cluster */
49062306a36Sopenharmony_ci	for (cpu = start_cpu; cpu < (start_cpu + maxcpus_per_cluster); cpu++) {
49162306a36Sopenharmony_ci		if (cpu_possible(cpu))
49262306a36Sopenharmony_ci			cpumask_set_cpu(cpu, policy->cpus);
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci	policy->cpuinfo.transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	bpmp_lut = data->bpmp_luts[clusterid];
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (data->icc_dram_bw_scaling) {
49962306a36Sopenharmony_ci		ret = tegra_cpufreq_init_cpufreq_table(policy, bpmp_lut, &freq_table);
50062306a36Sopenharmony_ci		if (!ret) {
50162306a36Sopenharmony_ci			policy->freq_table = freq_table;
50262306a36Sopenharmony_ci			return 0;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	data->icc_dram_bw_scaling = false;
50762306a36Sopenharmony_ci	policy->freq_table = bpmp_lut;
50862306a36Sopenharmony_ci	pr_info("OPP tables missing from DT, EMC frequency scaling disabled\n");
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return 0;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic int tegra194_cpufreq_online(struct cpufreq_policy *policy)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	/* We did light-weight tear down earlier, nothing to do here */
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic int tegra194_cpufreq_offline(struct cpufreq_policy *policy)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	/*
52262306a36Sopenharmony_ci	 * Preserve policy->driver_data and don't free resources on light-weight
52362306a36Sopenharmony_ci	 * tear down.
52462306a36Sopenharmony_ci	 */
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int tegra194_cpufreq_exit(struct cpufreq_policy *policy)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct device *cpu_dev = get_cpu_device(policy->cpu);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dev_pm_opp_remove_all_dynamic(cpu_dev);
53462306a36Sopenharmony_ci	dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
54062306a36Sopenharmony_ci				       unsigned int index)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct cpufreq_frequency_table *tbl = policy->freq_table + index;
54362306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/*
54662306a36Sopenharmony_ci	 * Each core writes frequency in per core register. Then both cores
54762306a36Sopenharmony_ci	 * in a cluster run at same frequency which is the maximum frequency
54862306a36Sopenharmony_ci	 * request out of the values requested by both cores in that cluster.
54962306a36Sopenharmony_ci	 */
55062306a36Sopenharmony_ci	data->soc->ops->set_cpu_ndiv(policy, (u64)tbl->driver_data);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (data->icc_dram_bw_scaling)
55362306a36Sopenharmony_ci		tegra_cpufreq_set_bw(policy, tbl->frequency);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic struct cpufreq_driver tegra194_cpufreq_driver = {
55962306a36Sopenharmony_ci	.name = "tegra194",
56062306a36Sopenharmony_ci	.flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
56162306a36Sopenharmony_ci		 CPUFREQ_IS_COOLING_DEV,
56262306a36Sopenharmony_ci	.verify = cpufreq_generic_frequency_table_verify,
56362306a36Sopenharmony_ci	.target_index = tegra194_cpufreq_set_target,
56462306a36Sopenharmony_ci	.get = tegra194_get_speed,
56562306a36Sopenharmony_ci	.init = tegra194_cpufreq_init,
56662306a36Sopenharmony_ci	.exit = tegra194_cpufreq_exit,
56762306a36Sopenharmony_ci	.online = tegra194_cpufreq_online,
56862306a36Sopenharmony_ci	.offline = tegra194_cpufreq_offline,
56962306a36Sopenharmony_ci	.attr = cpufreq_generic_attr,
57062306a36Sopenharmony_ci};
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic struct tegra_cpufreq_ops tegra194_cpufreq_ops = {
57362306a36Sopenharmony_ci	.read_counters = tegra194_read_counters,
57462306a36Sopenharmony_ci	.get_cpu_cluster_id = tegra194_get_cpu_cluster_id,
57562306a36Sopenharmony_ci	.get_cpu_ndiv = tegra194_get_cpu_ndiv,
57662306a36Sopenharmony_ci	.set_cpu_ndiv = tegra194_set_cpu_ndiv,
57762306a36Sopenharmony_ci};
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic const struct tegra_cpufreq_soc tegra194_cpufreq_soc = {
58062306a36Sopenharmony_ci	.ops = &tegra194_cpufreq_ops,
58162306a36Sopenharmony_ci	.maxcpus_per_cluster = 2,
58262306a36Sopenharmony_ci	.num_clusters = 4,
58362306a36Sopenharmony_ci};
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic void tegra194_cpufreq_free_resources(void)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	destroy_workqueue(read_counters_wq);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic struct cpufreq_frequency_table *
59162306a36Sopenharmony_citegra_cpufreq_bpmp_read_lut(struct platform_device *pdev, struct tegra_bpmp *bpmp,
59262306a36Sopenharmony_ci			    unsigned int cluster_id)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct cpufreq_frequency_table *freq_table;
59562306a36Sopenharmony_ci	struct mrq_cpu_ndiv_limits_response resp;
59662306a36Sopenharmony_ci	unsigned int num_freqs, ndiv, delta_ndiv;
59762306a36Sopenharmony_ci	struct mrq_cpu_ndiv_limits_request req;
59862306a36Sopenharmony_ci	struct tegra_bpmp_message msg;
59962306a36Sopenharmony_ci	u16 freq_table_step_size;
60062306a36Sopenharmony_ci	int err, index;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	memset(&req, 0, sizeof(req));
60362306a36Sopenharmony_ci	req.cluster_id = cluster_id;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
60662306a36Sopenharmony_ci	msg.mrq = MRQ_CPU_NDIV_LIMITS;
60762306a36Sopenharmony_ci	msg.tx.data = &req;
60862306a36Sopenharmony_ci	msg.tx.size = sizeof(req);
60962306a36Sopenharmony_ci	msg.rx.data = &resp;
61062306a36Sopenharmony_ci	msg.rx.size = sizeof(resp);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	err = tegra_bpmp_transfer(bpmp, &msg);
61362306a36Sopenharmony_ci	if (err)
61462306a36Sopenharmony_ci		return ERR_PTR(err);
61562306a36Sopenharmony_ci	if (msg.rx.ret == -BPMP_EINVAL) {
61662306a36Sopenharmony_ci		/* Cluster not available */
61762306a36Sopenharmony_ci		return NULL;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	if (msg.rx.ret)
62062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/*
62362306a36Sopenharmony_ci	 * Make sure frequency table step is a multiple of mdiv to match
62462306a36Sopenharmony_ci	 * vhint table granularity.
62562306a36Sopenharmony_ci	 */
62662306a36Sopenharmony_ci	freq_table_step_size = resp.mdiv *
62762306a36Sopenharmony_ci			DIV_ROUND_UP(CPUFREQ_TBL_STEP_HZ, resp.ref_clk_hz);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "cluster %d: frequency table step size: %d\n",
63062306a36Sopenharmony_ci		cluster_id, freq_table_step_size);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	delta_ndiv = resp.ndiv_max - resp.ndiv_min;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (unlikely(delta_ndiv == 0)) {
63562306a36Sopenharmony_ci		num_freqs = 1;
63662306a36Sopenharmony_ci	} else {
63762306a36Sopenharmony_ci		/* We store both ndiv_min and ndiv_max hence the +1 */
63862306a36Sopenharmony_ci		num_freqs = delta_ndiv / freq_table_step_size + 1;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	num_freqs += (delta_ndiv % freq_table_step_size) ? 1 : 0;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	freq_table = devm_kcalloc(&pdev->dev, num_freqs + 1,
64462306a36Sopenharmony_ci				  sizeof(*freq_table), GFP_KERNEL);
64562306a36Sopenharmony_ci	if (!freq_table)
64662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	for (index = 0, ndiv = resp.ndiv_min;
64962306a36Sopenharmony_ci			ndiv < resp.ndiv_max;
65062306a36Sopenharmony_ci			index++, ndiv += freq_table_step_size) {
65162306a36Sopenharmony_ci		freq_table[index].driver_data = ndiv;
65262306a36Sopenharmony_ci		freq_table[index].frequency = map_ndiv_to_freq(&resp, ndiv);
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	freq_table[index].driver_data = resp.ndiv_max;
65662306a36Sopenharmony_ci	freq_table[index++].frequency = map_ndiv_to_freq(&resp, resp.ndiv_max);
65762306a36Sopenharmony_ci	freq_table[index].frequency = CPUFREQ_TABLE_END;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return freq_table;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int tegra194_cpufreq_probe(struct platform_device *pdev)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	const struct tegra_cpufreq_soc *soc;
66562306a36Sopenharmony_ci	struct tegra194_cpufreq_data *data;
66662306a36Sopenharmony_ci	struct tegra_bpmp *bpmp;
66762306a36Sopenharmony_ci	struct device *cpu_dev;
66862306a36Sopenharmony_ci	int err, i;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
67162306a36Sopenharmony_ci	if (!data)
67262306a36Sopenharmony_ci		return -ENOMEM;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	soc = of_device_get_match_data(&pdev->dev);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (soc->ops && soc->maxcpus_per_cluster && soc->num_clusters) {
67762306a36Sopenharmony_ci		data->soc = soc;
67862306a36Sopenharmony_ci	} else {
67962306a36Sopenharmony_ci		dev_err(&pdev->dev, "soc data missing\n");
68062306a36Sopenharmony_ci		return -EINVAL;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	data->bpmp_luts = devm_kcalloc(&pdev->dev, data->soc->num_clusters,
68462306a36Sopenharmony_ci				       sizeof(*data->bpmp_luts), GFP_KERNEL);
68562306a36Sopenharmony_ci	if (!data->bpmp_luts)
68662306a36Sopenharmony_ci		return -ENOMEM;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if (soc->actmon_cntr_base) {
68962306a36Sopenharmony_ci		/* mmio registers are used for frequency request and re-construction */
69062306a36Sopenharmony_ci		data->regs = devm_platform_ioremap_resource(pdev, 0);
69162306a36Sopenharmony_ci		if (IS_ERR(data->regs))
69262306a36Sopenharmony_ci			return PTR_ERR(data->regs);
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	platform_set_drvdata(pdev, data);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	bpmp = tegra_bpmp_get(&pdev->dev);
69862306a36Sopenharmony_ci	if (IS_ERR(bpmp))
69962306a36Sopenharmony_ci		return PTR_ERR(bpmp);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1);
70262306a36Sopenharmony_ci	if (!read_counters_wq) {
70362306a36Sopenharmony_ci		dev_err(&pdev->dev, "fail to create_workqueue\n");
70462306a36Sopenharmony_ci		err = -EINVAL;
70562306a36Sopenharmony_ci		goto put_bpmp;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	for (i = 0; i < data->soc->num_clusters; i++) {
70962306a36Sopenharmony_ci		data->bpmp_luts[i] = tegra_cpufreq_bpmp_read_lut(pdev, bpmp, i);
71062306a36Sopenharmony_ci		if (IS_ERR(data->bpmp_luts[i])) {
71162306a36Sopenharmony_ci			err = PTR_ERR(data->bpmp_luts[i]);
71262306a36Sopenharmony_ci			goto err_free_res;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	tegra194_cpufreq_driver.driver_data = data;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
71962306a36Sopenharmony_ci	cpu_dev = get_cpu_device(0);
72062306a36Sopenharmony_ci	if (!cpu_dev) {
72162306a36Sopenharmony_ci		err = -EPROBE_DEFER;
72262306a36Sopenharmony_ci		goto err_free_res;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (dev_pm_opp_of_get_opp_desc_node(cpu_dev)) {
72662306a36Sopenharmony_ci		err = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
72762306a36Sopenharmony_ci		if (!err)
72862306a36Sopenharmony_ci			data->icc_dram_bw_scaling = true;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	err = cpufreq_register_driver(&tegra194_cpufreq_driver);
73262306a36Sopenharmony_ci	if (!err)
73362306a36Sopenharmony_ci		goto put_bpmp;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cierr_free_res:
73662306a36Sopenharmony_ci	tegra194_cpufreq_free_resources();
73762306a36Sopenharmony_ciput_bpmp:
73862306a36Sopenharmony_ci	tegra_bpmp_put(bpmp);
73962306a36Sopenharmony_ci	return err;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic void tegra194_cpufreq_remove(struct platform_device *pdev)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	cpufreq_unregister_driver(&tegra194_cpufreq_driver);
74562306a36Sopenharmony_ci	tegra194_cpufreq_free_resources();
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic const struct of_device_id tegra194_cpufreq_of_match[] = {
74962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-ccplex", .data = &tegra194_cpufreq_soc },
75062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra234-ccplex-cluster", .data = &tegra234_cpufreq_soc },
75162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra239-ccplex-cluster", .data = &tegra239_cpufreq_soc },
75262306a36Sopenharmony_ci	{ /* sentinel */ }
75362306a36Sopenharmony_ci};
75462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra194_cpufreq_of_match);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic struct platform_driver tegra194_ccplex_driver = {
75762306a36Sopenharmony_ci	.driver = {
75862306a36Sopenharmony_ci		.name = "tegra194-cpufreq",
75962306a36Sopenharmony_ci		.of_match_table = tegra194_cpufreq_of_match,
76062306a36Sopenharmony_ci	},
76162306a36Sopenharmony_ci	.probe = tegra194_cpufreq_probe,
76262306a36Sopenharmony_ci	.remove_new = tegra194_cpufreq_remove,
76362306a36Sopenharmony_ci};
76462306a36Sopenharmony_cimodule_platform_driver(tegra194_ccplex_driver);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ciMODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
76762306a36Sopenharmony_ciMODULE_AUTHOR("Sumit Gupta <sumitg@nvidia.com>");
76862306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra194 cpufreq driver");
76962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
770