2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2001 - 2002 Deep Blue Solutions Ltd .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* CPU support functions
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/cpufreq.h>
# include <linux/sched.h>
# include <linux/smp.h>
# include <linux/init.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2013-06-18 00:52:32 +04:00
# include <linux/platform_device.h>
# include <linux/of.h>
# include <linux/of_address.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach-types.h>
2010-01-16 23:16:10 +03:00
# include <asm/hardware/icst.h>
2005-04-17 02:20:36 +04:00
2013-06-18 00:52:32 +04:00
static void __iomem * cm_base ;
/* The cpufreq driver only use the OSC register */
# define INTEGRATOR_HDR_OSC_OFFSET 0x08
# define INTEGRATOR_HDR_LOCK_OFFSET 0x14
2005-04-17 02:20:36 +04:00
2013-06-18 00:52:32 +04:00
static struct cpufreq_driver integrator_driver ;
2005-04-17 02:20:36 +04:00
2010-01-16 19:27:28 +03:00
static const struct icst_params lclk_params = {
2010-01-16 20:28:44 +03:00
. ref = 24000000 ,
2010-01-16 21:08:47 +03:00
. vco_max = ICST525_VCO_MAX_5V ,
2010-01-16 22:49:39 +03:00
. vco_min = ICST525_VCO_MIN ,
2005-04-17 02:20:36 +04:00
. vd_min = 8 ,
. vd_max = 132 ,
. rd_min = 24 ,
. rd_max = 24 ,
2010-01-16 22:46:19 +03:00
. s2div = icst525_s2div ,
. idx2s = icst525_idx2s ,
2005-04-17 02:20:36 +04:00
} ;
2010-01-16 19:27:28 +03:00
static const struct icst_params cclk_params = {
2010-01-16 20:28:44 +03:00
. ref = 24000000 ,
2010-01-16 21:08:47 +03:00
. vco_max = ICST525_VCO_MAX_5V ,
2010-01-16 22:49:39 +03:00
. vco_min = ICST525_VCO_MIN ,
2005-04-17 02:20:36 +04:00
. vd_min = 12 ,
. vd_max = 160 ,
. rd_min = 24 ,
. rd_max = 24 ,
2010-01-16 22:46:19 +03:00
. s2div = icst525_s2div ,
. idx2s = icst525_idx2s ,
2005-04-17 02:20:36 +04:00
} ;
/*
* Validate the speed policy .
*/
static int integrator_verify_policy ( struct cpufreq_policy * policy )
{
2010-01-16 19:27:28 +03:00
struct icst_vco vco ;
2005-04-17 02:20:36 +04:00
2013-10-02 12:43:19 +04:00
cpufreq_verify_within_cpu_limits ( policy ) ;
2005-04-17 02:20:36 +04:00
2010-01-16 23:16:10 +03:00
vco = icst_hz_to_vco ( & cclk_params , policy - > max * 1000 ) ;
policy - > max = icst_hz ( & cclk_params , vco ) / 1000 ;
2005-04-17 02:20:36 +04:00
2010-01-16 23:16:10 +03:00
vco = icst_hz_to_vco ( & cclk_params , policy - > min * 1000 ) ;
policy - > min = icst_hz ( & cclk_params , vco ) / 1000 ;
2005-04-17 02:20:36 +04:00
2013-10-02 12:43:19 +04:00
cpufreq_verify_within_cpu_limits ( policy ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int integrator_set_target ( struct cpufreq_policy * policy ,
unsigned int target_freq ,
unsigned int relation )
{
cpumask_t cpus_allowed ;
int cpu = policy - > cpu ;
2010-01-16 19:27:28 +03:00
struct icst_vco vco ;
2005-04-17 02:20:36 +04:00
struct cpufreq_freqs freqs ;
u_int cm_osc ;
/*
* Save this threads cpus_allowed mask .
*/
cpus_allowed = current - > cpus_allowed ;
/*
* Bind to the specified CPU . When this call returns ,
* we should be running on the right CPU .
*/
set_cpus_allowed ( current , cpumask_of_cpu ( cpu ) ) ;
BUG_ON ( cpu ! = smp_processor_id ( ) ) ;
/* get current setting */
2013-06-18 00:52:32 +04:00
cm_osc = __raw_readl ( cm_base + INTEGRATOR_HDR_OSC_OFFSET ) ;
2005-04-17 02:20:36 +04:00
if ( machine_is_integrator ( ) ) {
vco . s = ( cm_osc > > 8 ) & 7 ;
} else if ( machine_is_cintegrator ( ) ) {
vco . s = 1 ;
}
vco . v = cm_osc & 255 ;
vco . r = 22 ;
2010-01-16 23:16:10 +03:00
freqs . old = icst_hz ( & cclk_params , vco ) / 1000 ;
2005-04-17 02:20:36 +04:00
2010-01-16 23:16:10 +03:00
/* icst_hz_to_vco rounds down -- so we need the next
2005-04-17 02:20:36 +04:00
* larger freq in case of CPUFREQ_RELATION_L .
*/
if ( relation = = CPUFREQ_RELATION_L )
target_freq + = 999 ;
if ( target_freq > policy - > max )
target_freq = policy - > max ;
2010-01-16 23:16:10 +03:00
vco = icst_hz_to_vco ( & cclk_params , target_freq * 1000 ) ;
freqs . new = icst_hz ( & cclk_params , vco ) / 1000 ;
2005-04-17 02:20:36 +04:00
if ( freqs . old = = freqs . new ) {
set_cpus_allowed ( current , cpus_allowed ) ;
return 0 ;
}
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_PRECHANGE ) ;
2005-04-17 02:20:36 +04:00
2013-06-18 00:52:32 +04:00
cm_osc = __raw_readl ( cm_base + INTEGRATOR_HDR_OSC_OFFSET ) ;
2005-04-17 02:20:36 +04:00
if ( machine_is_integrator ( ) ) {
cm_osc & = 0xfffff800 ;
cm_osc | = vco . s < < 8 ;
} else if ( machine_is_cintegrator ( ) ) {
cm_osc & = 0xffffff00 ;
}
cm_osc | = vco . v ;
2013-06-18 00:52:32 +04:00
__raw_writel ( 0xa05f , cm_base + INTEGRATOR_HDR_LOCK_OFFSET ) ;
__raw_writel ( cm_osc , cm_base + INTEGRATOR_HDR_OSC_OFFSET ) ;
__raw_writel ( 0 , cm_base + INTEGRATOR_HDR_LOCK_OFFSET ) ;
2005-04-17 02:20:36 +04:00
/*
* Restore the CPUs allowed mask .
*/
set_cpus_allowed ( current , cpus_allowed ) ;
2013-03-24 10:26:43 +04:00
cpufreq_notify_transition ( policy , & freqs , CPUFREQ_POSTCHANGE ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static unsigned int integrator_get ( unsigned int cpu )
{
cpumask_t cpus_allowed ;
unsigned int current_freq ;
u_int cm_osc ;
2010-01-16 19:27:28 +03:00
struct icst_vco vco ;
2005-04-17 02:20:36 +04:00
cpus_allowed = current - > cpus_allowed ;
set_cpus_allowed ( current , cpumask_of_cpu ( cpu ) ) ;
BUG_ON ( cpu ! = smp_processor_id ( ) ) ;
/* detect memory etc. */
2013-06-18 00:52:32 +04:00
cm_osc = __raw_readl ( cm_base + INTEGRATOR_HDR_OSC_OFFSET ) ;
2005-04-17 02:20:36 +04:00
if ( machine_is_integrator ( ) ) {
vco . s = ( cm_osc > > 8 ) & 7 ;
2011-01-11 16:48:17 +03:00
} else {
2005-04-17 02:20:36 +04:00
vco . s = 1 ;
}
vco . v = cm_osc & 255 ;
vco . r = 22 ;
2010-01-16 23:16:10 +03:00
current_freq = icst_hz ( & cclk_params , vco ) / 1000 ; /* current freq */
2005-04-17 02:20:36 +04:00
set_cpus_allowed ( current , cpus_allowed ) ;
return current_freq ;
}
static int integrator_cpufreq_init ( struct cpufreq_policy * policy )
{
/* set default policy and cpuinfo */
2013-10-03 18:58:46 +04:00
policy - > max = policy - > cpuinfo . max_freq = 160000 ;
policy - > min = policy - > cpuinfo . min_freq = 12000 ;
2005-04-17 02:20:36 +04:00
policy - > cpuinfo . transition_latency = 1000000 ; /* 1 ms, assumed */
return 0 ;
}
static struct cpufreq_driver integrator_driver = {
2013-12-03 09:50:45 +04:00
. flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK ,
2005-04-17 02:20:36 +04:00
. verify = integrator_verify_policy ,
. target = integrator_set_target ,
. get = integrator_get ,
. init = integrator_cpufreq_init ,
. name = " integrator " ,
} ;
2013-06-18 00:52:32 +04:00
static int __init integrator_cpufreq_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2013-06-18 00:52:32 +04:00
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
cm_base = devm_ioremap ( & pdev - > dev , res - > start , resource_size ( res ) ) ;
if ( ! cm_base )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
return cpufreq_register_driver ( & integrator_driver ) ;
}
2013-06-18 00:52:32 +04:00
static void __exit integrator_cpufreq_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
cpufreq_unregister_driver ( & integrator_driver ) ;
}
2013-06-18 00:52:32 +04:00
static const struct of_device_id integrator_cpufreq_match [ ] = {
{ . compatible = " arm,core-module-integrator " } ,
{ } ,
} ;
static struct platform_driver integrator_cpufreq_driver = {
. driver = {
. name = " integrator-cpufreq " ,
. owner = THIS_MODULE ,
. of_match_table = integrator_cpufreq_match ,
} ,
. remove = __exit_p ( integrator_cpufreq_remove ) ,
} ;
module_platform_driver_probe ( integrator_cpufreq_driver ,
integrator_cpufreq_probe ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Russell M. King " ) ;
MODULE_DESCRIPTION ( " cpufreq driver for ARM Integrator CPUs " ) ;
MODULE_LICENSE ( " GPL " ) ;