2017-12-25 22:54:32 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2009 Simtec Electronics
// http://armlinux.simtec.co.uk/
// Ben Dooks <ben@simtec.co.uk>
//
// Simtec Osiris Dynamic Voltage Scaling support.
2009-11-14 01:34:21 +03:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/cpufreq.h>
# include <linux/gpio.h>
2017-08-14 19:34:23 +03:00
# include <linux/mfd/tps65010.h>
2009-11-14 01:34:21 +03:00
2020-08-06 21:20:52 +03:00
# include <linux/soc/samsung/s3c-cpu-freq.h>
2019-09-02 19:37:30 +03:00
# include "gpio-samsung.h"
2009-11-14 01:34:21 +03:00
# define OSIRIS_GPIO_DVS S3C2410_GPB(5)
static bool dvs_en ;
static void osiris_dvs_tps_setdvs ( bool on )
{
unsigned vregs1 = 0 , vdcdc2 = 0 ;
if ( ! on ) {
vdcdc2 = TPS_VCORE_DISCH | TPS_LP_COREOFF ;
vregs1 = TPS_LDO1_OFF ; /* turn off in low-power mode */
}
dvs_en = on ;
vdcdc2 | = TPS_VCORE_1_3V | TPS_VCORE_LP_1_0V ;
vregs1 | = TPS_LDO2_ENABLE | TPS_LDO1_ENABLE ;
tps65010_config_vregs1 ( vregs1 ) ;
tps65010_config_vdcdc2 ( vdcdc2 ) ;
}
static bool is_dvs ( struct s3c_freq * f )
{
/* at the moment, we assume ARMCLK = HCLK => DVS */
return f - > armclk = = f - > hclk ;
}
/* keep track of current state */
static bool cur_dvs = false ;
static int osiris_dvs_notify ( struct notifier_block * nb ,
unsigned long val , void * data )
{
struct cpufreq_freqs * cf = data ;
struct s3c_cpufreq_freqs * freqs = to_s3c_cpufreq ( cf ) ;
bool old_dvs = is_dvs ( & freqs - > old ) ;
bool new_dvs = is_dvs ( & freqs - > new ) ;
int ret = 0 ;
if ( ! dvs_en )
return 0 ;
printk ( KERN_DEBUG " %s: old %ld,%ld new %ld,%ld \n " , __func__ ,
freqs - > old . armclk , freqs - > old . hclk ,
freqs - > new . armclk , freqs - > new . hclk ) ;
switch ( val ) {
case CPUFREQ_PRECHANGE :
2019-01-03 23:14:08 +03:00
if ( ( old_dvs & & ! new_dvs ) | |
( cur_dvs & & ! new_dvs ) ) {
2009-11-14 01:34:21 +03:00
pr_debug ( " %s: exiting dvs \n " , __func__ ) ;
cur_dvs = false ;
gpio_set_value ( OSIRIS_GPIO_DVS , 1 ) ;
}
break ;
case CPUFREQ_POSTCHANGE :
2019-01-03 23:14:08 +03:00
if ( ( ! old_dvs & & new_dvs ) | |
( ! cur_dvs & & new_dvs ) ) {
2009-11-14 01:34:21 +03:00
pr_debug ( " entering dvs \n " ) ;
cur_dvs = true ;
gpio_set_value ( OSIRIS_GPIO_DVS , 0 ) ;
}
break ;
}
return ret ;
}
static struct notifier_block osiris_dvs_nb = {
. notifier_call = osiris_dvs_notify ,
} ;
2012-12-22 02:02:24 +04:00
static int osiris_dvs_probe ( struct platform_device * pdev )
2009-11-14 01:34:21 +03:00
{
int ret ;
dev_info ( & pdev - > dev , " initialising \n " ) ;
ret = gpio_request ( OSIRIS_GPIO_DVS , " osiris-dvs " ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " cannot claim gpio \n " ) ;
goto err_nogpio ;
}
/* start with dvs disabled */
gpio_direction_output ( OSIRIS_GPIO_DVS , 1 ) ;
ret = cpufreq_register_notifier ( & osiris_dvs_nb ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register with cpufreq \n " ) ;
goto err_nofreq ;
}
osiris_dvs_tps_setdvs ( true ) ;
return 0 ;
err_nofreq :
gpio_free ( OSIRIS_GPIO_DVS ) ;
err_nogpio :
return ret ;
}
2012-12-22 02:02:24 +04:00
static int osiris_dvs_remove ( struct platform_device * pdev )
2009-11-14 01:34:21 +03:00
{
dev_info ( & pdev - > dev , " exiting \n " ) ;
/* disable any current dvs */
gpio_set_value ( OSIRIS_GPIO_DVS , 1 ) ;
osiris_dvs_tps_setdvs ( false ) ;
cpufreq_unregister_notifier ( & osiris_dvs_nb ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
gpio_free ( OSIRIS_GPIO_DVS ) ;
return 0 ;
}
2016-05-21 14:51:23 +03:00
/* the CONFIG_PM block is so small, it isn't worth actually compiling it
2009-11-14 01:34:21 +03:00
* out if the configuration isn ' t set . */
static int osiris_dvs_suspend ( struct device * dev )
{
gpio_set_value ( OSIRIS_GPIO_DVS , 1 ) ;
osiris_dvs_tps_setdvs ( false ) ;
cur_dvs = false ;
return 0 ;
}
static int osiris_dvs_resume ( struct device * dev )
{
osiris_dvs_tps_setdvs ( true ) ;
return 0 ;
}
static const struct dev_pm_ops osiris_dvs_pm = {
. suspend = osiris_dvs_suspend ,
. resume = osiris_dvs_resume ,
} ;
static struct platform_driver osiris_dvs_driver = {
. probe = osiris_dvs_probe ,
2012-12-22 02:02:24 +04:00
. remove = osiris_dvs_remove ,
2009-11-14 01:34:21 +03:00
. driver = {
. name = " osiris-dvs " ,
. pm = & osiris_dvs_pm ,
} ,
} ;
2012-09-07 01:33:28 +04:00
module_platform_driver ( osiris_dvs_driver ) ;
2009-11-14 01:34:21 +03:00
MODULE_DESCRIPTION ( " Simtec OSIRIS DVS support " ) ;
MODULE_AUTHOR ( " Ben Dooks <ben@simtec.co.uk> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:osiris-dvs " ) ;