1de6cc651SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 27fb6a53dSViresh Kumar /* 37fb6a53dSViresh Kumar * Copyright (C) 2007 PA Semi, Inc 47fb6a53dSViresh Kumar * 57fb6a53dSViresh Kumar * Authors: Egor Martovetsky <egor@pasemi.com> 67fb6a53dSViresh Kumar * Olof Johansson <olof@lixom.net> 77fb6a53dSViresh Kumar * 87fb6a53dSViresh Kumar * Maintained by: Olof Johansson <olof@lixom.net> 97fb6a53dSViresh Kumar * 107fb6a53dSViresh Kumar * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: 117fb6a53dSViresh Kumar * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 127fb6a53dSViresh Kumar */ 137fb6a53dSViresh Kumar 147fb6a53dSViresh Kumar #include <linux/cpufreq.h> 157fb6a53dSViresh Kumar #include <linux/timer.h> 167fb6a53dSViresh Kumar #include <linux/module.h> 175af50730SRob Herring #include <linux/of_address.h> 187fb6a53dSViresh Kumar 197fb6a53dSViresh Kumar #include <asm/hw_irq.h> 207fb6a53dSViresh Kumar #include <asm/io.h> 217fb6a53dSViresh Kumar #include <asm/prom.h> 227fb6a53dSViresh Kumar #include <asm/time.h> 237fb6a53dSViresh Kumar #include <asm/smp.h> 247fb6a53dSViresh Kumar 254a27aa9cSLee Jones #include <platforms/pasemi/pasemi.h> 264a27aa9cSLee Jones 277fb6a53dSViresh Kumar #define SDCASR_REG 0x0100 287fb6a53dSViresh Kumar #define SDCASR_REG_STRIDE 0x1000 297fb6a53dSViresh Kumar #define SDCPWR_CFGA0_REG 0x0100 307fb6a53dSViresh Kumar #define SDCPWR_PWST0_REG 0x0000 317fb6a53dSViresh Kumar #define SDCPWR_GIZTIME_REG 0x0440 327fb6a53dSViresh Kumar 337fb6a53dSViresh Kumar /* SDCPWR_GIZTIME_REG fields */ 347fb6a53dSViresh Kumar #define SDCPWR_GIZTIME_GR 0x80000000 357fb6a53dSViresh Kumar #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff 367fb6a53dSViresh Kumar 377fb6a53dSViresh Kumar /* Offset of ASR registers from SDC base */ 387fb6a53dSViresh Kumar #define SDCASR_OFFSET 0x120000 397fb6a53dSViresh Kumar 407fb6a53dSViresh Kumar static void __iomem *sdcpwr_mapbase; 417fb6a53dSViresh Kumar static void __iomem *sdcasr_mapbase; 427fb6a53dSViresh Kumar 437fb6a53dSViresh Kumar /* Current astate, is used when waking up from power savings on 447fb6a53dSViresh Kumar * one core, in case the other core has switched states during 457fb6a53dSViresh Kumar * the idle time. 467fb6a53dSViresh Kumar */ 477fb6a53dSViresh Kumar static int current_astate; 487fb6a53dSViresh Kumar 497fb6a53dSViresh Kumar /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ 507fb6a53dSViresh Kumar static struct cpufreq_frequency_table pas_freqs[] = { 517f4b0461SViresh Kumar {0, 0, 0}, 527f4b0461SViresh Kumar {0, 1, 0}, 537f4b0461SViresh Kumar {0, 2, 0}, 547f4b0461SViresh Kumar {0, 3, 0}, 557f4b0461SViresh Kumar {0, 4, 0}, 567f4b0461SViresh Kumar {0, 0, CPUFREQ_TABLE_END}, 577fb6a53dSViresh Kumar }; 587fb6a53dSViresh Kumar 597fb6a53dSViresh Kumar /* 607fb6a53dSViresh Kumar * hardware specific functions 617fb6a53dSViresh Kumar */ 627fb6a53dSViresh Kumar 637fb6a53dSViresh Kumar static int get_astate_freq(int astate) 647fb6a53dSViresh Kumar { 657fb6a53dSViresh Kumar u32 ret; 667fb6a53dSViresh Kumar ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); 677fb6a53dSViresh Kumar 687fb6a53dSViresh Kumar return ret & 0x3f; 697fb6a53dSViresh Kumar } 707fb6a53dSViresh Kumar 717fb6a53dSViresh Kumar static int get_cur_astate(int cpu) 727fb6a53dSViresh Kumar { 737fb6a53dSViresh Kumar u32 ret; 747fb6a53dSViresh Kumar 757fb6a53dSViresh Kumar ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); 767fb6a53dSViresh Kumar ret = (ret >> (cpu * 4)) & 0x7; 777fb6a53dSViresh Kumar 787fb6a53dSViresh Kumar return ret; 797fb6a53dSViresh Kumar } 807fb6a53dSViresh Kumar 817fb6a53dSViresh Kumar static int get_gizmo_latency(void) 827fb6a53dSViresh Kumar { 837fb6a53dSViresh Kumar u32 giztime, ret; 847fb6a53dSViresh Kumar 857fb6a53dSViresh Kumar giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); 867fb6a53dSViresh Kumar 877fb6a53dSViresh Kumar /* just provide the upper bound */ 887fb6a53dSViresh Kumar if (giztime & SDCPWR_GIZTIME_GR) 897fb6a53dSViresh Kumar ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; 907fb6a53dSViresh Kumar else 917fb6a53dSViresh Kumar ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; 927fb6a53dSViresh Kumar 937fb6a53dSViresh Kumar return ret; 947fb6a53dSViresh Kumar } 957fb6a53dSViresh Kumar 967fb6a53dSViresh Kumar static void set_astate(int cpu, unsigned int astate) 977fb6a53dSViresh Kumar { 987fb6a53dSViresh Kumar unsigned long flags; 997fb6a53dSViresh Kumar 1007fb6a53dSViresh Kumar /* Return if called before init has run */ 1017fb6a53dSViresh Kumar if (unlikely(!sdcasr_mapbase)) 1027fb6a53dSViresh Kumar return; 1037fb6a53dSViresh Kumar 1047fb6a53dSViresh Kumar local_irq_save(flags); 1057fb6a53dSViresh Kumar 1067fb6a53dSViresh Kumar out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); 1077fb6a53dSViresh Kumar 1087fb6a53dSViresh Kumar local_irq_restore(flags); 1097fb6a53dSViresh Kumar } 1107fb6a53dSViresh Kumar 1117fb6a53dSViresh Kumar int check_astate(void) 1127fb6a53dSViresh Kumar { 1137fb6a53dSViresh Kumar return get_cur_astate(hard_smp_processor_id()); 1147fb6a53dSViresh Kumar } 1157fb6a53dSViresh Kumar 1167fb6a53dSViresh Kumar void restore_astate(int cpu) 1177fb6a53dSViresh Kumar { 1187fb6a53dSViresh Kumar set_astate(cpu, current_astate); 1197fb6a53dSViresh Kumar } 1207fb6a53dSViresh Kumar 1217fb6a53dSViresh Kumar /* 1227fb6a53dSViresh Kumar * cpufreq functions 1237fb6a53dSViresh Kumar */ 1247fb6a53dSViresh Kumar 1257fb6a53dSViresh Kumar static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) 1267fb6a53dSViresh Kumar { 127041526f9SStratos Karafotis struct cpufreq_frequency_table *pos; 1287fb6a53dSViresh Kumar const u32 *max_freqp; 1297fb6a53dSViresh Kumar u32 max_freq; 130ffd81dcfSDominik Brodowski int cur_astate, idx; 1317fb6a53dSViresh Kumar struct resource res; 1327fb6a53dSViresh Kumar struct device_node *cpu, *dn; 1337fb6a53dSViresh Kumar int err = -ENODEV; 1347fb6a53dSViresh Kumar 1357fb6a53dSViresh Kumar cpu = of_get_cpu_node(policy->cpu, NULL); 1367fb6a53dSViresh Kumar if (!cpu) 1377fb6a53dSViresh Kumar goto out; 1387fb6a53dSViresh Kumar 139e0a12445SWen Yang max_freqp = of_get_property(cpu, "clock-frequency", NULL); 140e0a12445SWen Yang of_node_put(cpu); 141e0a12445SWen Yang if (!max_freqp) { 142e0a12445SWen Yang err = -EINVAL; 143e0a12445SWen Yang goto out; 144e0a12445SWen Yang } 145e0a12445SWen Yang 146e0a12445SWen Yang /* we need the freq in kHz */ 147e0a12445SWen Yang max_freq = *max_freqp / 1000; 148e0a12445SWen Yang 1497fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, "1682m-sdc"); 1507fb6a53dSViresh Kumar if (!dn) 1517fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, 1527fb6a53dSViresh Kumar "pasemi,pwrficient-sdc"); 1537fb6a53dSViresh Kumar if (!dn) 1547fb6a53dSViresh Kumar goto out; 1557fb6a53dSViresh Kumar err = of_address_to_resource(dn, 0, &res); 1567fb6a53dSViresh Kumar of_node_put(dn); 1577fb6a53dSViresh Kumar if (err) 1587fb6a53dSViresh Kumar goto out; 1597fb6a53dSViresh Kumar sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000); 1607fb6a53dSViresh Kumar if (!sdcasr_mapbase) { 1617fb6a53dSViresh Kumar err = -EINVAL; 1627fb6a53dSViresh Kumar goto out; 1637fb6a53dSViresh Kumar } 1647fb6a53dSViresh Kumar 1657fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo"); 1667fb6a53dSViresh Kumar if (!dn) 1677fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, 1687fb6a53dSViresh Kumar "pasemi,pwrficient-gizmo"); 1697fb6a53dSViresh Kumar if (!dn) { 1707fb6a53dSViresh Kumar err = -ENODEV; 1717fb6a53dSViresh Kumar goto out_unmap_sdcasr; 1727fb6a53dSViresh Kumar } 1737fb6a53dSViresh Kumar err = of_address_to_resource(dn, 0, &res); 1747fb6a53dSViresh Kumar of_node_put(dn); 1757fb6a53dSViresh Kumar if (err) 1767fb6a53dSViresh Kumar goto out_unmap_sdcasr; 1777fb6a53dSViresh Kumar sdcpwr_mapbase = ioremap(res.start, 0x1000); 1787fb6a53dSViresh Kumar if (!sdcpwr_mapbase) { 1797fb6a53dSViresh Kumar err = -EINVAL; 1807fb6a53dSViresh Kumar goto out_unmap_sdcasr; 1817fb6a53dSViresh Kumar } 1827fb6a53dSViresh Kumar 1837fb6a53dSViresh Kumar pr_debug("init cpufreq on CPU %d\n", policy->cpu); 1847fb6a53dSViresh Kumar pr_debug("max clock-frequency is at %u kHz\n", max_freq); 1857fb6a53dSViresh Kumar pr_debug("initializing frequency table\n"); 1867fb6a53dSViresh Kumar 1877fb6a53dSViresh Kumar /* initialize frequency table */ 188ffd81dcfSDominik Brodowski cpufreq_for_each_entry_idx(pos, pas_freqs, idx) { 189041526f9SStratos Karafotis pos->frequency = get_astate_freq(pos->driver_data) * 100000; 190ffd81dcfSDominik Brodowski pr_debug("%d: %d\n", idx, pos->frequency); 1917fb6a53dSViresh Kumar } 1927fb6a53dSViresh Kumar 1937fb6a53dSViresh Kumar cur_astate = get_cur_astate(policy->cpu); 1947fb6a53dSViresh Kumar pr_debug("current astate is at %d\n",cur_astate); 1957fb6a53dSViresh Kumar 1967fb6a53dSViresh Kumar policy->cur = pas_freqs[cur_astate].frequency; 1977fb6a53dSViresh Kumar ppc_proc_freq = policy->cur * 1000ul; 1987fb6a53dSViresh Kumar 199c4dcc8a1SViresh Kumar cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency()); 200c4dcc8a1SViresh Kumar return 0; 2017fb6a53dSViresh Kumar 2027fb6a53dSViresh Kumar out_unmap_sdcasr: 2037fb6a53dSViresh Kumar iounmap(sdcasr_mapbase); 2047fb6a53dSViresh Kumar out: 2057fb6a53dSViresh Kumar return err; 2067fb6a53dSViresh Kumar } 2077fb6a53dSViresh Kumar 2087fb6a53dSViresh Kumar static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) 2097fb6a53dSViresh Kumar { 2107fb6a53dSViresh Kumar /* 2117fb6a53dSViresh Kumar * We don't support CPU hotplug. Don't unmap after the system 2127fb6a53dSViresh Kumar * has already made it to a running state. 2137fb6a53dSViresh Kumar */ 214d04e31a2SThomas Gleixner if (system_state >= SYSTEM_RUNNING) 2157fb6a53dSViresh Kumar return 0; 2167fb6a53dSViresh Kumar 2177fb6a53dSViresh Kumar if (sdcasr_mapbase) 2187fb6a53dSViresh Kumar iounmap(sdcasr_mapbase); 2197fb6a53dSViresh Kumar if (sdcpwr_mapbase) 2207fb6a53dSViresh Kumar iounmap(sdcpwr_mapbase); 2217fb6a53dSViresh Kumar 2227fb6a53dSViresh Kumar return 0; 2237fb6a53dSViresh Kumar } 2247fb6a53dSViresh Kumar 2257fb6a53dSViresh Kumar static int pas_cpufreq_target(struct cpufreq_policy *policy, 2269c0ebcf7SViresh Kumar unsigned int pas_astate_new) 2277fb6a53dSViresh Kumar { 2287fb6a53dSViresh Kumar int i; 2297fb6a53dSViresh Kumar 2307fb6a53dSViresh Kumar pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", 2317fb6a53dSViresh Kumar policy->cpu, 2327fb6a53dSViresh Kumar pas_freqs[pas_astate_new].frequency, 2337fb6a53dSViresh Kumar pas_freqs[pas_astate_new].driver_data); 2347fb6a53dSViresh Kumar 2357fb6a53dSViresh Kumar current_astate = pas_astate_new; 2367fb6a53dSViresh Kumar 2377fb6a53dSViresh Kumar for_each_online_cpu(i) 2387fb6a53dSViresh Kumar set_astate(i, pas_astate_new); 2397fb6a53dSViresh Kumar 240d4019f0aSViresh Kumar ppc_proc_freq = pas_freqs[pas_astate_new].frequency * 1000ul; 2417fb6a53dSViresh Kumar return 0; 2427fb6a53dSViresh Kumar } 2437fb6a53dSViresh Kumar 2447fb6a53dSViresh Kumar static struct cpufreq_driver pas_cpufreq_driver = { 2457fb6a53dSViresh Kumar .name = "pas-cpufreq", 2467fb6a53dSViresh Kumar .flags = CPUFREQ_CONST_LOOPS, 2477fb6a53dSViresh Kumar .init = pas_cpufreq_cpu_init, 2487fb6a53dSViresh Kumar .exit = pas_cpufreq_cpu_exit, 24957174310SViresh Kumar .verify = cpufreq_generic_frequency_table_verify, 2509c0ebcf7SViresh Kumar .target_index = pas_cpufreq_target, 25157174310SViresh Kumar .attr = cpufreq_generic_attr, 2527fb6a53dSViresh Kumar }; 2537fb6a53dSViresh Kumar 2547fb6a53dSViresh Kumar /* 2557fb6a53dSViresh Kumar * module init and destoy 2567fb6a53dSViresh Kumar */ 2577fb6a53dSViresh Kumar 2587fb6a53dSViresh Kumar static int __init pas_cpufreq_init(void) 2597fb6a53dSViresh Kumar { 2607fb6a53dSViresh Kumar if (!of_machine_is_compatible("PA6T-1682M") && 2617fb6a53dSViresh Kumar !of_machine_is_compatible("pasemi,pwrficient")) 2627fb6a53dSViresh Kumar return -ENODEV; 2637fb6a53dSViresh Kumar 2647fb6a53dSViresh Kumar return cpufreq_register_driver(&pas_cpufreq_driver); 2657fb6a53dSViresh Kumar } 2667fb6a53dSViresh Kumar 2677fb6a53dSViresh Kumar static void __exit pas_cpufreq_exit(void) 2687fb6a53dSViresh Kumar { 2697fb6a53dSViresh Kumar cpufreq_unregister_driver(&pas_cpufreq_driver); 2707fb6a53dSViresh Kumar } 2717fb6a53dSViresh Kumar 2727fb6a53dSViresh Kumar module_init(pas_cpufreq_init); 2737fb6a53dSViresh Kumar module_exit(pas_cpufreq_exit); 2747fb6a53dSViresh Kumar 2757fb6a53dSViresh Kumar MODULE_LICENSE("GPL"); 2767fb6a53dSViresh Kumar MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>"); 277