18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/cpu.h> 78c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <soc/tegra/bpmp.h> 198c2ecf20Sopenharmony_ci#include <soc/tegra/bpmp-abi.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define KHZ 1000 228c2ecf20Sopenharmony_ci#define REF_CLK_MHZ 408 /* 408 MHz */ 238c2ecf20Sopenharmony_ci#define US_DELAY 500 248c2ecf20Sopenharmony_ci#define US_DELAY_MIN 2 258c2ecf20Sopenharmony_ci#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ) 268c2ecf20Sopenharmony_ci#define MAX_CNT ~0U 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* cpufreq transisition latency */ 298c2ecf20Sopenharmony_ci#define TEGRA_CPUFREQ_TRANSITION_LATENCY (300 * 1000) /* unit in nanoseconds */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cienum cluster { 328c2ecf20Sopenharmony_ci CLUSTER0, 338c2ecf20Sopenharmony_ci CLUSTER1, 348c2ecf20Sopenharmony_ci CLUSTER2, 358c2ecf20Sopenharmony_ci CLUSTER3, 368c2ecf20Sopenharmony_ci MAX_CLUSTERS, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct tegra194_cpufreq_data { 408c2ecf20Sopenharmony_ci void __iomem *regs; 418c2ecf20Sopenharmony_ci size_t num_clusters; 428c2ecf20Sopenharmony_ci struct cpufreq_frequency_table **tables; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct tegra_cpu_ctr { 468c2ecf20Sopenharmony_ci u32 cpu; 478c2ecf20Sopenharmony_ci u32 delay; 488c2ecf20Sopenharmony_ci u32 coreclk_cnt, last_coreclk_cnt; 498c2ecf20Sopenharmony_ci u32 refclk_cnt, last_refclk_cnt; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct read_counters_work { 538c2ecf20Sopenharmony_ci struct work_struct work; 548c2ecf20Sopenharmony_ci struct tegra_cpu_ctr c; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct workqueue_struct *read_counters_wq; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void get_cpu_cluster(void *cluster) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci *((uint32_t *)cluster) = MPIDR_AFFINITY_LEVEL(mpidr, 1); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * Read per-core Read-only system register NVFREQ_FEEDBACK_EL1. 688c2ecf20Sopenharmony_ci * The register provides frequency feedback information to 698c2ecf20Sopenharmony_ci * determine the average actual frequency a core has run at over 708c2ecf20Sopenharmony_ci * a period of time. 718c2ecf20Sopenharmony_ci * [31:0] PLLP counter: Counts at fixed frequency (408 MHz) 728c2ecf20Sopenharmony_ci * [63:32] Core clock counter: counts on every core clock cycle 738c2ecf20Sopenharmony_ci * where the core is architecturally clocking 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic u64 read_freq_feedback(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci u64 val = 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci asm volatile("mrs %0, s3_0_c15_c0_5" : "=r" (val) : ); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return val; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_response 858c2ecf20Sopenharmony_ci *nltbl, u16 ndiv) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return nltbl->ref_clk_hz / KHZ * ndiv / (nltbl->pdiv * nltbl->mdiv); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void tegra_read_counters(struct work_struct *work) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct read_counters_work *read_counters_work; 938c2ecf20Sopenharmony_ci struct tegra_cpu_ctr *c; 948c2ecf20Sopenharmony_ci u64 val; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * ref_clk_counter(32 bit counter) runs on constant clk, 988c2ecf20Sopenharmony_ci * pll_p(408MHz). 998c2ecf20Sopenharmony_ci * It will take = 2 ^ 32 / 408 MHz to overflow ref clk counter 1008c2ecf20Sopenharmony_ci * = 10526880 usec = 10.527 sec to overflow 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Like wise core_clk_counter(32 bit counter) runs on core clock. 1038c2ecf20Sopenharmony_ci * It's synchronized to crab_clk (cpu_crab_clk) which runs at 1048c2ecf20Sopenharmony_ci * freq of cluster. Assuming max cluster clock ~2000MHz, 1058c2ecf20Sopenharmony_ci * It will take = 2 ^ 32 / 2000 MHz to overflow core clk counter 1068c2ecf20Sopenharmony_ci * = ~2.147 sec to overflow 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci read_counters_work = container_of(work, struct read_counters_work, 1098c2ecf20Sopenharmony_ci work); 1108c2ecf20Sopenharmony_ci c = &read_counters_work->c; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci val = read_freq_feedback(); 1138c2ecf20Sopenharmony_ci c->last_refclk_cnt = lower_32_bits(val); 1148c2ecf20Sopenharmony_ci c->last_coreclk_cnt = upper_32_bits(val); 1158c2ecf20Sopenharmony_ci udelay(c->delay); 1168c2ecf20Sopenharmony_ci val = read_freq_feedback(); 1178c2ecf20Sopenharmony_ci c->refclk_cnt = lower_32_bits(val); 1188c2ecf20Sopenharmony_ci c->coreclk_cnt = upper_32_bits(val); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Return instantaneous cpu speed 1238c2ecf20Sopenharmony_ci * Instantaneous freq is calculated as - 1248c2ecf20Sopenharmony_ci * -Takes sample on every query of getting the freq. 1258c2ecf20Sopenharmony_ci * - Read core and ref clock counters; 1268c2ecf20Sopenharmony_ci * - Delay for X us 1278c2ecf20Sopenharmony_ci * - Read above cycle counters again 1288c2ecf20Sopenharmony_ci * - Calculates freq by subtracting current and previous counters 1298c2ecf20Sopenharmony_ci * divided by the delay time or eqv. of ref_clk_counter in delta time 1308c2ecf20Sopenharmony_ci * - Return Kcycles/second, freq in KHz 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * delta time period = x sec 1338c2ecf20Sopenharmony_ci * = delta ref_clk_counter / (408 * 10^6) sec 1348c2ecf20Sopenharmony_ci * freq in Hz = cycles/sec 1358c2ecf20Sopenharmony_ci * = (delta cycles / x sec 1368c2ecf20Sopenharmony_ci * = (delta cycles * 408 * 10^6) / delta ref_clk_counter 1378c2ecf20Sopenharmony_ci * in KHz = (delta cycles * 408 * 10^3) / delta ref_clk_counter 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * @cpu - logical cpu whose freq to be updated 1408c2ecf20Sopenharmony_ci * Returns freq in KHz on success, 0 if cpu is offline 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_cistatic unsigned int tegra194_get_speed_common(u32 cpu, u32 delay) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct read_counters_work read_counters_work; 1458c2ecf20Sopenharmony_ci struct tegra_cpu_ctr c; 1468c2ecf20Sopenharmony_ci u32 delta_refcnt; 1478c2ecf20Sopenharmony_ci u32 delta_ccnt; 1488c2ecf20Sopenharmony_ci u32 rate_mhz; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * udelay() is required to reconstruct cpu frequency over an 1528c2ecf20Sopenharmony_ci * observation window. Using workqueue to call udelay() with 1538c2ecf20Sopenharmony_ci * interrupts enabled. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci read_counters_work.c.cpu = cpu; 1568c2ecf20Sopenharmony_ci read_counters_work.c.delay = delay; 1578c2ecf20Sopenharmony_ci INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters); 1588c2ecf20Sopenharmony_ci queue_work_on(cpu, read_counters_wq, &read_counters_work.work); 1598c2ecf20Sopenharmony_ci flush_work(&read_counters_work.work); 1608c2ecf20Sopenharmony_ci c = read_counters_work.c; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (c.coreclk_cnt < c.last_coreclk_cnt) 1638c2ecf20Sopenharmony_ci delta_ccnt = c.coreclk_cnt + (MAX_CNT - c.last_coreclk_cnt); 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci delta_ccnt = c.coreclk_cnt - c.last_coreclk_cnt; 1668c2ecf20Sopenharmony_ci if (!delta_ccnt) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* ref clock is 32 bits */ 1708c2ecf20Sopenharmony_ci if (c.refclk_cnt < c.last_refclk_cnt) 1718c2ecf20Sopenharmony_ci delta_refcnt = c.refclk_cnt + (MAX_CNT - c.last_refclk_cnt); 1728c2ecf20Sopenharmony_ci else 1738c2ecf20Sopenharmony_ci delta_refcnt = c.refclk_cnt - c.last_refclk_cnt; 1748c2ecf20Sopenharmony_ci if (!delta_refcnt) { 1758c2ecf20Sopenharmony_ci pr_debug("cpufreq: %d is idle, delta_refcnt: 0\n", cpu); 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci rate_mhz = ((unsigned long)(delta_ccnt * REF_CLK_MHZ)) / delta_refcnt; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return (rate_mhz * KHZ); /* in KHz */ 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic unsigned int tegra194_get_speed(u32 cpu) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci return tegra194_get_speed_common(cpu, US_DELAY); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int tegra194_cpufreq_init(struct cpufreq_policy *policy) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct tegra194_cpufreq_data *data = cpufreq_get_driver_data(); 1918c2ecf20Sopenharmony_ci u32 cpu; 1928c2ecf20Sopenharmony_ci u32 cl; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci smp_call_function_single(policy->cpu, get_cpu_cluster, &cl, true); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (cl >= data->num_clusters) 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* boot freq */ 2008c2ecf20Sopenharmony_ci policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* set same policy for all cpus in a cluster */ 2038c2ecf20Sopenharmony_ci for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++) 2048c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, policy->cpus); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci policy->freq_table = data->tables[cl]; 2078c2ecf20Sopenharmony_ci policy->cpuinfo.transition_latency = TEGRA_CPUFREQ_TRANSITION_LATENCY; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void set_cpu_ndiv(void *data) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct cpufreq_frequency_table *tbl = data; 2158c2ecf20Sopenharmony_ci u64 ndiv_val = (u64)tbl->driver_data; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val)); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int tegra194_cpufreq_set_target(struct cpufreq_policy *policy, 2218c2ecf20Sopenharmony_ci unsigned int index) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct cpufreq_frequency_table *tbl = policy->freq_table + index; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * Each core writes frequency in per core register. Then both cores 2278c2ecf20Sopenharmony_ci * in a cluster run at same frequency which is the maximum frequency 2288c2ecf20Sopenharmony_ci * request out of the values requested by both cores in that cluster. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci on_each_cpu_mask(policy->cpus, set_cpu_ndiv, tbl, true); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic struct cpufreq_driver tegra194_cpufreq_driver = { 2368c2ecf20Sopenharmony_ci .name = "tegra194", 2378c2ecf20Sopenharmony_ci .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | 2388c2ecf20Sopenharmony_ci CPUFREQ_NEED_INITIAL_FREQ_CHECK, 2398c2ecf20Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 2408c2ecf20Sopenharmony_ci .target_index = tegra194_cpufreq_set_target, 2418c2ecf20Sopenharmony_ci .get = tegra194_get_speed, 2428c2ecf20Sopenharmony_ci .init = tegra194_cpufreq_init, 2438c2ecf20Sopenharmony_ci .attr = cpufreq_generic_attr, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void tegra194_cpufreq_free_resources(void) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci destroy_workqueue(read_counters_wq); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct cpufreq_frequency_table * 2528c2ecf20Sopenharmony_ciinit_freq_table(struct platform_device *pdev, struct tegra_bpmp *bpmp, 2538c2ecf20Sopenharmony_ci unsigned int cluster_id) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct cpufreq_frequency_table *freq_table; 2568c2ecf20Sopenharmony_ci struct mrq_cpu_ndiv_limits_response resp; 2578c2ecf20Sopenharmony_ci unsigned int num_freqs, ndiv, delta_ndiv; 2588c2ecf20Sopenharmony_ci struct mrq_cpu_ndiv_limits_request req; 2598c2ecf20Sopenharmony_ci struct tegra_bpmp_message msg; 2608c2ecf20Sopenharmony_ci u16 freq_table_step_size; 2618c2ecf20Sopenharmony_ci int err, index; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 2648c2ecf20Sopenharmony_ci req.cluster_id = cluster_id; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 2678c2ecf20Sopenharmony_ci msg.mrq = MRQ_CPU_NDIV_LIMITS; 2688c2ecf20Sopenharmony_ci msg.tx.data = &req; 2698c2ecf20Sopenharmony_ci msg.tx.size = sizeof(req); 2708c2ecf20Sopenharmony_ci msg.rx.data = &resp; 2718c2ecf20Sopenharmony_ci msg.rx.size = sizeof(resp); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci err = tegra_bpmp_transfer(bpmp, &msg); 2748c2ecf20Sopenharmony_ci if (err) 2758c2ecf20Sopenharmony_ci return ERR_PTR(err); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * Make sure frequency table step is a multiple of mdiv to match 2798c2ecf20Sopenharmony_ci * vhint table granularity. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci freq_table_step_size = resp.mdiv * 2828c2ecf20Sopenharmony_ci DIV_ROUND_UP(CPUFREQ_TBL_STEP_HZ, resp.ref_clk_hz); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "cluster %d: frequency table step size: %d\n", 2858c2ecf20Sopenharmony_ci cluster_id, freq_table_step_size); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci delta_ndiv = resp.ndiv_max - resp.ndiv_min; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (unlikely(delta_ndiv == 0)) { 2908c2ecf20Sopenharmony_ci num_freqs = 1; 2918c2ecf20Sopenharmony_ci } else { 2928c2ecf20Sopenharmony_ci /* We store both ndiv_min and ndiv_max hence the +1 */ 2938c2ecf20Sopenharmony_ci num_freqs = delta_ndiv / freq_table_step_size + 1; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci num_freqs += (delta_ndiv % freq_table_step_size) ? 1 : 0; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci freq_table = devm_kcalloc(&pdev->dev, num_freqs + 1, 2998c2ecf20Sopenharmony_ci sizeof(*freq_table), GFP_KERNEL); 3008c2ecf20Sopenharmony_ci if (!freq_table) 3018c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci for (index = 0, ndiv = resp.ndiv_min; 3048c2ecf20Sopenharmony_ci ndiv < resp.ndiv_max; 3058c2ecf20Sopenharmony_ci index++, ndiv += freq_table_step_size) { 3068c2ecf20Sopenharmony_ci freq_table[index].driver_data = ndiv; 3078c2ecf20Sopenharmony_ci freq_table[index].frequency = map_ndiv_to_freq(&resp, ndiv); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci freq_table[index].driver_data = resp.ndiv_max; 3118c2ecf20Sopenharmony_ci freq_table[index++].frequency = map_ndiv_to_freq(&resp, resp.ndiv_max); 3128c2ecf20Sopenharmony_ci freq_table[index].frequency = CPUFREQ_TABLE_END; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return freq_table; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int tegra194_cpufreq_probe(struct platform_device *pdev) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct tegra194_cpufreq_data *data; 3208c2ecf20Sopenharmony_ci struct tegra_bpmp *bpmp; 3218c2ecf20Sopenharmony_ci int err, i; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 3248c2ecf20Sopenharmony_ci if (!data) 3258c2ecf20Sopenharmony_ci return -ENOMEM; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci data->num_clusters = MAX_CLUSTERS; 3288c2ecf20Sopenharmony_ci data->tables = devm_kcalloc(&pdev->dev, data->num_clusters, 3298c2ecf20Sopenharmony_ci sizeof(*data->tables), GFP_KERNEL); 3308c2ecf20Sopenharmony_ci if (!data->tables) 3318c2ecf20Sopenharmony_ci return -ENOMEM; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci bpmp = tegra_bpmp_get(&pdev->dev); 3368c2ecf20Sopenharmony_ci if (IS_ERR(bpmp)) 3378c2ecf20Sopenharmony_ci return PTR_ERR(bpmp); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1); 3408c2ecf20Sopenharmony_ci if (!read_counters_wq) { 3418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "fail to create_workqueue\n"); 3428c2ecf20Sopenharmony_ci err = -EINVAL; 3438c2ecf20Sopenharmony_ci goto put_bpmp; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci for (i = 0; i < data->num_clusters; i++) { 3478c2ecf20Sopenharmony_ci data->tables[i] = init_freq_table(pdev, bpmp, i); 3488c2ecf20Sopenharmony_ci if (IS_ERR(data->tables[i])) { 3498c2ecf20Sopenharmony_ci err = PTR_ERR(data->tables[i]); 3508c2ecf20Sopenharmony_ci goto err_free_res; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci tegra194_cpufreq_driver.driver_data = data; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci err = cpufreq_register_driver(&tegra194_cpufreq_driver); 3578c2ecf20Sopenharmony_ci if (!err) 3588c2ecf20Sopenharmony_ci goto put_bpmp; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cierr_free_res: 3618c2ecf20Sopenharmony_ci tegra194_cpufreq_free_resources(); 3628c2ecf20Sopenharmony_ciput_bpmp: 3638c2ecf20Sopenharmony_ci tegra_bpmp_put(bpmp); 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int tegra194_cpufreq_remove(struct platform_device *pdev) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci cpufreq_unregister_driver(&tegra194_cpufreq_driver); 3708c2ecf20Sopenharmony_ci tegra194_cpufreq_free_resources(); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic const struct of_device_id tegra194_cpufreq_of_match[] = { 3768c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra194-ccplex", }, 3778c2ecf20Sopenharmony_ci { /* sentinel */ } 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra194_cpufreq_of_match); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic struct platform_driver tegra194_ccplex_driver = { 3828c2ecf20Sopenharmony_ci .driver = { 3838c2ecf20Sopenharmony_ci .name = "tegra194-cpufreq", 3848c2ecf20Sopenharmony_ci .of_match_table = tegra194_cpufreq_of_match, 3858c2ecf20Sopenharmony_ci }, 3868c2ecf20Sopenharmony_ci .probe = tegra194_cpufreq_probe, 3878c2ecf20Sopenharmony_ci .remove = tegra194_cpufreq_remove, 3888c2ecf20Sopenharmony_ci}; 3898c2ecf20Sopenharmony_cimodule_platform_driver(tegra194_ccplex_driver); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); 3928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sumit Gupta <sumitg@nvidia.com>"); 3938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra194 cpufreq driver"); 3948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 395