2013-02-04 09:46:29 +04:00
/*
* Copyright ( C ) 2013 Freescale Semiconductor , Inc .
*
* 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 .
*/
# include <linux/clk.h>
2013-09-10 21:59:47 +04:00
# include <linux/cpu.h>
2013-02-04 09:46:29 +04:00
# include <linux/cpufreq.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/of.h>
2013-09-20 01:03:52 +04:00
# include <linux/pm_opp.h>
2013-02-04 09:46:29 +04:00
# include <linux/platform_device.h>
# include <linux/regulator/consumer.h>
# define PU_SOC_VOLTAGE_NORMAL 1250000
# define PU_SOC_VOLTAGE_HIGH 1275000
# define FREQ_1P2_GHZ 1200000000
static struct regulator * arm_reg ;
static struct regulator * pu_reg ;
static struct regulator * soc_reg ;
static struct clk * arm_clk ;
static struct clk * pll1_sys_clk ;
static struct clk * pll1_sw_clk ;
static struct clk * step_clk ;
static struct clk * pll2_pfd2_396m_clk ;
2015-09-11 18:41:05 +03:00
/* clk used by i.MX6UL */
static struct clk * pll2_bus_clk ;
static struct clk * secondary_sel_clk ;
2013-02-04 09:46:29 +04:00
static struct device * cpu_dev ;
2014-11-25 13:34:23 +03:00
static bool free_opp ;
2013-02-04 09:46:29 +04:00
static struct cpufreq_frequency_table * freq_table ;
static unsigned int transition_latency ;
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
static u32 * imx6_soc_volt ;
static u32 soc_opp_count ;
2013-10-25 18:15:48 +04:00
static int imx6q_set_target ( struct cpufreq_policy * policy , unsigned int index )
2013-02-04 09:46:29 +04:00
{
2013-09-20 01:03:51 +04:00
struct dev_pm_opp * opp ;
2013-02-04 09:46:29 +04:00
unsigned long freq_hz , volt , volt_old ;
2013-08-14 18:08:24 +04:00
unsigned int old_freq , new_freq ;
2013-02-04 09:46:29 +04:00
int ret ;
2013-08-14 18:08:24 +04:00
new_freq = freq_table [ index ] . frequency ;
freq_hz = new_freq * 1000 ;
old_freq = clk_get_rate ( arm_clk ) / 1000 ;
2013-02-04 09:46:29 +04:00
rcu_read_lock ( ) ;
2013-09-20 01:03:50 +04:00
opp = dev_pm_opp_find_freq_ceil ( cpu_dev , & freq_hz ) ;
2013-02-04 09:46:29 +04:00
if ( IS_ERR ( opp ) ) {
rcu_read_unlock ( ) ;
dev_err ( cpu_dev , " failed to find OPP for %ld \n " , freq_hz ) ;
return PTR_ERR ( opp ) ;
}
2013-09-20 01:03:50 +04:00
volt = dev_pm_opp_get_voltage ( opp ) ;
2013-02-04 09:46:29 +04:00
rcu_read_unlock ( ) ;
volt_old = regulator_get_voltage ( arm_reg ) ;
dev_dbg ( cpu_dev , " %u MHz, %ld mV --> %u MHz, %ld mV \n " ,
2013-08-14 18:08:24 +04:00
old_freq / 1000 , volt_old / 1000 ,
new_freq / 1000 , volt / 1000 ) ;
2013-06-19 09:48:20 +04:00
2013-02-04 09:46:29 +04:00
/* scaling up? scale voltage before frequency */
2013-08-14 18:08:24 +04:00
if ( new_freq > old_freq ) {
2014-06-20 11:42:18 +04:00
if ( ! IS_ERR ( pu_reg ) ) {
ret = regulator_set_voltage_tol ( pu_reg , imx6_soc_volt [ index ] , 0 ) ;
if ( ret ) {
dev_err ( cpu_dev , " failed to scale vddpu up: %d \n " , ret ) ;
return ret ;
}
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
}
ret = regulator_set_voltage_tol ( soc_reg , imx6_soc_volt [ index ] , 0 ) ;
if ( ret ) {
dev_err ( cpu_dev , " failed to scale vddsoc up: %d \n " , ret ) ;
return ret ;
}
2013-02-04 09:46:29 +04:00
ret = regulator_set_voltage_tol ( arm_reg , volt , 0 ) ;
if ( ret ) {
dev_err ( cpu_dev ,
" failed to scale vddarm up: %d \n " , ret ) ;
2013-08-14 18:08:24 +04:00
return ret ;
2013-02-04 09:46:29 +04:00
}
}
/*
* The setpoints are selected per PLL / PDF frequencies , so we need to
* reprogram PLL for frequency scaling . The procedure of reprogramming
* PLL1 is as below .
2015-09-11 18:41:05 +03:00
* For i . MX6UL , it has a secondary clk mux , the cpu frequency change
* flow is slightly different from other i . MX6 OSC .
* The cpu frequeny change flow for i . MX6 ( except i . MX6UL ) is as below :
2013-02-04 09:46:29 +04:00
* - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it
* - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it
* - Disable pll2_pfd2_396m_clk
*/
2015-09-11 18:41:05 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ul " ) ) {
/*
* When changing pll1_sw_clk ' s parent to pll1_sys_clk ,
* CPU may run at higher than 528 MHz , this will lead to
* the system unstable if the voltage is lower than the
* voltage of 528 MHz , so lower the CPU frequency to one
* half before changing CPU frequency .
*/
clk_set_rate ( arm_clk , ( old_freq > > 1 ) * 1000 ) ;
2013-02-04 09:46:29 +04:00
clk_set_parent ( pll1_sw_clk , pll1_sys_clk ) ;
2015-09-11 18:41:05 +03:00
if ( freq_hz > clk_get_rate ( pll2_pfd2_396m_clk ) )
clk_set_parent ( secondary_sel_clk , pll2_bus_clk ) ;
else
clk_set_parent ( secondary_sel_clk , pll2_pfd2_396m_clk ) ;
clk_set_parent ( step_clk , secondary_sel_clk ) ;
clk_set_parent ( pll1_sw_clk , step_clk ) ;
} else {
clk_set_parent ( step_clk , pll2_pfd2_396m_clk ) ;
clk_set_parent ( pll1_sw_clk , step_clk ) ;
if ( freq_hz > clk_get_rate ( pll2_pfd2_396m_clk ) ) {
clk_set_rate ( pll1_sys_clk , new_freq * 1000 ) ;
clk_set_parent ( pll1_sw_clk , pll1_sys_clk ) ;
}
2013-02-04 09:46:29 +04:00
}
/* Ensure the arm clock divider is what we expect */
2013-08-14 18:08:24 +04:00
ret = clk_set_rate ( arm_clk , new_freq * 1000 ) ;
2013-02-04 09:46:29 +04:00
if ( ret ) {
dev_err ( cpu_dev , " failed to set clock rate: %d \n " , ret ) ;
regulator_set_voltage_tol ( arm_reg , volt_old , 0 ) ;
2013-08-14 18:08:24 +04:00
return ret ;
2013-02-04 09:46:29 +04:00
}
/* scaling down? scale voltage after frequency */
2013-08-14 18:08:24 +04:00
if ( new_freq < old_freq ) {
2013-02-04 09:46:29 +04:00
ret = regulator_set_voltage_tol ( arm_reg , volt , 0 ) ;
2013-06-19 09:48:20 +04:00
if ( ret ) {
2013-02-04 09:46:29 +04:00
dev_warn ( cpu_dev ,
" failed to scale vddarm down: %d \n " , ret ) ;
2013-06-19 09:48:20 +04:00
ret = 0 ;
}
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
ret = regulator_set_voltage_tol ( soc_reg , imx6_soc_volt [ index ] , 0 ) ;
if ( ret ) {
dev_warn ( cpu_dev , " failed to scale vddsoc down: %d \n " , ret ) ;
ret = 0 ;
}
2014-06-20 11:42:18 +04:00
if ( ! IS_ERR ( pu_reg ) ) {
ret = regulator_set_voltage_tol ( pu_reg , imx6_soc_volt [ index ] , 0 ) ;
if ( ret ) {
dev_warn ( cpu_dev , " failed to scale vddpu down: %d \n " , ret ) ;
ret = 0 ;
}
2013-02-04 09:46:29 +04:00
}
}
2013-08-14 18:08:24 +04:00
return 0 ;
2013-02-04 09:46:29 +04:00
}
static int imx6q_cpufreq_init ( struct cpufreq_policy * policy )
{
2014-01-09 19:08:43 +04:00
policy - > clk = arm_clk ;
2013-10-03 18:59:14 +04:00
return cpufreq_generic_init ( policy , freq_table , transition_latency ) ;
2013-02-04 09:46:29 +04:00
}
static struct cpufreq_driver imx6q_cpufreq_driver = {
2013-12-03 09:50:45 +04:00
. flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK ,
2013-10-03 18:58:08 +04:00
. verify = cpufreq_generic_frequency_table_verify ,
2013-10-25 18:15:48 +04:00
. target_index = imx6q_set_target ,
2014-01-09 19:08:43 +04:00
. get = cpufreq_generic_get ,
2013-02-04 09:46:29 +04:00
. init = imx6q_cpufreq_init ,
. name = " imx6q-cpufreq " ,
2013-10-03 18:58:08 +04:00
. attr = cpufreq_generic_attr ,
2013-02-04 09:46:29 +04:00
} ;
static int imx6q_cpufreq_probe ( struct platform_device * pdev )
{
struct device_node * np ;
2013-09-20 01:03:51 +04:00
struct dev_pm_opp * opp ;
2013-02-04 09:46:29 +04:00
unsigned long min_volt , max_volt ;
int num , ret ;
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
const struct property * prop ;
const __be32 * val ;
u32 nr , i , j ;
2013-02-04 09:46:29 +04:00
2013-09-10 21:59:47 +04:00
cpu_dev = get_cpu_device ( 0 ) ;
if ( ! cpu_dev ) {
pr_err ( " failed to get cpu0 device \n " ) ;
return - ENODEV ;
}
2013-02-04 09:46:29 +04:00
2013-06-17 17:58:48 +04:00
np = of_node_get ( cpu_dev - > of_node ) ;
2013-02-04 09:46:29 +04:00
if ( ! np ) {
dev_err ( cpu_dev , " failed to find cpu0 node \n " ) ;
return - ENOENT ;
}
2014-05-14 20:02:23 +04:00
arm_clk = clk_get ( cpu_dev , " arm " ) ;
pll1_sys_clk = clk_get ( cpu_dev , " pll1_sys " ) ;
pll1_sw_clk = clk_get ( cpu_dev , " pll1_sw " ) ;
step_clk = clk_get ( cpu_dev , " step " ) ;
pll2_pfd2_396m_clk = clk_get ( cpu_dev , " pll2_pfd2_396m " ) ;
2013-02-04 09:46:29 +04:00
if ( IS_ERR ( arm_clk ) | | IS_ERR ( pll1_sys_clk ) | | IS_ERR ( pll1_sw_clk ) | |
IS_ERR ( step_clk ) | | IS_ERR ( pll2_pfd2_396m_clk ) ) {
dev_err ( cpu_dev , " failed to get clocks \n " ) ;
ret = - ENOENT ;
2014-05-14 20:02:23 +04:00
goto put_clk ;
2013-02-04 09:46:29 +04:00
}
2015-09-11 18:41:05 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ul " ) ) {
pll2_bus_clk = clk_get ( cpu_dev , " pll2_bus " ) ;
secondary_sel_clk = clk_get ( cpu_dev , " secondary_sel " ) ;
if ( IS_ERR ( pll2_bus_clk ) | | IS_ERR ( secondary_sel_clk ) ) {
dev_err ( cpu_dev , " failed to get clocks specific to imx6ul \n " ) ;
ret = - ENOENT ;
goto put_clk ;
}
}
2014-05-14 20:02:23 +04:00
arm_reg = regulator_get ( cpu_dev , " arm " ) ;
2014-06-20 11:42:18 +04:00
pu_reg = regulator_get_optional ( cpu_dev , " pu " ) ;
2014-05-14 20:02:23 +04:00
soc_reg = regulator_get ( cpu_dev , " soc " ) ;
2014-06-20 11:42:18 +04:00
if ( IS_ERR ( arm_reg ) | | IS_ERR ( soc_reg ) ) {
2013-02-04 09:46:29 +04:00
dev_err ( cpu_dev , " failed to get regulators \n " ) ;
ret = - ENOENT ;
2014-05-14 20:02:23 +04:00
goto put_reg ;
2013-02-04 09:46:29 +04:00
}
2013-12-20 10:56:28 +04:00
/*
* We expect an OPP table supplied by platform .
* Just , incase the platform did not supply the OPP
* table , it will try to get it .
*/
2013-09-20 01:03:50 +04:00
num = dev_pm_opp_get_opp_count ( cpu_dev ) ;
2013-02-04 09:46:29 +04:00
if ( num < 0 ) {
2015-09-04 11:17:24 +03:00
ret = dev_pm_opp_of_add_table ( cpu_dev ) ;
2013-12-20 10:56:28 +04:00
if ( ret < 0 ) {
dev_err ( cpu_dev , " failed to init OPP table: %d \n " , ret ) ;
2014-05-14 20:02:23 +04:00
goto put_reg ;
2013-12-20 10:56:28 +04:00
}
2014-11-25 13:34:23 +03:00
/* Because we have added the OPPs here, we must free them */
free_opp = true ;
2013-12-20 10:56:28 +04:00
num = dev_pm_opp_get_opp_count ( cpu_dev ) ;
if ( num < 0 ) {
ret = num ;
dev_err ( cpu_dev , " no OPP table is found: %d \n " , ret ) ;
2014-11-25 13:34:23 +03:00
goto out_free_opp ;
2013-12-20 10:56:28 +04:00
}
2013-02-04 09:46:29 +04:00
}
2013-09-20 01:03:50 +04:00
ret = dev_pm_opp_init_cpufreq_table ( cpu_dev , & freq_table ) ;
2013-02-04 09:46:29 +04:00
if ( ret ) {
dev_err ( cpu_dev , " failed to init cpufreq table: %d \n " , ret ) ;
2014-05-14 20:02:23 +04:00
goto put_reg ;
2013-02-04 09:46:29 +04:00
}
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
/* Make imx6_soc_volt array's size same as arm opp number */
imx6_soc_volt = devm_kzalloc ( cpu_dev , sizeof ( * imx6_soc_volt ) * num , GFP_KERNEL ) ;
if ( imx6_soc_volt = = NULL ) {
ret = - ENOMEM ;
goto free_freq_table ;
}
prop = of_find_property ( np , " fsl,soc-operating-points " , NULL ) ;
if ( ! prop | | ! prop - > value )
goto soc_opp_out ;
/*
* Each OPP is a set of tuples consisting of frequency and
* voltage like < freq - kHz vol - uV > .
*/
nr = prop - > length / sizeof ( u32 ) ;
if ( nr % 2 | | ( nr / 2 ) < num )
goto soc_opp_out ;
for ( j = 0 ; j < num ; j + + ) {
val = prop - > value ;
for ( i = 0 ; i < nr / 2 ; i + + ) {
unsigned long freq = be32_to_cpup ( val + + ) ;
unsigned long volt = be32_to_cpup ( val + + ) ;
if ( freq_table [ j ] . frequency = = freq ) {
imx6_soc_volt [ soc_opp_count + + ] = volt ;
break ;
}
}
}
soc_opp_out :
/* use fixed soc opp volt if no valid soc opp info found in dtb */
if ( soc_opp_count ! = num ) {
dev_warn ( cpu_dev , " can NOT find valid fsl,soc-operating-points property in dtb, use default value! \n " ) ;
for ( j = 0 ; j < num ; j + + )
imx6_soc_volt [ j ] = PU_SOC_VOLTAGE_NORMAL ;
if ( freq_table [ num - 1 ] . frequency * 1000 = = FREQ_1P2_GHZ )
imx6_soc_volt [ num - 1 ] = PU_SOC_VOLTAGE_HIGH ;
}
2013-02-04 09:46:29 +04:00
if ( of_property_read_u32 ( np , " clock-latency " , & transition_latency ) )
transition_latency = CPUFREQ_ETERNAL ;
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
/*
* Calculate the ramp time for max voltage change in the
* VDDSOC and VDDPU regulators .
*/
ret = regulator_set_voltage_time ( soc_reg , imx6_soc_volt [ 0 ] , imx6_soc_volt [ num - 1 ] ) ;
if ( ret > 0 )
transition_latency + = ret * 1000 ;
2014-06-20 11:42:18 +04:00
if ( ! IS_ERR ( pu_reg ) ) {
ret = regulator_set_voltage_time ( pu_reg , imx6_soc_volt [ 0 ] , imx6_soc_volt [ num - 1 ] ) ;
if ( ret > 0 )
transition_latency + = ret * 1000 ;
}
cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed
on i.MX6Q, cpu freq change need to follow below flows:
1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint
table from dts;
2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before
VDDARM, if VDDPU is off, no need to change it;
3. when cpu freq is scaling down, need to decrease VDDARM voltage before
VDDSOC/PU, if VDDPU is off, no need to change it;
normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will
use fixed value for vddsoc/pu voltage setting.
Signed-off-by: Anson Huang <b20788@freescale.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-19 18:16:47 +04:00
2013-02-04 09:46:29 +04:00
/*
* OPP is maintained in order of increasing frequency , and
* freq_table initialised from OPP is therefore sorted in the
* same order .
*/
rcu_read_lock ( ) ;
2013-09-20 01:03:50 +04:00
opp = dev_pm_opp_find_freq_exact ( cpu_dev ,
2013-02-04 09:46:29 +04:00
freq_table [ 0 ] . frequency * 1000 , true ) ;
2013-09-20 01:03:50 +04:00
min_volt = dev_pm_opp_get_voltage ( opp ) ;
opp = dev_pm_opp_find_freq_exact ( cpu_dev ,
2013-02-04 09:46:29 +04:00
freq_table [ - - num ] . frequency * 1000 , true ) ;
2013-09-20 01:03:50 +04:00
max_volt = dev_pm_opp_get_voltage ( opp ) ;
2013-02-04 09:46:29 +04:00
rcu_read_unlock ( ) ;
ret = regulator_set_voltage_time ( arm_reg , min_volt , max_volt ) ;
if ( ret > 0 )
transition_latency + = ret * 1000 ;
ret = cpufreq_register_driver ( & imx6q_cpufreq_driver ) ;
if ( ret ) {
dev_err ( cpu_dev , " failed register driver: %d \n " , ret ) ;
goto free_freq_table ;
}
of_node_put ( np ) ;
return 0 ;
free_freq_table :
2013-09-20 01:03:50 +04:00
dev_pm_opp_free_cpufreq_table ( cpu_dev , & freq_table ) ;
2014-11-25 13:34:23 +03:00
out_free_opp :
if ( free_opp )
2015-09-04 11:17:24 +03:00
dev_pm_opp_of_remove_table ( cpu_dev ) ;
2014-05-14 20:02:23 +04:00
put_reg :
if ( ! IS_ERR ( arm_reg ) )
regulator_put ( arm_reg ) ;
if ( ! IS_ERR ( pu_reg ) )
regulator_put ( pu_reg ) ;
if ( ! IS_ERR ( soc_reg ) )
regulator_put ( soc_reg ) ;
put_clk :
if ( ! IS_ERR ( arm_clk ) )
clk_put ( arm_clk ) ;
if ( ! IS_ERR ( pll1_sys_clk ) )
clk_put ( pll1_sys_clk ) ;
if ( ! IS_ERR ( pll1_sw_clk ) )
clk_put ( pll1_sw_clk ) ;
if ( ! IS_ERR ( step_clk ) )
clk_put ( step_clk ) ;
if ( ! IS_ERR ( pll2_pfd2_396m_clk ) )
clk_put ( pll2_pfd2_396m_clk ) ;
2015-09-11 18:41:05 +03:00
if ( ! IS_ERR ( pll2_bus_clk ) )
clk_put ( pll2_bus_clk ) ;
if ( ! IS_ERR ( secondary_sel_clk ) )
clk_put ( secondary_sel_clk ) ;
2013-02-04 09:46:29 +04:00
of_node_put ( np ) ;
return ret ;
}
static int imx6q_cpufreq_remove ( struct platform_device * pdev )
{
cpufreq_unregister_driver ( & imx6q_cpufreq_driver ) ;
2013-09-20 01:03:50 +04:00
dev_pm_opp_free_cpufreq_table ( cpu_dev , & freq_table ) ;
2014-11-25 13:34:23 +03:00
if ( free_opp )
2015-09-04 11:17:24 +03:00
dev_pm_opp_of_remove_table ( cpu_dev ) ;
2014-05-14 20:02:23 +04:00
regulator_put ( arm_reg ) ;
2014-06-20 11:42:18 +04:00
if ( ! IS_ERR ( pu_reg ) )
regulator_put ( pu_reg ) ;
2014-05-14 20:02:23 +04:00
regulator_put ( soc_reg ) ;
clk_put ( arm_clk ) ;
clk_put ( pll1_sys_clk ) ;
clk_put ( pll1_sw_clk ) ;
clk_put ( step_clk ) ;
clk_put ( pll2_pfd2_396m_clk ) ;
2015-09-11 18:41:05 +03:00
clk_put ( pll2_bus_clk ) ;
clk_put ( secondary_sel_clk ) ;
2013-02-04 09:46:29 +04:00
return 0 ;
}
static struct platform_driver imx6q_cpufreq_platdrv = {
. driver = {
. name = " imx6q-cpufreq " ,
} ,
. probe = imx6q_cpufreq_probe ,
. remove = imx6q_cpufreq_remove ,
} ;
module_platform_driver ( imx6q_cpufreq_platdrv ) ;
MODULE_AUTHOR ( " Shawn Guo <shawn.guo@linaro.org> " ) ;
MODULE_DESCRIPTION ( " Freescale i.MX6Q cpufreq driver " ) ;
MODULE_LICENSE ( " GPL " ) ;