Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2007 PA Semi, Inc
0004  *
0005  * Authors: Egor Martovetsky <egor@pasemi.com>
0006  *      Olof Johansson <olof@lixom.net>
0007  *
0008  * Maintained by: Olof Johansson <olof@lixom.net>
0009  *
0010  * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
0011  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
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 /* SDCPWR_GIZTIME_REG fields */
0033 #define SDCPWR_GIZTIME_GR   0x80000000
0034 #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
0035 
0036 /* Offset of ASR registers from SDC base */
0037 #define SDCASR_OFFSET       0x120000
0038 
0039 static void __iomem *sdcpwr_mapbase;
0040 static void __iomem *sdcasr_mapbase;
0041 
0042 /* Current astate, is used when waking up from power savings on
0043  * one core, in case the other core has switched states during
0044  * the idle time.
0045  */
0046 static int current_astate;
0047 
0048 /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
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  * hardware specific functions
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     /* just provide the upper bound */
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     /* Return if called before init has run */
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  * cpufreq functions
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     /* we need the freq in kHz */
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     /* initialize frequency table */
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      * We don't support CPU hotplug. Don't unmap after the system
0211      * has already made it to a running state.
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  * module init and destoy
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>");