17fb6a53dSViresh Kumar /* 27fb6a53dSViresh Kumar * Copyright (C) 2007 PA Semi, Inc 37fb6a53dSViresh Kumar * 47fb6a53dSViresh Kumar * Authors: Egor Martovetsky <egor@pasemi.com> 57fb6a53dSViresh Kumar * Olof Johansson <olof@lixom.net> 67fb6a53dSViresh Kumar * 77fb6a53dSViresh Kumar * Maintained by: Olof Johansson <olof@lixom.net> 87fb6a53dSViresh Kumar * 97fb6a53dSViresh Kumar * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: 107fb6a53dSViresh Kumar * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 117fb6a53dSViresh Kumar * 127fb6a53dSViresh Kumar * This program is free software; you can redistribute it and/or modify 137fb6a53dSViresh Kumar * it under the terms of the GNU General Public License as published by 147fb6a53dSViresh Kumar * the Free Software Foundation; either version 2, or (at your option) 157fb6a53dSViresh Kumar * any later version. 167fb6a53dSViresh Kumar * 177fb6a53dSViresh Kumar * This program is distributed in the hope that it will be useful, 187fb6a53dSViresh Kumar * but WITHOUT ANY WARRANTY; without even the implied warranty of 197fb6a53dSViresh Kumar * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 207fb6a53dSViresh Kumar * GNU General Public License for more details. 217fb6a53dSViresh Kumar * 227fb6a53dSViresh Kumar * You should have received a copy of the GNU General Public License 237fb6a53dSViresh Kumar * along with this program; if not, write to the Free Software 247fb6a53dSViresh Kumar * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 257fb6a53dSViresh Kumar * 267fb6a53dSViresh Kumar */ 277fb6a53dSViresh Kumar 287fb6a53dSViresh Kumar #include <linux/cpufreq.h> 297fb6a53dSViresh Kumar #include <linux/timer.h> 307fb6a53dSViresh Kumar #include <linux/module.h> 315af50730SRob Herring #include <linux/of_address.h> 327fb6a53dSViresh Kumar 337fb6a53dSViresh Kumar #include <asm/hw_irq.h> 347fb6a53dSViresh Kumar #include <asm/io.h> 357fb6a53dSViresh Kumar #include <asm/prom.h> 367fb6a53dSViresh Kumar #include <asm/time.h> 377fb6a53dSViresh Kumar #include <asm/smp.h> 387fb6a53dSViresh Kumar 397fb6a53dSViresh Kumar #define SDCASR_REG 0x0100 407fb6a53dSViresh Kumar #define SDCASR_REG_STRIDE 0x1000 417fb6a53dSViresh Kumar #define SDCPWR_CFGA0_REG 0x0100 427fb6a53dSViresh Kumar #define SDCPWR_PWST0_REG 0x0000 437fb6a53dSViresh Kumar #define SDCPWR_GIZTIME_REG 0x0440 447fb6a53dSViresh Kumar 457fb6a53dSViresh Kumar /* SDCPWR_GIZTIME_REG fields */ 467fb6a53dSViresh Kumar #define SDCPWR_GIZTIME_GR 0x80000000 477fb6a53dSViresh Kumar #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff 487fb6a53dSViresh Kumar 497fb6a53dSViresh Kumar /* Offset of ASR registers from SDC base */ 507fb6a53dSViresh Kumar #define SDCASR_OFFSET 0x120000 517fb6a53dSViresh Kumar 527fb6a53dSViresh Kumar static void __iomem *sdcpwr_mapbase; 537fb6a53dSViresh Kumar static void __iomem *sdcasr_mapbase; 547fb6a53dSViresh Kumar 557fb6a53dSViresh Kumar /* Current astate, is used when waking up from power savings on 567fb6a53dSViresh Kumar * one core, in case the other core has switched states during 577fb6a53dSViresh Kumar * the idle time. 587fb6a53dSViresh Kumar */ 597fb6a53dSViresh Kumar static int current_astate; 607fb6a53dSViresh Kumar 617fb6a53dSViresh Kumar /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ 627fb6a53dSViresh Kumar static struct cpufreq_frequency_table pas_freqs[] = { 637f4b0461SViresh Kumar {0, 0, 0}, 647f4b0461SViresh Kumar {0, 1, 0}, 657f4b0461SViresh Kumar {0, 2, 0}, 667f4b0461SViresh Kumar {0, 3, 0}, 677f4b0461SViresh Kumar {0, 4, 0}, 687f4b0461SViresh Kumar {0, 0, CPUFREQ_TABLE_END}, 697fb6a53dSViresh Kumar }; 707fb6a53dSViresh Kumar 717fb6a53dSViresh Kumar /* 727fb6a53dSViresh Kumar * hardware specific functions 737fb6a53dSViresh Kumar */ 747fb6a53dSViresh Kumar 757fb6a53dSViresh Kumar static int get_astate_freq(int astate) 767fb6a53dSViresh Kumar { 777fb6a53dSViresh Kumar u32 ret; 787fb6a53dSViresh Kumar ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); 797fb6a53dSViresh Kumar 807fb6a53dSViresh Kumar return ret & 0x3f; 817fb6a53dSViresh Kumar } 827fb6a53dSViresh Kumar 837fb6a53dSViresh Kumar static int get_cur_astate(int cpu) 847fb6a53dSViresh Kumar { 857fb6a53dSViresh Kumar u32 ret; 867fb6a53dSViresh Kumar 877fb6a53dSViresh Kumar ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); 887fb6a53dSViresh Kumar ret = (ret >> (cpu * 4)) & 0x7; 897fb6a53dSViresh Kumar 907fb6a53dSViresh Kumar return ret; 917fb6a53dSViresh Kumar } 927fb6a53dSViresh Kumar 937fb6a53dSViresh Kumar static int get_gizmo_latency(void) 947fb6a53dSViresh Kumar { 957fb6a53dSViresh Kumar u32 giztime, ret; 967fb6a53dSViresh Kumar 977fb6a53dSViresh Kumar giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); 987fb6a53dSViresh Kumar 997fb6a53dSViresh Kumar /* just provide the upper bound */ 1007fb6a53dSViresh Kumar if (giztime & SDCPWR_GIZTIME_GR) 1017fb6a53dSViresh Kumar ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; 1027fb6a53dSViresh Kumar else 1037fb6a53dSViresh Kumar ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; 1047fb6a53dSViresh Kumar 1057fb6a53dSViresh Kumar return ret; 1067fb6a53dSViresh Kumar } 1077fb6a53dSViresh Kumar 1087fb6a53dSViresh Kumar static void set_astate(int cpu, unsigned int astate) 1097fb6a53dSViresh Kumar { 1107fb6a53dSViresh Kumar unsigned long flags; 1117fb6a53dSViresh Kumar 1127fb6a53dSViresh Kumar /* Return if called before init has run */ 1137fb6a53dSViresh Kumar if (unlikely(!sdcasr_mapbase)) 1147fb6a53dSViresh Kumar return; 1157fb6a53dSViresh Kumar 1167fb6a53dSViresh Kumar local_irq_save(flags); 1177fb6a53dSViresh Kumar 1187fb6a53dSViresh Kumar out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); 1197fb6a53dSViresh Kumar 1207fb6a53dSViresh Kumar local_irq_restore(flags); 1217fb6a53dSViresh Kumar } 1227fb6a53dSViresh Kumar 1237fb6a53dSViresh Kumar int check_astate(void) 1247fb6a53dSViresh Kumar { 1257fb6a53dSViresh Kumar return get_cur_astate(hard_smp_processor_id()); 1267fb6a53dSViresh Kumar } 1277fb6a53dSViresh Kumar 1287fb6a53dSViresh Kumar void restore_astate(int cpu) 1297fb6a53dSViresh Kumar { 1307fb6a53dSViresh Kumar set_astate(cpu, current_astate); 1317fb6a53dSViresh Kumar } 1327fb6a53dSViresh Kumar 1337fb6a53dSViresh Kumar /* 1347fb6a53dSViresh Kumar * cpufreq functions 1357fb6a53dSViresh Kumar */ 1367fb6a53dSViresh Kumar 1377fb6a53dSViresh Kumar static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) 1387fb6a53dSViresh Kumar { 139041526f9SStratos Karafotis struct cpufreq_frequency_table *pos; 1407fb6a53dSViresh Kumar const u32 *max_freqp; 1417fb6a53dSViresh Kumar u32 max_freq; 142041526f9SStratos Karafotis int cur_astate; 1437fb6a53dSViresh Kumar struct resource res; 1447fb6a53dSViresh Kumar struct device_node *cpu, *dn; 1457fb6a53dSViresh Kumar int err = -ENODEV; 1467fb6a53dSViresh Kumar 1477fb6a53dSViresh Kumar cpu = of_get_cpu_node(policy->cpu, NULL); 1487fb6a53dSViresh Kumar 1497fb6a53dSViresh Kumar if (!cpu) 1507fb6a53dSViresh Kumar goto out; 1517fb6a53dSViresh Kumar 1527fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, "1682m-sdc"); 1537fb6a53dSViresh Kumar if (!dn) 1547fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, 1557fb6a53dSViresh Kumar "pasemi,pwrficient-sdc"); 1567fb6a53dSViresh Kumar if (!dn) 1577fb6a53dSViresh Kumar goto out; 1587fb6a53dSViresh Kumar err = of_address_to_resource(dn, 0, &res); 1597fb6a53dSViresh Kumar of_node_put(dn); 1607fb6a53dSViresh Kumar if (err) 1617fb6a53dSViresh Kumar goto out; 1627fb6a53dSViresh Kumar sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000); 1637fb6a53dSViresh Kumar if (!sdcasr_mapbase) { 1647fb6a53dSViresh Kumar err = -EINVAL; 1657fb6a53dSViresh Kumar goto out; 1667fb6a53dSViresh Kumar } 1677fb6a53dSViresh Kumar 1687fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo"); 1697fb6a53dSViresh Kumar if (!dn) 1707fb6a53dSViresh Kumar dn = of_find_compatible_node(NULL, NULL, 1717fb6a53dSViresh Kumar "pasemi,pwrficient-gizmo"); 1727fb6a53dSViresh Kumar if (!dn) { 1737fb6a53dSViresh Kumar err = -ENODEV; 1747fb6a53dSViresh Kumar goto out_unmap_sdcasr; 1757fb6a53dSViresh Kumar } 1767fb6a53dSViresh Kumar err = of_address_to_resource(dn, 0, &res); 1777fb6a53dSViresh Kumar of_node_put(dn); 1787fb6a53dSViresh Kumar if (err) 1797fb6a53dSViresh Kumar goto out_unmap_sdcasr; 1807fb6a53dSViresh Kumar sdcpwr_mapbase = ioremap(res.start, 0x1000); 1817fb6a53dSViresh Kumar if (!sdcpwr_mapbase) { 1827fb6a53dSViresh Kumar err = -EINVAL; 1837fb6a53dSViresh Kumar goto out_unmap_sdcasr; 1847fb6a53dSViresh Kumar } 1857fb6a53dSViresh Kumar 1867fb6a53dSViresh Kumar pr_debug("init cpufreq on CPU %d\n", policy->cpu); 1877fb6a53dSViresh Kumar 1887fb6a53dSViresh Kumar max_freqp = of_get_property(cpu, "clock-frequency", NULL); 1897fb6a53dSViresh Kumar if (!max_freqp) { 1907fb6a53dSViresh Kumar err = -EINVAL; 1917fb6a53dSViresh Kumar goto out_unmap_sdcpwr; 1927fb6a53dSViresh Kumar } 1937fb6a53dSViresh Kumar 1947fb6a53dSViresh Kumar /* we need the freq in kHz */ 1957fb6a53dSViresh Kumar max_freq = *max_freqp / 1000; 1967fb6a53dSViresh Kumar 1977fb6a53dSViresh Kumar pr_debug("max clock-frequency is at %u kHz\n", max_freq); 1987fb6a53dSViresh Kumar pr_debug("initializing frequency table\n"); 1997fb6a53dSViresh Kumar 2007fb6a53dSViresh Kumar /* initialize frequency table */ 201041526f9SStratos Karafotis cpufreq_for_each_entry(pos, pas_freqs) { 202041526f9SStratos Karafotis pos->frequency = get_astate_freq(pos->driver_data) * 100000; 203041526f9SStratos Karafotis pr_debug("%d: %d\n", (int)(pos - pas_freqs), pos->frequency); 2047fb6a53dSViresh Kumar } 2057fb6a53dSViresh Kumar 2067fb6a53dSViresh Kumar cur_astate = get_cur_astate(policy->cpu); 2077fb6a53dSViresh Kumar pr_debug("current astate is at %d\n",cur_astate); 2087fb6a53dSViresh Kumar 2097fb6a53dSViresh Kumar policy->cur = pas_freqs[cur_astate].frequency; 2107fb6a53dSViresh Kumar ppc_proc_freq = policy->cur * 1000ul; 2117fb6a53dSViresh Kumar 212e315bb73SViresh Kumar return cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency()); 2137fb6a53dSViresh Kumar 2147fb6a53dSViresh Kumar out_unmap_sdcpwr: 2157fb6a53dSViresh Kumar iounmap(sdcpwr_mapbase); 2167fb6a53dSViresh Kumar 2177fb6a53dSViresh Kumar out_unmap_sdcasr: 2187fb6a53dSViresh Kumar iounmap(sdcasr_mapbase); 2197fb6a53dSViresh Kumar out: 2207fb6a53dSViresh Kumar return err; 2217fb6a53dSViresh Kumar } 2227fb6a53dSViresh Kumar 2237fb6a53dSViresh Kumar static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) 2247fb6a53dSViresh Kumar { 2257fb6a53dSViresh Kumar /* 2267fb6a53dSViresh Kumar * We don't support CPU hotplug. Don't unmap after the system 2277fb6a53dSViresh Kumar * has already made it to a running state. 2287fb6a53dSViresh Kumar */ 2297fb6a53dSViresh Kumar if (system_state != SYSTEM_BOOTING) 2307fb6a53dSViresh Kumar return 0; 2317fb6a53dSViresh Kumar 2327fb6a53dSViresh Kumar if (sdcasr_mapbase) 2337fb6a53dSViresh Kumar iounmap(sdcasr_mapbase); 2347fb6a53dSViresh Kumar if (sdcpwr_mapbase) 2357fb6a53dSViresh Kumar iounmap(sdcpwr_mapbase); 2367fb6a53dSViresh Kumar 2377fb6a53dSViresh Kumar return 0; 2387fb6a53dSViresh Kumar } 2397fb6a53dSViresh Kumar 2407fb6a53dSViresh Kumar static int pas_cpufreq_target(struct cpufreq_policy *policy, 2419c0ebcf7SViresh Kumar unsigned int pas_astate_new) 2427fb6a53dSViresh Kumar { 2437fb6a53dSViresh Kumar int i; 2447fb6a53dSViresh Kumar 2457fb6a53dSViresh Kumar pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", 2467fb6a53dSViresh Kumar policy->cpu, 2477fb6a53dSViresh Kumar pas_freqs[pas_astate_new].frequency, 2487fb6a53dSViresh Kumar pas_freqs[pas_astate_new].driver_data); 2497fb6a53dSViresh Kumar 2507fb6a53dSViresh Kumar current_astate = pas_astate_new; 2517fb6a53dSViresh Kumar 2527fb6a53dSViresh Kumar for_each_online_cpu(i) 2537fb6a53dSViresh Kumar set_astate(i, pas_astate_new); 2547fb6a53dSViresh Kumar 255d4019f0aSViresh Kumar ppc_proc_freq = pas_freqs[pas_astate_new].frequency * 1000ul; 2567fb6a53dSViresh Kumar return 0; 2577fb6a53dSViresh Kumar } 2587fb6a53dSViresh Kumar 2597fb6a53dSViresh Kumar static struct cpufreq_driver pas_cpufreq_driver = { 2607fb6a53dSViresh Kumar .name = "pas-cpufreq", 2617fb6a53dSViresh Kumar .flags = CPUFREQ_CONST_LOOPS, 2627fb6a53dSViresh Kumar .init = pas_cpufreq_cpu_init, 2637fb6a53dSViresh Kumar .exit = pas_cpufreq_cpu_exit, 26457174310SViresh Kumar .verify = cpufreq_generic_frequency_table_verify, 2659c0ebcf7SViresh Kumar .target_index = pas_cpufreq_target, 26657174310SViresh Kumar .attr = cpufreq_generic_attr, 2677fb6a53dSViresh Kumar }; 2687fb6a53dSViresh Kumar 2697fb6a53dSViresh Kumar /* 2707fb6a53dSViresh Kumar * module init and destoy 2717fb6a53dSViresh Kumar */ 2727fb6a53dSViresh Kumar 2737fb6a53dSViresh Kumar static int __init pas_cpufreq_init(void) 2747fb6a53dSViresh Kumar { 2757fb6a53dSViresh Kumar if (!of_machine_is_compatible("PA6T-1682M") && 2767fb6a53dSViresh Kumar !of_machine_is_compatible("pasemi,pwrficient")) 2777fb6a53dSViresh Kumar return -ENODEV; 2787fb6a53dSViresh Kumar 2797fb6a53dSViresh Kumar return cpufreq_register_driver(&pas_cpufreq_driver); 2807fb6a53dSViresh Kumar } 2817fb6a53dSViresh Kumar 2827fb6a53dSViresh Kumar static void __exit pas_cpufreq_exit(void) 2837fb6a53dSViresh Kumar { 2847fb6a53dSViresh Kumar cpufreq_unregister_driver(&pas_cpufreq_driver); 2857fb6a53dSViresh Kumar } 2867fb6a53dSViresh Kumar 2877fb6a53dSViresh Kumar module_init(pas_cpufreq_init); 2887fb6a53dSViresh Kumar module_exit(pas_cpufreq_exit); 2897fb6a53dSViresh Kumar 2907fb6a53dSViresh Kumar MODULE_LICENSE("GPL"); 2917fb6a53dSViresh Kumar MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>"); 292