Path: blob/master/arch/powerpc/platforms/pasemi/cpufreq.c
10819 views
/*1* Copyright (C) 2007 PA Semi, Inc2*3* Authors: Egor Martovetsky <[email protected]>4* Olof Johansson <[email protected]>5*6* Maintained by: Olof Johansson <[email protected]>7*8* Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:9* (C) Copyright IBM Deutschland Entwicklung GmbH 200510*11* This program is free software; you can redistribute it and/or modify12* it under the terms of the GNU General Public License as published by13* the Free Software Foundation; either version 2, or (at your option)14* any later version.15*16* This program is distributed in the hope that it will be useful,17* but WITHOUT ANY WARRANTY; without even the implied warranty of18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the19* GNU General Public License for more details.20*21* You should have received a copy of the GNU General Public License22* along with this program; if not, write to the Free Software23* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.24*25*/2627#include <linux/cpufreq.h>28#include <linux/timer.h>2930#include <asm/hw_irq.h>31#include <asm/io.h>32#include <asm/prom.h>33#include <asm/time.h>34#include <asm/smp.h>3536#define SDCASR_REG 0x010037#define SDCASR_REG_STRIDE 0x100038#define SDCPWR_CFGA0_REG 0x010039#define SDCPWR_PWST0_REG 0x000040#define SDCPWR_GIZTIME_REG 0x04404142/* SDCPWR_GIZTIME_REG fields */43#define SDCPWR_GIZTIME_GR 0x8000000044#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff4546/* Offset of ASR registers from SDC base */47#define SDCASR_OFFSET 0x1200004849static void __iomem *sdcpwr_mapbase;50static void __iomem *sdcasr_mapbase;5152static DEFINE_MUTEX(pas_switch_mutex);5354/* Current astate, is used when waking up from power savings on55* one core, in case the other core has switched states during56* the idle time.57*/58static int current_astate;5960/* 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};6970static struct freq_attr *pas_cpu_freqs_attr[] = {71&cpufreq_freq_attr_scaling_available_freqs,72NULL,73};7475/*76* hardware specific functions77*/7879static int get_astate_freq(int astate)80{81u32 ret;82ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));8384return ret & 0x3f;85}8687static int get_cur_astate(int cpu)88{89u32 ret;9091ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);92ret = (ret >> (cpu * 4)) & 0x7;9394return ret;95}9697static int get_gizmo_latency(void)98{99u32 giztime, ret;100101giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);102103/* just provide the upper bound */104if (giztime & SDCPWR_GIZTIME_GR)105ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;106else107ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;108109return ret;110}111112static void set_astate(int cpu, unsigned int astate)113{114unsigned long flags;115116/* Return if called before init has run */117if (unlikely(!sdcasr_mapbase))118return;119120local_irq_save(flags);121122out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);123124local_irq_restore(flags);125}126127int check_astate(void)128{129return get_cur_astate(hard_smp_processor_id());130}131132void restore_astate(int cpu)133{134set_astate(cpu, current_astate);135}136137/*138* cpufreq functions139*/140141static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)142{143const u32 *max_freqp;144u32 max_freq;145int i, cur_astate;146struct resource res;147struct device_node *cpu, *dn;148int err = -ENODEV;149150cpu = of_get_cpu_node(policy->cpu, NULL);151152if (!cpu)153goto out;154155dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");156if (!dn)157dn = of_find_compatible_node(NULL, NULL,158"pasemi,pwrficient-sdc");159if (!dn)160goto out;161err = of_address_to_resource(dn, 0, &res);162of_node_put(dn);163if (err)164goto out;165sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);166if (!sdcasr_mapbase) {167err = -EINVAL;168goto out;169}170171dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo");172if (!dn)173dn = of_find_compatible_node(NULL, NULL,174"pasemi,pwrficient-gizmo");175if (!dn) {176err = -ENODEV;177goto out_unmap_sdcasr;178}179err = of_address_to_resource(dn, 0, &res);180of_node_put(dn);181if (err)182goto out_unmap_sdcasr;183sdcpwr_mapbase = ioremap(res.start, 0x1000);184if (!sdcpwr_mapbase) {185err = -EINVAL;186goto out_unmap_sdcasr;187}188189pr_debug("init cpufreq on CPU %d\n", policy->cpu);190191max_freqp = of_get_property(cpu, "clock-frequency", NULL);192if (!max_freqp) {193err = -EINVAL;194goto out_unmap_sdcpwr;195}196197/* we need the freq in kHz */198max_freq = *max_freqp / 1000;199200pr_debug("max clock-frequency is at %u kHz\n", max_freq);201pr_debug("initializing frequency table\n");202203/* initialize frequency table */204for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {205pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;206pr_debug("%d: %d\n", i, pas_freqs[i].frequency);207}208209policy->cpuinfo.transition_latency = get_gizmo_latency();210211cur_astate = get_cur_astate(policy->cpu);212pr_debug("current astate is at %d\n",cur_astate);213214policy->cur = pas_freqs[cur_astate].frequency;215cpumask_copy(policy->cpus, cpu_online_mask);216217ppc_proc_freq = policy->cur * 1000ul;218219cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);220221/* this ensures that policy->cpuinfo_min and policy->cpuinfo_max222* are set correctly223*/224return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);225226out_unmap_sdcpwr:227iounmap(sdcpwr_mapbase);228229out_unmap_sdcasr:230iounmap(sdcasr_mapbase);231out:232return err;233}234235static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)236{237if (sdcasr_mapbase)238iounmap(sdcasr_mapbase);239if (sdcpwr_mapbase)240iounmap(sdcpwr_mapbase);241242cpufreq_frequency_table_put_attr(policy->cpu);243return 0;244}245246static int pas_cpufreq_verify(struct cpufreq_policy *policy)247{248return cpufreq_frequency_table_verify(policy, pas_freqs);249}250251static int pas_cpufreq_target(struct cpufreq_policy *policy,252unsigned int target_freq,253unsigned int relation)254{255struct cpufreq_freqs freqs;256int pas_astate_new;257int i;258259cpufreq_frequency_table_target(policy,260pas_freqs,261target_freq,262relation,263&pas_astate_new);264265freqs.old = policy->cur;266freqs.new = pas_freqs[pas_astate_new].frequency;267freqs.cpu = policy->cpu;268269mutex_lock(&pas_switch_mutex);270cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);271272pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",273policy->cpu,274pas_freqs[pas_astate_new].frequency,275pas_freqs[pas_astate_new].index);276277current_astate = pas_astate_new;278279for_each_online_cpu(i)280set_astate(i, pas_astate_new);281282cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);283mutex_unlock(&pas_switch_mutex);284285ppc_proc_freq = freqs.new * 1000ul;286return 0;287}288289static struct cpufreq_driver pas_cpufreq_driver = {290.name = "pas-cpufreq",291.owner = THIS_MODULE,292.flags = CPUFREQ_CONST_LOOPS,293.init = pas_cpufreq_cpu_init,294.exit = pas_cpufreq_cpu_exit,295.verify = pas_cpufreq_verify,296.target = pas_cpufreq_target,297.attr = pas_cpu_freqs_attr,298};299300/*301* module init and destoy302*/303304static int __init pas_cpufreq_init(void)305{306if (!of_machine_is_compatible("PA6T-1682M") &&307!of_machine_is_compatible("pasemi,pwrficient"))308return -ENODEV;309310return cpufreq_register_driver(&pas_cpufreq_driver);311}312313static void __exit pas_cpufreq_exit(void)314{315cpufreq_unregister_driver(&pas_cpufreq_driver);316}317318module_init(pas_cpufreq_init);319module_exit(pas_cpufreq_exit);320321MODULE_LICENSE("GPL");322MODULE_AUTHOR("Egor Martovetsky <[email protected]>, Olof Johansson <[email protected]>");323324325