diff options
author | Anson Huang <b20788@freescale.com> | 2015-08-20 12:10:27 +0800 |
---|---|---|
committer | Jason Liu <jason.hui.liu@nxp.com> | 2019-02-12 10:22:02 +0800 |
commit | 7a9b1a14855fb74efaf90fdeafb34b33209a97e1 (patch) | |
tree | ef89a9d064fdcf1f78baa46fbe06e7d05864616b /arch/arm/mach-imx/busfreq-imx.c | |
parent | dbcb068cbfc2a255b2e1638748749a1b5409787e (diff) |
MLK-11390-5 ARM: imx: add busfreq support for imx6sx DDR3
Add busfreq support for i.MX6SX DDR3 board, tested on
i.MX6SX SDB board, busfreq support below 3 setpoints:
high -> 400MHz
audio -> 50MHz
low -> 24MHz
Signed-off-by: Anson Huang <b20788@freescale.com>
Diffstat (limited to 'arch/arm/mach-imx/busfreq-imx.c')
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/busfreq-imx.c b/arch/arm/mach-imx/busfreq-imx.c index bd8a97554377..19246693f29e 100644 --- a/arch/arm/mach-imx/busfreq-imx.c +++ b/arch/arm/mach-imx/busfreq-imx.c @@ -45,6 +45,7 @@ unsigned long ddr_freq_change_total_size; unsigned long ddr_freq_change_iram_base; unsigned long ddr_freq_change_iram_phys; +static int ddr_type; static int low_bus_freq_mode; static int audio_bus_freq_mode; static int ultra_low_bus_freq_mode; @@ -61,8 +62,11 @@ static int cur_bus_freq_mode; extern unsigned long iram_tlb_phys_addr; extern int unsigned long iram_tlb_base_addr; +extern int init_mmdc_lpddr2_settings(struct platform_device *dev); +extern int init_mmdc_ddr3_settings_imx6_up(struct platform_device *dev); extern int init_ddrc_ddr_settings(struct platform_device *dev); extern int update_ddr_freq_imx_smp(int ddr_rate); +extern int update_ddr_freq_imx6_up(int ddr_rate); extern int update_lpddr2_freq(int ddr_rate); DEFINE_MUTEX(bus_freq_mutex); @@ -80,6 +84,24 @@ static struct clk *pll_dram; static struct clk *ahb_sel_clk; static struct clk *axi_clk; +static struct clk *m4_clk; +static struct clk *arm_clk; +static struct clk *pll3_clk; +static struct clk *step_clk; +static struct clk *mmdc_clk; +static struct clk *ocram_clk; +static struct clk *pll2_400_clk; +static struct clk *pll2_200_clk; +static struct clk *pll2_bus_clk; +static struct clk *periph_clk; +static struct clk *periph_pre_clk; +static struct clk *periph_clk2_clk; +static struct clk *periph_clk2_sel_clk; +static struct clk *periph2_clk; +static struct clk *periph2_pre_clk; +static struct clk *periph2_clk2_clk; +static struct clk *periph2_clk2_sel_clk; + static struct delayed_work low_bus_freq_handler; static struct delayed_work bus_freq_daemon; @@ -106,6 +128,114 @@ int unregister_busfreq_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_busfreq_notifier); +/* + * enter_lpm_imx6_up and exit_lpm_imx6_up is used by + * i.MX6SX/i.MX6UL for entering and exiting lpm mode. + */ +static void enter_lpm_imx6_up(void) +{ + /* set periph_clk2 to source from OSC for periph */ + clk_set_parent(periph_clk2_sel_clk, osc_clk); + clk_set_parent(periph_clk, periph_clk2_clk); + /* set ahb/ocram to 24MHz */ + clk_set_rate(ahb_clk, LPAPM_CLK); + clk_set_rate(ocram_clk, LPAPM_CLK); + + if (audio_bus_count) { + /* Need to ensure that PLL2_PFD_400M is kept ON. */ + clk_prepare_enable(pll2_400_clk); + if (ddr_type == IMX_DDR_TYPE_DDR3) + update_ddr_freq_imx6_up(LOW_AUDIO_CLK); + else if (ddr_type == IMX_DDR_TYPE_LPDDR2) + update_lpddr2_freq(HIGH_AUDIO_CLK); + clk_set_parent(periph2_clk2_sel_clk, pll3_clk); + clk_set_parent(periph2_pre_clk, pll2_400_clk); + clk_set_parent(periph2_clk, periph2_pre_clk); + /* + * As periph2_clk's parent is not changed from + * high mode to audio mode, so clk framework + * will not update its children's freq, but we + * change the mmdc's podf in asm code, so here + * need to update mmdc rate to make sure clk + * tree is right, although it will not do any + * change to hardware. + */ + if (high_bus_freq_mode) { + if (ddr_type == IMX_DDR_TYPE_DDR3) + clk_set_rate(mmdc_clk, LOW_AUDIO_CLK); + else if (ddr_type == IMX_DDR_TYPE_LPDDR2) + clk_set_rate(mmdc_clk, HIGH_AUDIO_CLK); + } + audio_bus_freq_mode = 1; + low_bus_freq_mode = 0; + cur_bus_freq_mode = BUS_FREQ_AUDIO; + } else { + if (ddr_type == IMX_DDR_TYPE_DDR3) + update_ddr_freq_imx6_up(LPAPM_CLK); + else if (ddr_type == IMX_DDR_TYPE_LPDDR2) + update_lpddr2_freq(LPAPM_CLK); + clk_set_parent(periph2_clk2_sel_clk, osc_clk); + clk_set_parent(periph2_clk, periph2_clk2_clk); + + if (audio_bus_freq_mode) + clk_disable_unprepare(pll2_400_clk); + low_bus_freq_mode = 1; + audio_bus_freq_mode = 0; + cur_bus_freq_mode = BUS_FREQ_LOW; + } +} + +static void exit_lpm_imx6_up(void) +{ + clk_prepare_enable(pll2_400_clk); + + /* + * lower ahb/ocram's freq first to avoid too high + * freq during parent switch from OSC to pll3. + */ + if (cpu_is_imx6ul()) + clk_set_rate(ahb_clk, LPAPM_CLK / 4); + else + clk_set_rate(ahb_clk, LPAPM_CLK / 3); + + clk_set_rate(ocram_clk, LPAPM_CLK / 2); + /* set periph_clk2 to pll3 */ + clk_set_parent(periph_clk2_sel_clk, pll3_clk); + /* set periph clk to from pll2_bus on i.MX6UL */ + if (cpu_is_imx6ul()) + clk_set_parent(periph_pre_clk, pll2_bus_clk); + /* set periph clk to from pll2_400 */ + else + clk_set_parent(periph_pre_clk, pll2_400_clk); + clk_set_parent(periph_clk, periph_pre_clk); + + if (ddr_type == IMX_DDR_TYPE_DDR3) + update_ddr_freq_imx6_up(ddr_normal_rate); + else if (ddr_type == IMX_DDR_TYPE_LPDDR2) + update_lpddr2_freq(ddr_normal_rate); + /* correct parent info after ddr freq change in asm code */ + clk_set_parent(periph2_pre_clk, pll2_400_clk); + clk_set_parent(periph2_clk, periph2_pre_clk); + clk_set_parent(periph2_clk2_sel_clk, pll3_clk); + + /* + * As periph2_clk's parent is not changed from + * audio mode to high mode, so clk framework + * will not update its children's freq, but we + * change the mmdc's podf in asm code, so here + * need to update mmdc rate to make sure clk + * tree is right, although it will not do any + * change to hardware. + */ + if (audio_bus_freq_mode) + clk_set_rate(mmdc_clk, ddr_normal_rate); + + clk_disable_unprepare(pll2_400_clk); + + if (audio_bus_freq_mode) + clk_disable_unprepare(pll2_400_clk); +} + static void enter_lpm_imx7d(void) { if (audio_bus_count) { @@ -152,6 +282,9 @@ static void exit_lpm_imx7d(void) static void reduce_bus_freq(void) { + if (cpu_is_imx6()) + clk_prepare_enable(pll3_clk); + if (audio_bus_count && (low_bus_freq_mode || ultra_low_bus_freq_mode)) busfreq_notify(LOW_BUSFREQ_EXIT); else if (!audio_bus_count) @@ -159,10 +292,15 @@ static void reduce_bus_freq(void) if (cpu_is_imx7d()) enter_lpm_imx7d(); + else if (cpu_is_imx6sx() || cpu_is_imx6ul()) + enter_lpm_imx6_up(); med_bus_freq_mode = 0; high_bus_freq_mode = 0; + if (cpu_is_imx6()) + clk_disable_unprepare(pll3_clk); + if (audio_bus_freq_mode) dev_dbg(busfreq_dev, "Bus freq set to audio mode. Count: high %d, med %d, audio %d\n", @@ -219,12 +357,19 @@ int set_low_bus_freq(void) */ static int set_high_bus_freq(int high_bus_freq) { + struct clk *periph_clk_parent; + if (bus_freq_scaling_initialized && bus_freq_scaling_is_active) cancel_delayed_work_sync(&low_bus_freq_handler); if (busfreq_suspended) return 0; + if (cpu_is_imx6q()) + periph_clk_parent = pll2_bus_clk; + else + periph_clk_parent = pll2_400_clk; + if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) return 0; @@ -238,8 +383,13 @@ static int set_high_bus_freq(int high_bus_freq) if (low_bus_freq_mode || ultra_low_bus_freq_mode) busfreq_notify(LOW_BUSFREQ_EXIT); + if (cpu_is_imx6()) + clk_prepare_enable(pll3_clk); + if (cpu_is_imx7d()) exit_lpm_imx7d(); + else if (cpu_is_imx6sx() || cpu_is_imx6ul()) + exit_lpm_imx6_up(); high_bus_freq_mode = 1; med_bus_freq_mode = 0; @@ -247,6 +397,9 @@ static int set_high_bus_freq(int high_bus_freq) audio_bus_freq_mode = 0; cur_bus_freq_mode = BUS_FREQ_HIGH; + if (cpu_is_imx6()) + clk_disable_unprepare(pll3_clk); + if (high_bus_freq_mode) dev_dbg(busfreq_dev, "Bus freq set to high mode. Count: high %d, med %d, audio %d\n", @@ -552,6 +705,45 @@ static int busfreq_probe(struct platform_device *pdev) if (!ddr_freq_change_iram_base) return -ENOMEM; + if (cpu_is_imx6sx()) { + m4_clk = devm_clk_get(&pdev->dev, "m4"); + arm_clk = devm_clk_get(&pdev->dev, "arm"); + osc_clk = devm_clk_get(&pdev->dev, "osc"); + ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + pll3_clk = devm_clk_get(&pdev->dev, "pll3_usb_otg"); + step_clk = devm_clk_get(&pdev->dev, "step"); + mmdc_clk = devm_clk_get(&pdev->dev, "mmdc"); + ocram_clk = devm_clk_get(&pdev->dev, "ocram"); + pll2_400_clk = devm_clk_get(&pdev->dev, "pll2_pfd2_396m"); + pll2_200_clk = devm_clk_get(&pdev->dev, "pll2_198m"); + pll2_bus_clk = devm_clk_get(&pdev->dev, "pll2_bus"); + periph_clk = devm_clk_get(&pdev->dev, "periph"); + periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre"); + periph_clk2_clk = devm_clk_get(&pdev->dev, "periph_clk2"); + periph_clk2_sel_clk = + devm_clk_get(&pdev->dev, "periph_clk2_sel"); + periph2_clk = devm_clk_get(&pdev->dev, "periph2"); + periph2_pre_clk = devm_clk_get(&pdev->dev, "periph2_pre"); + periph2_clk2_clk = devm_clk_get(&pdev->dev, "periph2_clk2"); + periph2_clk2_sel_clk = + devm_clk_get(&pdev->dev, "periph2_clk2_sel"); + if (IS_ERR(m4_clk) || IS_ERR(arm_clk) || IS_ERR(osc_clk) + || IS_ERR(ahb_clk) || IS_ERR(pll3_clk) + || IS_ERR(step_clk) || IS_ERR(mmdc_clk) + || IS_ERR(ocram_clk) || IS_ERR(pll2_400_clk) + || IS_ERR(pll2_200_clk) || IS_ERR(pll2_bus_clk) + || IS_ERR(periph_clk) || IS_ERR(periph_pre_clk) + || IS_ERR(periph_clk2_clk) + || IS_ERR(periph_clk2_sel_clk) + || IS_ERR(periph2_clk) || IS_ERR(periph2_pre_clk) + || IS_ERR(periph2_clk2_clk) + || IS_ERR(periph2_clk2_sel_clk)) { + dev_err(busfreq_dev, + "%s: failed to get busfreq clk\n", __func__); + return -EINVAL; + } + } + if (cpu_is_imx7d()) { osc_clk = devm_clk_get(&pdev->dev, "osc"); axi_sel_clk = devm_clk_get(&pdev->dev, "axi_sel"); @@ -633,6 +825,13 @@ static int busfreq_probe(struct platform_device *pdev) if (cpu_is_imx7d()) err = init_ddrc_ddr_settings(pdev); + else if (cpu_is_imx6sx()) { + ddr_type = imx_mmdc_get_ddr_type(); + if (ddr_type == IMX_DDR_TYPE_DDR3) + err = init_mmdc_ddr3_settings_imx6_up(pdev); + else if (ddr_type == IMX_DDR_TYPE_LPDDR2) + err = init_mmdc_lpddr2_settings(pdev); + } if (err) { dev_err(busfreq_dev, "Busfreq init of ddr controller failed\n"); |