summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx/busfreq-imx.c
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2015-08-20 12:10:27 +0800
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:22:02 +0800
commit7a9b1a14855fb74efaf90fdeafb34b33209a97e1 (patch)
treeef89a9d064fdcf1f78baa46fbe06e7d05864616b /arch/arm/mach-imx/busfreq-imx.c
parentdbcb068cbfc2a255b2e1638748749a1b5409787e (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.c199
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");