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