2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-02-04 09:46:29 +04:00
/*
* Copyright ( C ) 2013 Freescale Semiconductor , Inc .
*/
# 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>
2018-10-08 09:07:34 +03:00
# include <linux/nvmem-consumer.h>
2013-02-04 09:46:29 +04:00
# include <linux/of.h>
2017-09-30 18:16:46 +03:00
# include <linux/of_address.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 ;
2017-12-23 07:53:52 +03:00
enum IMX6_CPUFREQ_CLKS {
ARM ,
PLL1_SYS ,
STEP ,
PLL1_SW ,
PLL2_PFD2_396M ,
/* MX6UL requires two more clks */
PLL2_BUS ,
SECONDARY_SEL ,
} ;
# define IMX6Q_CPUFREQ_CLK_NUM 5
# define IMX6UL_CPUFREQ_CLK_NUM 7
static int num_clks ;
static struct clk_bulk_data clks [ ] = {
{ . id = " arm " } ,
{ . id = " pll1_sys " } ,
{ . id = " step " } ,
{ . id = " pll1_sw " } ,
{ . id = " pll2_pfd2_396m " } ,
{ . id = " pll2_bus " } ,
{ . id = " secondary_sel " } ,
} ;
2015-09-11 18:41:05 +03:00
2013-02-04 09:46:29 +04:00
static struct device * cpu_dev ;
static struct cpufreq_frequency_table * freq_table ;
2018-02-26 08:08:44 +03:00
static unsigned int max_freq ;
2013-02-04 09:46:29 +04:00
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 ;
2017-08-28 14:05:18 +03:00
bool pll1_sys_temp_enabled = false ;
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 ;
2017-12-23 07:53:52 +03:00
old_freq = clk_get_rate ( clks [ ARM ] . clk ) / 1000 ;
2013-02-04 09:46:29 +04:00
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 ) ) {
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 ) ;
2017-01-23 07:41:47 +03:00
dev_pm_opp_put ( opp ) ;
2013-02-04 09:46:29 +04:00
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
*/
2017-05-30 18:57:18 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ul " ) | |
of_machine_is_compatible ( " fsl,imx6ull " ) ) {
2015-09-11 18:41:05 +03:00
/*
* 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 .
*/
2017-12-23 07:53:52 +03:00
clk_set_rate ( clks [ ARM ] . clk , ( old_freq > > 1 ) * 1000 ) ;
clk_set_parent ( clks [ PLL1_SW ] . clk , clks [ PLL1_SYS ] . clk ) ;
if ( freq_hz > clk_get_rate ( clks [ PLL2_PFD2_396M ] . clk ) )
clk_set_parent ( clks [ SECONDARY_SEL ] . clk ,
clks [ PLL2_BUS ] . clk ) ;
2015-09-11 18:41:05 +03:00
else
2017-12-23 07:53:52 +03:00
clk_set_parent ( clks [ SECONDARY_SEL ] . clk ,
clks [ PLL2_PFD2_396M ] . clk ) ;
clk_set_parent ( clks [ STEP ] . clk , clks [ SECONDARY_SEL ] . clk ) ;
clk_set_parent ( clks [ PLL1_SW ] . clk , clks [ STEP ] . clk ) ;
2018-01-08 05:04:51 +03:00
if ( freq_hz > clk_get_rate ( clks [ PLL2_BUS ] . clk ) ) {
clk_set_rate ( clks [ PLL1_SYS ] . clk , new_freq * 1000 ) ;
clk_set_parent ( clks [ PLL1_SW ] . clk , clks [ PLL1_SYS ] . clk ) ;
}
2015-09-11 18:41:05 +03:00
} else {
2017-12-23 07:53:52 +03:00
clk_set_parent ( clks [ STEP ] . clk , clks [ PLL2_PFD2_396M ] . clk ) ;
clk_set_parent ( clks [ PLL1_SW ] . clk , clks [ STEP ] . clk ) ;
if ( freq_hz > clk_get_rate ( clks [ PLL2_PFD2_396M ] . clk ) ) {
clk_set_rate ( clks [ PLL1_SYS ] . clk , new_freq * 1000 ) ;
clk_set_parent ( clks [ PLL1_SW ] . clk , clks [ PLL1_SYS ] . clk ) ;
2017-08-28 14:05:18 +03:00
} else {
/* pll1_sys needs to be enabled for divider rate change to work. */
pll1_sys_temp_enabled = true ;
2017-12-23 07:53:52 +03:00
clk_prepare_enable ( clks [ PLL1_SYS ] . clk ) ;
2015-09-11 18:41:05 +03:00
}
2013-02-04 09:46:29 +04:00
}
/* Ensure the arm clock divider is what we expect */
2017-12-23 07:53:52 +03:00
ret = clk_set_rate ( clks [ ARM ] . clk , new_freq * 1000 ) ;
2013-02-04 09:46:29 +04:00
if ( ret ) {
2018-11-05 03:59:28 +03:00
int ret1 ;
2013-02-04 09:46:29 +04:00
dev_err ( cpu_dev , " failed to set clock rate: %d \n " , ret ) ;
2018-11-05 03:59:28 +03:00
ret1 = regulator_set_voltage_tol ( arm_reg , volt_old , 0 ) ;
if ( ret1 )
dev_warn ( cpu_dev ,
" failed to restore vddarm voltage: %d \n " , ret1 ) ;
2013-08-14 18:08:24 +04:00
return ret ;
2013-02-04 09:46:29 +04:00
}
2017-08-28 14:05:18 +03:00
/* PLL1 is only needed until after ARM-PODF is set. */
if ( pll1_sys_temp_enabled )
2017-12-23 07:53:52 +03:00
clk_disable_unprepare ( clks [ PLL1_SYS ] . clk ) ;
2017-08-28 14:05:18 +03:00
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 ) ;
2018-11-26 05:59:45 +03:00
if ( ret )
2013-02-04 09:46:29 +04:00
dev_warn ( cpu_dev ,
" failed to scale vddarm down: %d \n " , 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 ) ;
2018-11-26 05:59:45 +03:00
if ( 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
dev_warn ( cpu_dev , " failed to scale vddsoc down: %d \n " , ret ) ;
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 ) ;
2018-11-26 05:59:45 +03:00
if ( ret )
2014-06-20 11:42:18 +04:00
dev_warn ( cpu_dev , " failed to scale vddpu down: %d \n " , ret ) ;
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 )
{
2017-12-23 07:53:52 +03:00
policy - > clk = clks [ ARM ] . clk ;
2019-07-16 07:06:08 +03:00
cpufreq_generic_init ( policy , freq_table , transition_latency ) ;
2018-02-26 08:08:44 +03:00
policy - > suspend_freq = max_freq ;
2017-04-04 20:04:12 +03:00
2019-07-16 07:06:08 +03:00
return 0 ;
2013-02-04 09:46:29 +04:00
}
static struct cpufreq_driver imx6q_cpufreq_driver = {
2019-01-29 07:55:10 +03:00
. flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
CPUFREQ_IS_COOLING_DEV ,
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 ,
2021-08-10 09:54:36 +03:00
. register_em = cpufreq_register_em_with_opp ,
2013-02-04 09:46:29 +04:00
. name = " imx6q-cpufreq " ,
2013-10-03 18:58:08 +04:00
. attr = cpufreq_generic_attr ,
2017-04-04 20:04:12 +03:00
. suspend = cpufreq_generic_suspend ,
2013-02-04 09:46:29 +04:00
} ;
2023-05-12 18:07:11 +03:00
static void imx6x_disable_freq_in_opp ( struct device * dev , unsigned long freq )
{
int ret = dev_pm_opp_disable ( dev , freq ) ;
if ( ret < 0 & & ret ! = - ENODEV )
dev_warn ( dev , " failed to disable %ldMHz OPP \n " , freq / 1000000 ) ;
}
2017-09-30 18:16:46 +03:00
# define OCOTP_CFG3 0x440
# define OCOTP_CFG3_SPEED_SHIFT 16
# define OCOTP_CFG3_SPEED_1P2GHZ 0x3
# define OCOTP_CFG3_SPEED_996MHZ 0x2
# define OCOTP_CFG3_SPEED_852MHZ 0x1
2020-03-03 05:14:50 +03:00
static int imx6q_opp_check_speed_grading ( struct device * dev )
2017-09-30 18:16:46 +03:00
{
struct device_node * np ;
void __iomem * base ;
u32 val ;
2020-03-03 05:14:50 +03:00
int ret ;
2017-09-30 18:16:46 +03:00
2023-03-10 17:47:02 +03:00
if ( of_property_present ( dev - > of_node , " nvmem-cells " ) ) {
2020-03-03 05:14:50 +03:00
ret = nvmem_cell_read_u32 ( dev , " speed_grade " , & val ) ;
if ( ret )
return ret ;
} else {
np = of_find_compatible_node ( NULL , NULL , " fsl,imx6q-ocotp " ) ;
if ( ! np )
return - ENOENT ;
2017-09-30 18:16:46 +03:00
2020-03-03 05:14:50 +03:00
base = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! base ) {
dev_err ( dev , " failed to map ocotp \n " ) ;
return - EFAULT ;
}
/*
* SPEED_GRADING [ 1 : 0 ] defines the max speed of ARM :
* 2 b ' 11 : 1200000000 Hz ;
* 2 b ' 10 : 996000000 Hz ;
* 2 b ' 01 : 852000000 Hz ; - - i . MX6Q Only , exclusive with 996 MHz .
* 2 b ' 00 : 792000000 Hz ;
* We need to set the max speed of ARM according to fuse map .
*/
val = readl_relaxed ( base + OCOTP_CFG3 ) ;
iounmap ( base ) ;
2017-09-30 18:16:46 +03:00
}
val > > = OCOTP_CFG3_SPEED_SHIFT ;
val & = 0x3 ;
if ( val < OCOTP_CFG3_SPEED_996MHZ )
2023-05-12 18:07:11 +03:00
imx6x_disable_freq_in_opp ( dev , 996000000 ) ;
2017-12-11 16:19:00 +03:00
if ( of_machine_is_compatible ( " fsl,imx6q " ) | |
of_machine_is_compatible ( " fsl,imx6qp " ) ) {
2017-09-30 18:16:46 +03:00
if ( val ! = OCOTP_CFG3_SPEED_852MHZ )
2023-05-12 18:07:11 +03:00
imx6x_disable_freq_in_opp ( dev , 852000000 ) ;
2017-12-11 16:19:00 +03:00
if ( val ! = OCOTP_CFG3_SPEED_1P2GHZ )
2023-05-12 18:07:11 +03:00
imx6x_disable_freq_in_opp ( dev , 1200000000 ) ;
2017-09-30 18:16:46 +03:00
}
2020-03-03 05:14:50 +03:00
return 0 ;
2017-09-30 18:16:46 +03:00
}
2018-01-08 05:04:51 +03:00
# define OCOTP_CFG3_6UL_SPEED_696MHZ 0x2
2018-05-22 09:28:51 +03:00
# define OCOTP_CFG3_6ULL_SPEED_792MHZ 0x2
# define OCOTP_CFG3_6ULL_SPEED_900MHZ 0x3
2018-01-08 05:04:51 +03:00
2018-10-08 09:07:34 +03:00
static int imx6ul_opp_check_speed_grading ( struct device * dev )
2018-01-08 05:04:51 +03:00
{
u32 val ;
2018-10-08 09:07:34 +03:00
int ret = 0 ;
2018-01-08 05:04:51 +03:00
2023-03-10 17:47:02 +03:00
if ( of_property_present ( dev - > of_node , " nvmem-cells " ) ) {
2018-10-08 09:07:34 +03:00
ret = nvmem_cell_read_u32 ( dev , " speed_grade " , & val ) ;
if ( ret )
return ret ;
} else {
struct device_node * np ;
void __iomem * base ;
np = of_find_compatible_node ( NULL , NULL , " fsl,imx6ul-ocotp " ) ;
2020-02-11 14:58:07 +03:00
if ( ! np )
np = of_find_compatible_node ( NULL , NULL ,
" fsl,imx6ull-ocotp " ) ;
2018-10-08 09:07:34 +03:00
if ( ! np )
return - ENOENT ;
base = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! base ) {
dev_err ( dev , " failed to map ocotp \n " ) ;
return - EFAULT ;
}
2018-01-08 05:04:51 +03:00
2018-10-08 09:07:34 +03:00
val = readl_relaxed ( base + OCOTP_CFG3 ) ;
iounmap ( base ) ;
2018-01-08 05:04:51 +03:00
}
/*
* Speed GRADING [ 1 : 0 ] defines the max speed of ARM :
* 2 b ' 00 : Reserved ;
* 2 b ' 01 : 528000000 Hz ;
2018-05-22 09:28:51 +03:00
* 2 b ' 10 : 696000000 Hz on i . MX6UL , 792000000 Hz on i . MX6ULL ;
* 2 b ' 11 : 900000000 Hz on i . MX6ULL only ;
2018-01-08 05:04:51 +03:00
* We need to set the max speed of ARM according to fuse map .
*/
val > > = OCOTP_CFG3_SPEED_SHIFT ;
val & = 0x3 ;
2018-05-22 09:28:51 +03:00
2023-05-12 18:07:11 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ul " ) )
2018-05-22 09:28:51 +03:00
if ( val ! = OCOTP_CFG3_6UL_SPEED_696MHZ )
2023-05-12 18:07:11 +03:00
imx6x_disable_freq_in_opp ( dev , 696000000 ) ;
2018-05-22 09:28:51 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ull " ) ) {
2023-11-22 16:41:13 +03:00
if ( val < OCOTP_CFG3_6ULL_SPEED_792MHZ )
2023-05-12 18:07:11 +03:00
imx6x_disable_freq_in_opp ( dev , 792000000 ) ;
2018-05-22 09:28:51 +03:00
if ( val ! = OCOTP_CFG3_6ULL_SPEED_900MHZ )
2023-05-12 18:07:11 +03:00
imx6x_disable_freq_in_opp ( dev , 900000000 ) ;
2018-05-22 09:28:51 +03:00
}
2018-10-08 09:07:34 +03:00
return ret ;
2018-01-08 05:04:51 +03:00
}
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 ;
}
2017-05-30 18:57:18 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ul " ) | |
2017-12-23 07:53:52 +03:00
of_machine_is_compatible ( " fsl,imx6ull " ) )
num_clks = IMX6UL_CPUFREQ_CLK_NUM ;
else
num_clks = IMX6Q_CPUFREQ_CLK_NUM ;
ret = clk_bulk_get ( cpu_dev , num_clks , clks ) ;
if ( ret )
goto put_node ;
2015-09-11 18:41:05 +03:00
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 " ) ;
2017-04-04 20:04:11 +03:00
if ( PTR_ERR ( arm_reg ) = = - EPROBE_DEFER | |
PTR_ERR ( soc_reg ) = = - EPROBE_DEFER | |
PTR_ERR ( pu_reg ) = = - EPROBE_DEFER ) {
ret = - EPROBE_DEFER ;
dev_dbg ( cpu_dev , " regulators not ready, defer \n " ) ;
goto put_reg ;
}
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
}
2017-09-30 18:16:46 +03:00
ret = dev_pm_opp_of_add_table ( cpu_dev ) ;
if ( ret < 0 ) {
dev_err ( cpu_dev , " failed to init OPP table: %d \n " , ret ) ;
goto put_reg ;
}
2013-12-20 10:56:28 +04:00
2018-05-22 09:28:51 +03:00
if ( of_machine_is_compatible ( " fsl,imx6ul " ) | |
2018-10-08 09:07:34 +03:00
of_machine_is_compatible ( " fsl,imx6ull " ) ) {
ret = imx6ul_opp_check_speed_grading ( cpu_dev ) ;
} else {
2020-03-03 05:14:50 +03:00
ret = imx6q_opp_check_speed_grading ( cpu_dev ) ;
}
if ( ret ) {
2022-09-27 18:40:19 +03:00
dev_err_probe ( cpu_dev , ret , " failed to read ocotp \n " ) ;
2020-03-03 05:14:50 +03:00
goto out_free_opp ;
2018-10-08 09:07:34 +03:00
}
2014-11-25 13:34:23 +03:00
2017-09-30 18:16:46 +03: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 ) ;
goto out_free_opp ;
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 ) ;
2017-04-09 10:33:52 +03:00
goto out_free_opp ;
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 */
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
imx6_soc_volt = devm_kcalloc ( cpu_dev , num , sizeof ( * imx6_soc_volt ) ,
GFP_KERNEL ) ;
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
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 .
*/
2018-02-26 08:08:44 +03:00
max_freq = freq_table [ - - num ] . frequency ;
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 ) ;
2017-01-23 07:41:47 +03:00
dev_pm_opp_put ( opp ) ;
2018-02-26 08:08:44 +03:00
opp = dev_pm_opp_find_freq_exact ( cpu_dev , max_freq * 1000 , true ) ;
2013-09-20 01:03:50 +04:00
max_volt = dev_pm_opp_get_voltage ( opp ) ;
2017-01-23 07:41:47 +03:00
dev_pm_opp_put ( opp ) ;
2013-02-04 09:46:29 +04:00
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 :
2020-08-20 13:16:20 +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 ) ;
2017-12-23 07:53:52 +03:00
clk_bulk_put ( num_clks , clks ) ;
put_node :
2013-02-04 09:46:29 +04:00
of_node_put ( np ) ;
2017-12-23 07:53:52 +03:00
2013-02-04 09:46:29 +04:00
return ret ;
}
2023-07-12 12:33:08 +03:00
static void imx6q_cpufreq_remove ( struct platform_device * pdev )
2013-02-04 09:46:29 +04:00
{
cpufreq_unregister_driver ( & imx6q_cpufreq_driver ) ;
2013-09-20 01:03:50 +04:00
dev_pm_opp_free_cpufreq_table ( cpu_dev , & freq_table ) ;
2020-08-20 13:16:20 +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 ) ;
2017-12-23 07:53:52 +03:00
clk_bulk_put ( num_clks , clks ) ;
2013-02-04 09:46:29 +04:00
}
static struct platform_driver imx6q_cpufreq_platdrv = {
. driver = {
. name = " imx6q-cpufreq " ,
} ,
. probe = imx6q_cpufreq_probe ,
2023-07-12 12:33:08 +03:00
. remove_new = imx6q_cpufreq_remove ,
2013-02-04 09:46:29 +04:00
} ;
module_platform_driver ( imx6q_cpufreq_platdrv ) ;
2018-01-30 12:55:26 +03:00
MODULE_ALIAS ( " platform:imx6q-cpufreq " ) ;
2013-02-04 09:46:29 +04:00
MODULE_AUTHOR ( " Shawn Guo <shawn.guo@linaro.org> " ) ;
MODULE_DESCRIPTION ( " Freescale i.MX6Q cpufreq driver " ) ;
MODULE_LICENSE ( " GPL " ) ;