0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <linux/cpufreq.h>
0015 #include <linux/timer.h>
0016 #include <linux/module.h>
0017 #include <linux/of_address.h>
0018
0019 #include <asm/hw_irq.h>
0020 #include <asm/io.h>
0021 #include <asm/time.h>
0022 #include <asm/smp.h>
0023
0024 #include <platforms/pasemi/pasemi.h>
0025
0026 #define SDCASR_REG 0x0100
0027 #define SDCASR_REG_STRIDE 0x1000
0028 #define SDCPWR_CFGA0_REG 0x0100
0029 #define SDCPWR_PWST0_REG 0x0000
0030 #define SDCPWR_GIZTIME_REG 0x0440
0031
0032
0033 #define SDCPWR_GIZTIME_GR 0x80000000
0034 #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
0035
0036
0037 #define SDCASR_OFFSET 0x120000
0038
0039 static void __iomem *sdcpwr_mapbase;
0040 static void __iomem *sdcasr_mapbase;
0041
0042
0043
0044
0045
0046 static int current_astate;
0047
0048
0049 static struct cpufreq_frequency_table pas_freqs[] = {
0050 {0, 0, 0},
0051 {0, 1, 0},
0052 {0, 2, 0},
0053 {0, 3, 0},
0054 {0, 4, 0},
0055 {0, 0, CPUFREQ_TABLE_END},
0056 };
0057
0058
0059
0060
0061
0062 static int get_astate_freq(int astate)
0063 {
0064 u32 ret;
0065 ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
0066
0067 return ret & 0x3f;
0068 }
0069
0070 static int get_cur_astate(int cpu)
0071 {
0072 u32 ret;
0073
0074 ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
0075 ret = (ret >> (cpu * 4)) & 0x7;
0076
0077 return ret;
0078 }
0079
0080 static int get_gizmo_latency(void)
0081 {
0082 u32 giztime, ret;
0083
0084 giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
0085
0086
0087 if (giztime & SDCPWR_GIZTIME_GR)
0088 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
0089 else
0090 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
0091
0092 return ret;
0093 }
0094
0095 static void set_astate(int cpu, unsigned int astate)
0096 {
0097 unsigned long flags;
0098
0099
0100 if (unlikely(!sdcasr_mapbase))
0101 return;
0102
0103 local_irq_save(flags);
0104
0105 out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
0106
0107 local_irq_restore(flags);
0108 }
0109
0110 int check_astate(void)
0111 {
0112 return get_cur_astate(hard_smp_processor_id());
0113 }
0114
0115 void restore_astate(int cpu)
0116 {
0117 set_astate(cpu, current_astate);
0118 }
0119
0120
0121
0122
0123
0124 static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
0125 {
0126 struct cpufreq_frequency_table *pos;
0127 const u32 *max_freqp;
0128 u32 max_freq;
0129 int cur_astate, idx;
0130 struct resource res;
0131 struct device_node *cpu, *dn;
0132 int err = -ENODEV;
0133
0134 cpu = of_get_cpu_node(policy->cpu, NULL);
0135 if (!cpu)
0136 goto out;
0137
0138 max_freqp = of_get_property(cpu, "clock-frequency", NULL);
0139 of_node_put(cpu);
0140 if (!max_freqp) {
0141 err = -EINVAL;
0142 goto out;
0143 }
0144
0145
0146 max_freq = *max_freqp / 1000;
0147
0148 dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
0149 if (!dn)
0150 dn = of_find_compatible_node(NULL, NULL,
0151 "pasemi,pwrficient-sdc");
0152 if (!dn)
0153 goto out;
0154 err = of_address_to_resource(dn, 0, &res);
0155 of_node_put(dn);
0156 if (err)
0157 goto out;
0158 sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
0159 if (!sdcasr_mapbase) {
0160 err = -EINVAL;
0161 goto out;
0162 }
0163
0164 dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo");
0165 if (!dn)
0166 dn = of_find_compatible_node(NULL, NULL,
0167 "pasemi,pwrficient-gizmo");
0168 if (!dn) {
0169 err = -ENODEV;
0170 goto out_unmap_sdcasr;
0171 }
0172 err = of_address_to_resource(dn, 0, &res);
0173 of_node_put(dn);
0174 if (err)
0175 goto out_unmap_sdcasr;
0176 sdcpwr_mapbase = ioremap(res.start, 0x1000);
0177 if (!sdcpwr_mapbase) {
0178 err = -EINVAL;
0179 goto out_unmap_sdcasr;
0180 }
0181
0182 pr_debug("init cpufreq on CPU %d\n", policy->cpu);
0183 pr_debug("max clock-frequency is at %u kHz\n", max_freq);
0184 pr_debug("initializing frequency table\n");
0185
0186
0187 cpufreq_for_each_entry_idx(pos, pas_freqs, idx) {
0188 pos->frequency = get_astate_freq(pos->driver_data) * 100000;
0189 pr_debug("%d: %d\n", idx, pos->frequency);
0190 }
0191
0192 cur_astate = get_cur_astate(policy->cpu);
0193 pr_debug("current astate is at %d\n",cur_astate);
0194
0195 policy->cur = pas_freqs[cur_astate].frequency;
0196 ppc_proc_freq = policy->cur * 1000ul;
0197
0198 cpufreq_generic_init(policy, pas_freqs, get_gizmo_latency());
0199 return 0;
0200
0201 out_unmap_sdcasr:
0202 iounmap(sdcasr_mapbase);
0203 out:
0204 return err;
0205 }
0206
0207 static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
0208 {
0209
0210
0211
0212
0213 if (system_state >= SYSTEM_RUNNING)
0214 return 0;
0215
0216 if (sdcasr_mapbase)
0217 iounmap(sdcasr_mapbase);
0218 if (sdcpwr_mapbase)
0219 iounmap(sdcpwr_mapbase);
0220
0221 return 0;
0222 }
0223
0224 static int pas_cpufreq_target(struct cpufreq_policy *policy,
0225 unsigned int pas_astate_new)
0226 {
0227 int i;
0228
0229 pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
0230 policy->cpu,
0231 pas_freqs[pas_astate_new].frequency,
0232 pas_freqs[pas_astate_new].driver_data);
0233
0234 current_astate = pas_astate_new;
0235
0236 for_each_online_cpu(i)
0237 set_astate(i, pas_astate_new);
0238
0239 ppc_proc_freq = pas_freqs[pas_astate_new].frequency * 1000ul;
0240 return 0;
0241 }
0242
0243 static struct cpufreq_driver pas_cpufreq_driver = {
0244 .name = "pas-cpufreq",
0245 .flags = CPUFREQ_CONST_LOOPS,
0246 .init = pas_cpufreq_cpu_init,
0247 .exit = pas_cpufreq_cpu_exit,
0248 .verify = cpufreq_generic_frequency_table_verify,
0249 .target_index = pas_cpufreq_target,
0250 .attr = cpufreq_generic_attr,
0251 };
0252
0253
0254
0255
0256
0257 static int __init pas_cpufreq_init(void)
0258 {
0259 if (!of_machine_is_compatible("PA6T-1682M") &&
0260 !of_machine_is_compatible("pasemi,pwrficient"))
0261 return -ENODEV;
0262
0263 return cpufreq_register_driver(&pas_cpufreq_driver);
0264 }
0265
0266 static void __exit pas_cpufreq_exit(void)
0267 {
0268 cpufreq_unregister_driver(&pas_cpufreq_driver);
0269 }
0270
0271 module_init(pas_cpufreq_init);
0272 module_exit(pas_cpufreq_exit);
0273
0274 MODULE_LICENSE("GPL");
0275 MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");