1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2007 PA Semi, Inc 4 * 5 * Authors: Egor Martovetsky <egor@pasemi.com> 6 * Olof Johansson <olof@lixom.net> 7 * 8 * Maintained by: Olof Johansson <olof@lixom.net> 9 * 10 * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: 11 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 12 */ 13 14 #include <linux/cpufreq.h> 15 #include <linux/timer.h> 16 #include <linux/module.h> 17 #include <linux/of_address.h> 18 19 #include <asm/hw_irq.h> 20 #include <asm/io.h> 21 #include <asm/prom.h> 22 #include <asm/time.h> 23 #include <asm/smp.h> 24 25 #define SDCASR_REG 0x0100 26 #define SDCASR_REG_STRIDE 0x1000 27 #define SDCPWR_CFGA0_REG 0x0100 28 #define SDCPWR_PWST0_REG 0x0000 29 #define SDCPWR_GIZTIME_REG 0x0440 30 31 /* SDCPWR_GIZTIME_REG fields */ 32 #define SDCPWR_GIZTIME_GR 0x80000000 33 #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff 34 35 /* Offset of ASR registers from SDC base */ 36 #define SDCASR_OFFSET 0x120000 37 38 static void __iomem *sdcpwr_mapbase; 39 static void __iomem *sdcasr_mapbase; 40 41 /* Current astate, is used when waking up from power savings on 42 * one core, in case the other core has switched states during 43 * the idle time. 44 */ 45 static int current_astate; 46 47 /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ 48 static struct cpufreq_frequency_table pas_freqs[] = { 49 {0, 0, 0}, 50 {0, 1, 0}, 51 {0, 2, 0}, 52 {0, 3, 0}, 53 {0, 4, 0}, 54 {0, 0, CPUFREQ_TABLE_END}, 55 }; 56 57 /* 58 * hardware specific functions 59 */ 60 61 static int get_astate_freq(int astate) 62 { 63 u32 ret; 64 ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); 65 66 return ret & 0x3f; 67 } 68 69 static int get_cur_astate(int cpu) 70 { 71 u32 ret; 72 73 ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); 74 ret = (ret >> (cpu * 4)) & 0x7; 75 76 return ret; 77 } 78 79 static int get_gizmo_latency(void) 80 { 81 u32 giztime, ret; 82 83 giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); 84 85 /* just provide the upper bound */ 86 if (giztime & SDCPWR_GIZTIME_GR) 87 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; 88 else 89 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; 90 91 return ret; 92 } 93 94 static void set_astate(int cpu, unsigned int astate) 95 { 96 unsigned long flags; 97 98 /* Return if called before init has run */ 99 if (unlikely(!sdcasr_mapbase)) 100 return; 101 102 local_irq_save(flags); 103 104 out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); 105 106 local_irq_restore(flags); 107 } 108 109 int check_astate(void) 110 { 111 return get_cur_astate(hard_smp_processor_id()); 112 } 113 114 void restore_astate(int cpu) 115 { 116 set_astate(cpu, current_astate); 117 } 118 119 /* 120 * cpufreq functions 121 */ 122 123 static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) 124 { 125 struct cpufreq_frequency_table *pos; 126 const u32 *max_freqp; 127 u32 max_freq; 128 int cur_astate, idx; 129 struct resource res; 130 struct device_node *cpu, *dn; 131 int err = -ENODEV; 132 133 cpu = of_get_cpu_node(policy->cpu, NULL); 134 if (!cpu) 135 goto out; 136 137 max_freqp = of_get_property(cpu, "clock-frequency", NULL); 138 of_node_put(cpu); 139 if (!max_freqp) { 140 err = -EINVAL; 141 goto out; 142 } 143 144 /* we need the freq in kHz */ 145 max_freq = *max_freqp / 1000; 146 147 dn = of_find_compatible_node(NULL, NULL, "1682m-sdc"); 148 if (!dn) 149 dn = of_find_compatible_node(NULL, NULL, 150 "pasemi,pwrficient-sdc"); 151 if (!dn) 152 goto out; 153 err = of_address_to_resource(dn, 0, &res); 154 of_node_put(dn); 155 if (err) 156 goto out; 157 sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000); 158 if (!sdcasr_mapbase) { 159 err = -EINVAL; 160 goto out; 161 } 162 163 dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo"); 164 if (!dn) 165 dn = of_find_compatible_node(NULL, NULL, 166 "pasemi,pwrficient-gizmo"); 167 if (!dn) { 168 err = -ENODEV; 169 goto out_unmap_sdcasr; 170 } 171 err = of_address_to_resource(dn, 0, &res); 172 of_node_put(dn); 173 if (err) 174 goto out_unmap_sdcasr; 175 sdcpwr_mapbase = ioremap(res.start, 0x1000); 176 if (!sdcpwr_mapbase) { 177 err = -EINVAL; 178 goto out_unmap_sdcasr; 179 } 180 181 pr_debug("init cpufreq on CPU %d\n", policy->cpu); 182 pr_debug("max clock-frequency is at %u kHz\n", max_freq); 183 pr_debug("initializing frequency table\n"); 184 185 /* initialize frequency table */ 186 cpufreq_for_each_entry_idx(pos, pas_freqs, idx) { 187 pos->frequency = get_astate_freq(pos->driver_data) * 100000; 188 pr_debug("%d: %d\n", idx, pos->frequency); 189 } 190 191 cur_astate = get_cur_astate(policy->cpu); 192 pr_debug("current astate is at %d\n",cur_astate); 193 194 policy->cur = pas_freqs[cur_astate].frequency; 195 ppc_proc_freq = policy->cur * 1000ul; 196 197 cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency()); 198 return 0; 199 200 out_unmap_sdcasr: 201 iounmap(sdcasr_mapbase); 202 out: 203 return err; 204 } 205 206 static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) 207 { 208 /* 209 * We don't support CPU hotplug. Don't unmap after the system 210 * has already made it to a running state. 211 */ 212 if (system_state >= SYSTEM_RUNNING) 213 return 0; 214 215 if (sdcasr_mapbase) 216 iounmap(sdcasr_mapbase); 217 if (sdcpwr_mapbase) 218 iounmap(sdcpwr_mapbase); 219 220 return 0; 221 } 222 223 static int pas_cpufreq_target(struct cpufreq_policy *policy, 224 unsigned int pas_astate_new) 225 { 226 int i; 227 228 pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", 229 policy->cpu, 230 pas_freqs[pas_astate_new].frequency, 231 pas_freqs[pas_astate_new].driver_data); 232 233 current_astate = pas_astate_new; 234 235 for_each_online_cpu(i) 236 set_astate(i, pas_astate_new); 237 238 ppc_proc_freq = pas_freqs[pas_astate_new].frequency * 1000ul; 239 return 0; 240 } 241 242 static struct cpufreq_driver pas_cpufreq_driver = { 243 .name = "pas-cpufreq", 244 .flags = CPUFREQ_CONST_LOOPS, 245 .init = pas_cpufreq_cpu_init, 246 .exit = pas_cpufreq_cpu_exit, 247 .verify = cpufreq_generic_frequency_table_verify, 248 .target_index = pas_cpufreq_target, 249 .attr = cpufreq_generic_attr, 250 }; 251 252 /* 253 * module init and destoy 254 */ 255 256 static int __init pas_cpufreq_init(void) 257 { 258 if (!of_machine_is_compatible("PA6T-1682M") && 259 !of_machine_is_compatible("pasemi,pwrficient")) 260 return -ENODEV; 261 262 return cpufreq_register_driver(&pas_cpufreq_driver); 263 } 264 265 static void __exit pas_cpufreq_exit(void) 266 { 267 cpufreq_unregister_driver(&pas_cpufreq_driver); 268 } 269 270 module_init(pas_cpufreq_init); 271 module_exit(pas_cpufreq_exit); 272 273 MODULE_LICENSE("GPL"); 274 MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>"); 275