1/* 2 * Copyright (C) 2007 PA Semi, Inc 3 * 4 * Authors: Egor Martovetsky <egor@pasemi.com> 5 * Olof Johansson <olof@lixom.net> 6 * 7 * Maintained by: Olof Johansson <olof@lixom.net> 8 * 9 * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: 10 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2, or (at your option) 15 * any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 * 26 */ 27 28#include <linux/cpufreq.h> 29#include <linux/timer.h> 30 31#include <asm/hw_irq.h> 32#include <asm/io.h> 33#include <asm/prom.h> 34#include <asm/time.h> 35 36#define SDCASR_REG 0x0100 37#define SDCASR_REG_STRIDE 0x1000 38#define SDCPWR_CFGA0_REG 0x0100 39#define SDCPWR_PWST0_REG 0x0000 40#define SDCPWR_GIZTIME_REG 0x0440 41 42/* SDCPWR_GIZTIME_REG fields */ 43#define SDCPWR_GIZTIME_GR 0x80000000 44#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff 45 46/* Offset of ASR registers from SDC base */ 47#define SDCASR_OFFSET 0x120000 48 49static void __iomem *sdcpwr_mapbase; 50static void __iomem *sdcasr_mapbase; 51 52static DEFINE_MUTEX(pas_switch_mutex); 53 54/* Current astate, is used when waking up from power savings on 55 * one core, in case the other core has switched states during 56 * the idle time. 57 */ 58static int current_astate; 59 60/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ 61static struct cpufreq_frequency_table pas_freqs[] = { 62 {0, 0}, 63 {1, 0}, 64 {2, 0}, 65 {3, 0}, 66 {4, 0}, 67 {0, CPUFREQ_TABLE_END}, 68}; 69 70static struct freq_attr *pas_cpu_freqs_attr[] = { 71 &cpufreq_freq_attr_scaling_available_freqs, 72 NULL, 73}; 74 75/* 76 * hardware specific functions 77 */ 78 79static int get_astate_freq(int astate) 80{ 81 u32 ret; 82 ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); 83 84 return ret & 0x3f; 85} 86 87static int get_cur_astate(int cpu) 88{ 89 u32 ret; 90 91 ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); 92 ret = (ret >> (cpu * 4)) & 0x7; 93 94 return ret; 95} 96 97static int get_gizmo_latency(void) 98{ 99 u32 giztime, ret; 100 101 giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); 102 103 /* just provide the upper bound */ 104 if (giztime & SDCPWR_GIZTIME_GR) 105 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; 106 else 107 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; 108 109 return ret; 110} 111 112static void set_astate(int cpu, unsigned int astate) 113{ 114 u64 flags; 115 116 /* Return if called before init has run */ 117 if (unlikely(!sdcasr_mapbase)) 118 return; 119 120 local_irq_save(flags); 121 122 out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); 123 124 local_irq_restore(flags); 125} 126 127void restore_astate(int cpu) 128{ 129 set_astate(cpu, current_astate); 130} 131 132/* 133 * cpufreq functions 134 */ 135 136static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) 137{ 138 const u32 *max_freqp; 139 u32 max_freq; 140 int i, cur_astate; 141 struct resource res; 142 struct device_node *cpu, *dn; 143 int err = -ENODEV; 144 145 cpu = of_get_cpu_node(policy->cpu, NULL); 146 147 if (!cpu) 148 goto out; 149 150 dn = of_find_compatible_node(NULL, "sdc", "1682m-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, "gizmo", "1682m-gizmo"); 164 if (!dn) { 165 err = -ENODEV; 166 goto out_unmap_sdcasr; 167 } 168 err = of_address_to_resource(dn, 0, &res); 169 of_node_put(dn); 170 if (err) 171 goto out_unmap_sdcasr; 172 sdcpwr_mapbase = ioremap(res.start, 0x1000); 173 if (!sdcpwr_mapbase) { 174 err = -EINVAL; 175 goto out_unmap_sdcasr; 176 } 177 178 pr_debug("init cpufreq on CPU %d\n", policy->cpu); 179 180 max_freqp = of_get_property(cpu, "clock-frequency", NULL); 181 if (!max_freqp) { 182 err = -EINVAL; 183 goto out_unmap_sdcpwr; 184 } 185 186 /* we need the freq in kHz */ 187 max_freq = *max_freqp / 1000; 188 189 pr_debug("max clock-frequency is at %u kHz\n", max_freq); 190 pr_debug("initializing frequency table\n"); 191 192 /* initialize frequency table */ 193 for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { 194 pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000; 195 pr_debug("%d: %d\n", i, pas_freqs[i].frequency); 196 } 197 198 policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 199 200 policy->cpuinfo.transition_latency = get_gizmo_latency(); 201 202 cur_astate = get_cur_astate(policy->cpu); 203 pr_debug("current astate is at %d\n",cur_astate); 204 205 policy->cur = pas_freqs[cur_astate].frequency; 206 policy->cpus = cpu_online_map; 207 208 ppc_proc_freq = policy->cur * 1000ul; 209 210 cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu); 211 212 /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max 213 * are set correctly 214 */ 215 return cpufreq_frequency_table_cpuinfo(policy, pas_freqs); 216 217out_unmap_sdcpwr: 218 iounmap(sdcpwr_mapbase); 219 220out_unmap_sdcasr: 221 iounmap(sdcasr_mapbase); 222out: 223 return err; 224} 225 226static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) 227{ 228 if (sdcasr_mapbase) 229 iounmap(sdcasr_mapbase); 230 if (sdcpwr_mapbase) 231 iounmap(sdcpwr_mapbase); 232 233 cpufreq_frequency_table_put_attr(policy->cpu); 234 return 0; 235} 236 237static int pas_cpufreq_verify(struct cpufreq_policy *policy) 238{ 239 return cpufreq_frequency_table_verify(policy, pas_freqs); 240} 241 242static int pas_cpufreq_target(struct cpufreq_policy *policy, 243 unsigned int target_freq, 244 unsigned int relation) 245{ 246 struct cpufreq_freqs freqs; 247 int pas_astate_new; 248 int i; 249 250 cpufreq_frequency_table_target(policy, 251 pas_freqs, 252 target_freq, 253 relation, 254 &pas_astate_new); 255 256 freqs.old = policy->cur; 257 freqs.new = pas_freqs[pas_astate_new].frequency; 258 freqs.cpu = policy->cpu; 259 260 mutex_lock(&pas_switch_mutex); 261 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 262 263 pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", 264 policy->cpu, 265 pas_freqs[pas_astate_new].frequency, 266 pas_freqs[pas_astate_new].index); 267 268 current_astate = pas_astate_new; 269 270 for_each_online_cpu(i) 271 set_astate(i, pas_astate_new); 272 273 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 274 mutex_unlock(&pas_switch_mutex); 275 276 ppc_proc_freq = freqs.new * 1000ul; 277 return 0; 278} 279 280static struct cpufreq_driver pas_cpufreq_driver = { 281 .name = "pas-cpufreq", 282 .owner = THIS_MODULE, 283 .flags = CPUFREQ_CONST_LOOPS, 284 .init = pas_cpufreq_cpu_init, 285 .exit = pas_cpufreq_cpu_exit, 286 .verify = pas_cpufreq_verify, 287 .target = pas_cpufreq_target, 288 .attr = pas_cpu_freqs_attr, 289}; 290 291/* 292 * module init and destoy 293 */ 294 295static int __init pas_cpufreq_init(void) 296{ 297 if (!machine_is_compatible("PA6T-1682M")) 298 return -ENODEV; 299 300 return cpufreq_register_driver(&pas_cpufreq_driver); 301} 302 303static void __exit pas_cpufreq_exit(void) 304{ 305 cpufreq_unregister_driver(&pas_cpufreq_driver); 306} 307 308module_init(pas_cpufreq_init); 309module_exit(pas_cpufreq_exit); 310 311MODULE_LICENSE("GPL"); 312MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>"); 313