aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2014-05-21 03:20:16 -0400
committerAnson Huang <b20788@freescale.com>2014-06-04 22:21:44 -0400
commitfa3f525fa079abb61e139bb2b84b477db6238e02 (patch)
treefdcb81af087178de08b313643be4c15874546819
parentb0ac571af5a7ad9dc92a7cb2efcdd2d3578439f3 (diff)
ENGR00315400 ARM: imx: add lpddr2 busfreq support for i.mx6sx
Add freq scaling for lpddr2 of i.MX6SX, support 3 setpoints: high -> 400MHz audio -> 100MHz low -> 24MHz Signed-off-by: Anson Huang <b20788@freescale.com>
-rw-r--r--arch/arm/mach-imx/Makefile2
-rw-r--r--arch/arm/mach-imx/busfreq-imx6.c55
-rw-r--r--arch/arm/mach-imx/busfreq_lpddr2.c17
-rw-r--r--arch/arm/mach-imx/common.h1
-rw-r--r--arch/arm/mach-imx/lpddr2_freq_imx6sx.S454
-rw-r--r--arch/arm/mach-imx/mmdc.c20
6 files changed, 523 insertions, 26 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index f4ac8d38efaa..25366995d4df 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -111,7 +111,7 @@ ifeq ($(CONFIG_ARM_IMX6_CPUFREQ),y)
111obj-y += busfreq-imx6.o 111obj-y += busfreq-imx6.o
112obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o 112obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o
113obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o imx6sl_wfi.o 113obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o imx6sl_wfi.o
114obj-$(CONFIG_SOC_IMX6SX) += ddr3_freq_imx6sx.o 114obj-$(CONFIG_SOC_IMX6SX) += ddr3_freq_imx6sx.o lpddr2_freq_imx6sx.o
115 115
116endif 116endif
117 117
diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c
index ecc020855fcc..8e640d39013e 100644
--- a/arch/arm/mach-imx/busfreq-imx6.c
+++ b/arch/arm/mach-imx/busfreq-imx6.c
@@ -46,11 +46,16 @@
46#include <linux/sched.h> 46#include <linux/sched.h>
47#include <linux/suspend.h> 47#include <linux/suspend.h>
48#include "hardware.h" 48#include "hardware.h"
49#include "common.h"
49 50
50#define LPAPM_CLK 24000000 51#define LPAPM_CLK 24000000
51#define DDR3_AUDIO_CLK 50000000 52#define DDR3_AUDIO_CLK 50000000
52#define LPDDR2_AUDIO_CLK 100000000 53#define LPDDR2_AUDIO_CLK 100000000
53 54
55#define MMDC_MDMISC_DDR_TYPE_DDR3 0
56#define MMDC_MDMISC_DDR_TYPE_LPDDR2 1
57
58static int ddr_type;
54int high_bus_freq_mode; 59int high_bus_freq_mode;
55int med_bus_freq_mode; 60int med_bus_freq_mode;
56int audio_bus_freq_mode; 61int audio_bus_freq_mode;
@@ -75,7 +80,6 @@ extern int update_ddr_freq_imx6sx(int ddr_rate);
75extern int update_lpddr2_freq(int ddr_rate); 80extern int update_lpddr2_freq(int ddr_rate);
76 81
77DEFINE_MUTEX(bus_freq_mutex); 82DEFINE_MUTEX(bus_freq_mutex);
78static DEFINE_SPINLOCK(freq_lock);
79 83
80static struct clk *mmdc_clk; 84static struct clk *mmdc_clk;
81static struct clk *pll2_400; 85static struct clk *pll2_400;
@@ -116,7 +120,10 @@ static void enter_lpm_imx6sx(void)
116 if (audio_bus_count) { 120 if (audio_bus_count) {
117 /* Need to ensure that PLL2_PFD_400M is kept ON. */ 121 /* Need to ensure that PLL2_PFD_400M is kept ON. */
118 clk_prepare_enable(pll2_400); 122 clk_prepare_enable(pll2_400);
119 update_ddr_freq_imx6sx(DDR3_AUDIO_CLK); 123 if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3)
124 update_ddr_freq_imx6sx(DDR3_AUDIO_CLK);
125 else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2)
126 update_lpddr2_freq(LPDDR2_AUDIO_CLK);
120 clk_set_parent(periph2_clk2_sel, pll3); 127 clk_set_parent(periph2_clk2_sel, pll3);
121 clk_set_parent(periph2_pre_clk, pll2_400); 128 clk_set_parent(periph2_pre_clk, pll2_400);
122 clk_set_parent(periph2_clk, periph2_pre_clk); 129 clk_set_parent(periph2_clk, periph2_pre_clk);
@@ -129,13 +136,19 @@ static void enter_lpm_imx6sx(void)
129 * tree is right, although it will not do any 136 * tree is right, although it will not do any
130 * change to hardware. 137 * change to hardware.
131 */ 138 */
132 if (high_bus_freq_mode) 139 if (high_bus_freq_mode) {
133 clk_set_rate(mmdc_clk, DDR3_AUDIO_CLK); 140 if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3)
134 141 clk_set_rate(mmdc_clk, DDR3_AUDIO_CLK);
142 else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2)
143 clk_set_rate(mmdc_clk, LPDDR2_AUDIO_CLK);
144 }
135 audio_bus_freq_mode = 1; 145 audio_bus_freq_mode = 1;
136 low_bus_freq_mode = 0; 146 low_bus_freq_mode = 0;
137 } else { 147 } else {
138 update_ddr_freq_imx6sx(LPAPM_CLK); 148 if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3)
149 update_ddr_freq_imx6sx(LPAPM_CLK);
150 else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2)
151 update_lpddr2_freq(LPAPM_CLK);
139 clk_set_parent(periph2_clk2_sel, osc_clk); 152 clk_set_parent(periph2_clk2_sel, osc_clk);
140 clk_set_parent(periph2_clk, periph2_clk2); 153 clk_set_parent(periph2_clk, periph2_clk2);
141 154
@@ -162,7 +175,10 @@ static void exit_lpm_imx6sx(void)
162 clk_set_parent(periph_pre_clk, pll2_400); 175 clk_set_parent(periph_pre_clk, pll2_400);
163 clk_set_parent(periph_clk, periph_pre_clk); 176 clk_set_parent(periph_clk, periph_pre_clk);
164 177
165 update_ddr_freq_imx6sx(ddr_normal_rate); 178 if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3)
179 update_ddr_freq_imx6sx(ddr_normal_rate);
180 else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2)
181 update_lpddr2_freq(ddr_normal_rate);
166 /* correct parent info after ddr freq change in asm code */ 182 /* correct parent info after ddr freq change in asm code */
167 clk_set_parent(periph2_clk2_sel, pll3); 183 clk_set_parent(periph2_clk2_sel, pll3);
168 clk_set_parent(periph2_pre_clk, pll2_400); 184 clk_set_parent(periph2_pre_clk, pll2_400);
@@ -186,8 +202,6 @@ static void exit_lpm_imx6sx(void)
186 202
187static void enter_lpm_imx6sl(void) 203static void enter_lpm_imx6sl(void)
188{ 204{
189 unsigned long flags;
190
191 if (high_bus_freq_mode) { 205 if (high_bus_freq_mode) {
192 pll2_org_rate = clk_get_rate(pll2); 206 pll2_org_rate = clk_get_rate(pll2);
193 /* Set periph_clk to be sourced from OSC_CLK */ 207 /* Set periph_clk to be sourced from OSC_CLK */
@@ -202,9 +216,7 @@ static void enter_lpm_imx6sl(void)
202 clk_set_rate(ahb_clk, LPAPM_CLK / 3); 216 clk_set_rate(ahb_clk, LPAPM_CLK / 3);
203 217
204 /* Set up DDR to 100MHz. */ 218 /* Set up DDR to 100MHz. */
205 spin_lock_irqsave(&freq_lock, flags);
206 update_lpddr2_freq(LPDDR2_AUDIO_CLK); 219 update_lpddr2_freq(LPDDR2_AUDIO_CLK);
207 spin_unlock_irqrestore(&freq_lock, flags);
208 220
209 /* Fix the clock tree in kernel */ 221 /* Fix the clock tree in kernel */
210 clk_set_rate(pll2, pll2_org_rate); 222 clk_set_rate(pll2, pll2_org_rate);
@@ -271,9 +283,7 @@ static void enter_lpm_imx6sl(void)
271 */ 283 */
272 clk_set_parent(step_clk, osc_clk); 284 clk_set_parent(step_clk, osc_clk);
273 /* Now set DDR to 24MHz. */ 285 /* Now set DDR to 24MHz. */
274 spin_lock_irqsave(&freq_lock, flags);
275 update_lpddr2_freq(LPAPM_CLK); 286 update_lpddr2_freq(LPAPM_CLK);
276 spin_unlock_irqrestore(&freq_lock, flags);
277 287
278 /* 288 /*
279 * Fix the clock tree in kernel. 289 * Fix the clock tree in kernel.
@@ -299,12 +309,8 @@ static void enter_lpm_imx6sl(void)
299 309
300static void exit_lpm_imx6sl(void) 310static void exit_lpm_imx6sl(void)
301{ 311{
302 unsigned long flags;
303
304 spin_lock_irqsave(&freq_lock, flags);
305 /* Change DDR freq in IRAM. */ 312 /* Change DDR freq in IRAM. */
306 update_lpddr2_freq(ddr_normal_rate); 313 update_lpddr2_freq(ddr_normal_rate);
307 spin_unlock_irqrestore(&freq_lock, flags);
308 314
309 /* 315 /*
310 * Fix the clock tree in kernel. 316 * Fix the clock tree in kernel.
@@ -978,12 +984,19 @@ static int busfreq_probe(struct platform_device *pdev)
978 register_pm_notifier(&imx_bus_freq_pm_notifier); 984 register_pm_notifier(&imx_bus_freq_pm_notifier);
979 register_reboot_notifier(&imx_busfreq_reboot_notifier); 985 register_reboot_notifier(&imx_busfreq_reboot_notifier);
980 986
981 if (cpu_is_imx6sl()) 987 if (cpu_is_imx6sl()) {
982 err = init_mmdc_lpddr2_settings(pdev); 988 err = init_mmdc_lpddr2_settings(pdev);
983 else if (cpu_is_imx6sx()) 989 } else if (cpu_is_imx6sx()) {
984 err = init_mmdc_ddr3_settings_imx6sx(pdev); 990 ddr_type = imx_mmdc_get_ddr_type();
985 else 991 /* check whether it is a DDR3 or LPDDR2 board */
992 if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3)
993 err = init_mmdc_ddr3_settings_imx6sx(pdev);
994 else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2)
995 err = init_mmdc_lpddr2_settings(pdev);
996 } else {
986 err = init_mmdc_ddr3_settings_imx6q(pdev); 997 err = init_mmdc_ddr3_settings_imx6q(pdev);
998 }
999
987 if (err) { 1000 if (err) {
988 dev_err(busfreq_dev, "Busfreq init of MMDC failed\n"); 1001 dev_err(busfreq_dev, "Busfreq init of MMDC failed\n");
989 return err; 1002 return err;
diff --git a/arch/arm/mach-imx/busfreq_lpddr2.c b/arch/arm/mach-imx/busfreq_lpddr2.c
index a3559ca4a106..1e332bf66cbf 100644
--- a/arch/arm/mach-imx/busfreq_lpddr2.c
+++ b/arch/arm/mach-imx/busfreq_lpddr2.c
@@ -46,6 +46,7 @@
46static struct device *busfreq_dev; 46static struct device *busfreq_dev;
47static void *ddr_freq_change_iram_base; 47static void *ddr_freq_change_iram_base;
48static int curr_ddr_rate; 48static int curr_ddr_rate;
49static DEFINE_SPINLOCK(freq_lock);
49 50
50void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL; 51void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL;
51 52
@@ -53,6 +54,7 @@ extern unsigned int ddr_normal_rate;
53extern int low_bus_freq_mode; 54extern int low_bus_freq_mode;
54extern int ultra_low_bus_freq_mode; 55extern int ultra_low_bus_freq_mode;
55extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode); 56extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode);
57extern void imx6sx_lpddr2_freq_change(u32 freq, int bus_freq_mode);
56 58
57extern unsigned long save_ttbr1(void); 59extern unsigned long save_ttbr1(void);
58extern void restore_ttbr1(unsigned long ttbr1); 60extern void restore_ttbr1(unsigned long ttbr1);
@@ -61,12 +63,14 @@ extern unsigned long iram_tlb_phys_addr;
61/* change the DDR frequency. */ 63/* change the DDR frequency. */
62int update_lpddr2_freq(int ddr_rate) 64int update_lpddr2_freq(int ddr_rate)
63{ 65{
64 unsigned long ttbr1; 66 unsigned long ttbr1, flags;
67
65 if (ddr_rate == curr_ddr_rate) 68 if (ddr_rate == curr_ddr_rate)
66 return 0; 69 return 0;
67 70
68 dev_dbg(busfreq_dev, "\nBus freq set to %d start...\n", ddr_rate); 71 dev_dbg(busfreq_dev, "\nBus freq set to %d start...\n", ddr_rate);
69 72
73 spin_lock_irqsave(&freq_lock, flags);
70 /* 74 /*
71 * Flush the TLB, to ensure no TLB maintenance occurs 75 * Flush the TLB, to ensure no TLB maintenance occurs
72 * when DDR is in self-refresh. 76 * when DDR is in self-refresh.
@@ -79,6 +83,7 @@ int update_lpddr2_freq(int ddr_rate)
79 restore_ttbr1(ttbr1); 83 restore_ttbr1(ttbr1);
80 84
81 curr_ddr_rate = ddr_rate; 85 curr_ddr_rate = ddr_rate;
86 spin_unlock_irqrestore(&freq_lock, flags);
82 87
83 dev_dbg(busfreq_dev, "\nBus freq set to %d done...\n", ddr_rate); 88 dev_dbg(busfreq_dev, "\nBus freq set to %d done...\n", ddr_rate);
84 89
@@ -94,8 +99,14 @@ int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev)
94 (void *)IMX_IO_P2V(iram_tlb_phys_addr) + 99 (void *)IMX_IO_P2V(iram_tlb_phys_addr) +
95 MX6SL_LPDDR2_FREQ_ADDR_OFFSET; 100 MX6SL_LPDDR2_FREQ_ADDR_OFFSET;
96 101
97 mx6_change_lpddr2_freq = (void *)fncpy(ddr_freq_change_iram_base, 102 if (cpu_is_imx6sl())
98 &mx6_lpddr2_freq_change, LPDDR2_FREQ_CODE_SIZE); 103 mx6_change_lpddr2_freq = (void *)fncpy(
104 ddr_freq_change_iram_base,
105 &mx6_lpddr2_freq_change, LPDDR2_FREQ_CODE_SIZE);
106 else if (cpu_is_imx6sx())
107 mx6_change_lpddr2_freq = (void *)fncpy(
108 ddr_freq_change_iram_base,
109 &imx6sx_lpddr2_freq_change, LPDDR2_FREQ_CODE_SIZE);
99 110
100 curr_ddr_rate = ddr_normal_rate; 111 curr_ddr_rate = ddr_normal_rate;
101 112
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 80a132961671..b0872d986b7a 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -153,6 +153,7 @@ extern int imx6_set_lpm(enum mxc_cpu_pwr_mode mode);
153extern void imx6_set_cache_lpm_in_wait(bool enable); 153extern void imx6_set_cache_lpm_in_wait(bool enable);
154extern void imx6sl_set_wait_clk(bool enter); 154extern void imx6sl_set_wait_clk(bool enter);
155extern void imx6_enet_mac_init(const char *compatible); 155extern void imx6_enet_mac_init(const char *compatible);
156extern int imx_mmdc_get_ddr_type(void);
156 157
157extern void imx_cpu_die(unsigned int cpu); 158extern void imx_cpu_die(unsigned int cpu);
158extern int imx_cpu_kill(unsigned int cpu); 159extern int imx_cpu_kill(unsigned int cpu);
diff --git a/arch/arm/mach-imx/lpddr2_freq_imx6sx.S b/arch/arm/mach-imx/lpddr2_freq_imx6sx.S
new file mode 100644
index 000000000000..94e7db554283
--- /dev/null
+++ b/arch/arm/mach-imx/lpddr2_freq_imx6sx.S
@@ -0,0 +1,454 @@
1/*
2 * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <linux/linkage.h>
20#include "hardware.h"
21
22#define CCM_CBCDR 0x14
23#define CCM_CBCMR 0x18
24#define CCM_CSCMR1 0x1c
25#define CCM_CDHIPR 0x48
26
27#define L2_CACHE_SYNC 0x730
28
29#define MMDC0_MDPDC 0x4
30#define MMDC0_MAPSR 0x404
31#define MMDC0_MADPCR0 0x410
32
33 .macro wait_for_ccm_handshake
34
351:
36 ldr r8, [r2, #CCM_CDHIPR]
37 cmp r8, #0
38 bne 1b
39
40 .endm
41
42 .macro switch_to_24MHz
43
44 /* periph2_clk2 sel to OSC_CLK */
45 ldr r8, [r2, #CCM_CBCMR]
46 orr r8, r8, #(1 << 20)
47 str r8, [r2, #CCM_CBCMR]
48
49 /* periph2_clk2_podf to 0 */
50 ldr r8, [r2, #CCM_CBCDR]
51 bic r8, r8, #0x7
52 str r8, [r2, #CCM_CBCDR]
53
54 /* periph2_clk sel to periph2_clk2 */
55 ldr r8, [r2, #CCM_CBCDR]
56 orr r8, r8, #(0x1 << 26)
57 str r8, [r2, #CCM_CBCDR]
58
59 wait_for_ccm_handshake
60
61 /* fabric_mmdc_podf to 0 */
62 ldr r8, [r2, #CCM_CBCDR]
63 bic r8, r8, #(0x7 << 3)
64 str r8, [r2, #CCM_CBCDR]
65
66 wait_for_ccm_handshake
67
68 .endm
69
70 .macro switch_to_100MHz
71
72 /* check whether periph2_clk is from top path */
73 ldr r8, [r2, #CCM_CBCDR]
74 ands r8, #(1 << 26)
75 beq skip_periph2_clk2_switch_100m
76
77 /* now switch periph2_clk back. */
78 ldr r8, [r2, #CCM_CBCDR]
79 bic r8, r8, #(1 << 26)
80 str r8, [r2, #CCM_CBCDR]
81
82 wait_for_ccm_handshake
83
84 /*
85 * on i.MX6SX, pre_periph2_clk will be always from
86 * pll2_pfd2, so no need to set pre_periph2_clk
87 * parent, just set the mmdc divider directly.
88 */
89skip_periph2_clk2_switch_100m:
90
91 /* fabric_mmdc_podf to 3 so that mmdc is 400 / 4 = 100MHz */
92 ldr r8, [r2, #CCM_CBCDR]
93 bic r8, r8, #(0x7 << 3)
94 orr r8, r8, #(0x3 << 3)
95 str r8, [r2, #CCM_CBCDR]
96
97 wait_for_ccm_handshake
98
99 .endm
100
101 .macro switch_to_400MHz
102
103 /* check whether periph2_clk is from top path */
104 ldr r8, [r2, #CCM_CBCDR]
105 ands r8, #(1 << 26)
106 beq skip_periph2_clk2_switch_400m
107
108 /* now switch periph2_clk back. */
109 ldr r8, [r2, #CCM_CBCDR]
110 bic r8, r8, #(1 << 26)
111 str r8, [r2, #CCM_CBCDR]
112
113 wait_for_ccm_handshake
114
115 /*
116 * on i.MX6SX, pre_periph2_clk will be always from
117 * pll2_pfd2, so no need to set pre_periph2_clk
118 * parent, just set the mmdc divider directly.
119 */
120skip_periph2_clk2_switch_400m:
121
122 /* fabric_mmdc_podf to 0 */
123 ldr r8, [r2, #CCM_CBCDR]
124 bic r8, r8, #(0x7 << 3)
125 str r8, [r2, #CCM_CBCDR]
126
127 wait_for_ccm_handshake
128
129 .endm
130
131 .macro mmdc_clk_lower_100MHz
132
133 /*
134 * Prior to reducing the DDR frequency (at 528/400 MHz),
135 * read the Measure unit count bits (MU_UNIT_DEL_NUM)
136 */
137 ldr r8, =0x8B8
138 ldr r6, [r5, r8]
139 /* Original MU unit count */
140 mov r6, r6, LSR #16
141 ldr r4, =0x3FF
142 and r6, r6, r4
143 /* Original MU unit count * 2 */
144 mov r7, r6, LSL #1
145 /*
146 * Bypass the automatic measure unit when below 100 MHz
147 * by setting the Measure unit bypass enable bit (MU_BYP_EN)
148 */
149 ldr r6, [r5, r8]
150 orr r6, r6, #0x400
151 str r6, [r5, r8]
152 /*
153 * Double the measure count value read in step 1 and program it in the
154 * measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit
155 * Register for the reduced frequency operation below 100 MHz
156 */
157 ldr r6, [r5, r8]
158 ldr r4, =0x3FF
159 bic r6, r6, r4
160 orr r6, r6, r7
161 str r6, [r5, r8]
162 /* Now perform a Force Measurement. */
163 ldr r6, [r5, r8]
164 orr r6, r6, #0x800
165 str r6, [r5, r8]
166 /* Wait for FRC_MSR to clear. */
167force_measure:
168 ldr r6, [r5, r8]
169 and r6, r6, #0x800
170 cmp r6, #0x0
171 bne force_measure
172
173 /* For freq lower than 100MHz, need to set RALAT to 2 */
174 ldr r6, [r5, #0x18]
175 bic r6, r6, #(0x7 << 6)
176 orr r6, r6, #(0x2 << 6)
177 str r6, [r5, #0x18]
178
179 .endm
180
181 .macro mmdc_clk_above_100MHz
182
183 /* Make sure that the PHY measurement unit is NOT in bypass mode */
184 ldr r8, =0x8B8
185 ldr r6, [r5, r8]
186 bic r6, r6, #0x400
187 str r6, [r5, r8]
188 /* Now perform a Force Measurement. */
189 ldr r6, [r5, r8]
190 orr r6, r6, #0x800
191 str r6, [r5, r8]
192 /* Wait for FRC_MSR to clear. */
193force_measure1:
194 ldr r6, [r5, r8]
195 and r6, r6, #0x800
196 cmp r6, #0x0
197 bne force_measure1
198
199 /* For freq higher than 100MHz, need to set RALAT to 5 */
200 ldr r6, [r5, #0x18]
201 bic r6, r6, #(0x7 << 6)
202 orr r6, r6, #(0x5 << 6)
203 str r6, [r5, #0x18]
204
205 .endm
206
207 .align 3
208ENTRY(imx6sx_lpddr2_freq_change)
209
210 push {r2 - r8}
211
212 /*
213 * To ensure no page table walks occur in DDR, we
214 * have a another page table stored in IRAM that only
215 * contains entries pointing to IRAM, AIPS1 and AIPS2.
216 * We need to set the TTBR1 to the new IRAM TLB.
217 * Do the following steps:
218 * 1. Flush the Branch Target Address Cache (BTAC)
219 * 2. Set TTBR1 to point to IRAM page table.
220 * 3. Disable page table walks in TTBR0 (PD0 = 1)
221 * 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
222 * and 2-4G is translated by TTBR1.
223 */
224
225 ldr r6, =iram_tlb_phys_addr
226 ldr r7, [r6]
227
228 /* Flush the Branch Target Address Cache (BTAC) */
229 ldr r6, =0x0
230 mcr p15, 0, r6, c7, c1, 6
231
232 /* Disable Branch Prediction, Z bit in SCTLR. */
233 mrc p15, 0, r6, c1, c0, 0
234 bic r6, r6, #0x800
235 mcr p15, 0, r6, c1, c0, 0
236
237 dsb
238 isb
239 /* Store the IRAM table in TTBR1 */
240 mcr p15, 0, r7, c2, c0, 1
241
242 /* Read TTBCR and set PD0=1, N = 1 */
243 mrc p15, 0, r6, c2, c0, 2
244 orr r6, r6, #0x11
245 mcr p15, 0, r6, c2, c0, 2
246
247 dsb
248 isb
249
250 /* flush the TLB */
251 ldr r6, =0x0
252 mcr p15, 0, r6, c8, c3, 0
253
254 /* Disable L1 data cache. */
255 mrc p15, 0, r6, c1, c0, 0
256 bic r6, r6, #0x4
257 mcr p15, 0, r6, c1, c0, 0
258
259 dsb
260 isb
261
262#ifdef CONFIG_CACHE_L2X0
263 /*
264 * Need to make sure the buffers in L2 are drained.
265 * Performing a sync operation does this.
266 */
267 ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
268 mov r6, #0x0
269 str r6, [r7, #L2_CACHE_SYNC]
270
271 /*
272 * The second dsb might be needed to keep cache sync (device write)
273 * ordering with the memory accesses before it.
274 */
275 dsb
276 isb
277
278 /* Disable L2. */
279 str r6, [r7, #0x100]
280#endif
281
282 ldr r2, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
283 ldr r3, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
284 ldr r5, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
285
286 /* Disable Automatic power savings. */
287 ldr r6, [r5, #MMDC0_MAPSR]
288 orr r6, r6, #0x1
289 str r6, [r5, #MMDC0_MAPSR]
290
291 /* MMDC0_MDPDC disable power down timer */
292 ldr r6, [r5, #MMDC0_MDPDC]
293 bic r6, r6, #0xff00
294 str r6, [r5, #MMDC0_MDPDC]
295
296 /* Delay for a while */
297 ldr r8, =10
298delay:
299 ldr r7, =0
300cont:
301 ldr r6, [r5, r7]
302 add r7, r7, #4
303 cmp r7, #16
304 bne cont
305 sub r8, r8, #1
306 cmp r8, #0
307 bgt delay
308
309 /* Make the DDR explicitly enter self-refresh. */
310 ldr r6, [r5, #MMDC0_MAPSR]
311 orr r6, r6, #0x200000
312 str r6, [r5, #MMDC0_MAPSR]
313
314poll_dvfs_set_1:
315 ldr r6, [r5, #MMDC0_MAPSR]
316 and r6, r6, #0x2000000
317 cmp r6, #0x2000000
318 bne poll_dvfs_set_1
319
320 /* set SBS step-by-step mode */
321 ldr r6, [r5, #MMDC0_MADPCR0]
322 orr r6, r6, #0x100
323 str r6, [r5, #MMDC0_MADPCR0]
324
325 ldr r6, =100000000
326 cmp r0, r6
327 bgt set_ddr_mu_above_100
328 mmdc_clk_lower_100MHz
329
330set_ddr_mu_above_100:
331 ldr r6, =24000000
332 cmp r0, r6
333 beq set_to_24MHz
334
335 ldr r6, =100000000
336 cmp r0, r6
337 beq set_to_100MHz
338
339 switch_to_400MHz
340
341 mmdc_clk_above_100MHz
342
343 b done
344
345set_to_24MHz:
346 switch_to_24MHz
347 b done
348set_to_100MHz:
349 switch_to_100MHz
350done:
351 /* clear DVFS - exit from self refresh mode */
352 ldr r6, [r5, #MMDC0_MAPSR]
353 bic r6, r6, #0x200000
354 str r6, [r5, #MMDC0_MAPSR]
355
356poll_dvfs_clear_1:
357 ldr r6, [r5, #MMDC0_MAPSR]
358 and r6, r6, #0x2000000
359 cmp r6, #0x2000000
360 beq poll_dvfs_clear_1
361
362 /* Enable Automatic power savings. */
363 ldr r6, [r5, #MMDC0_MAPSR]
364 bic r6, r6, #0x1
365 str r6, [r5, #MMDC0_MAPSR]
366
367 ldr r6, =24000000
368 cmp r0, r6
369 beq skip_power_down
370
371 /* Enable MMDC power down timer. */
372 ldr r6, [r5, #MMDC0_MDPDC]
373 orr r6, r6, #0x5500
374 str r6, [r5, #MMDC0_MDPDC]
375
376skip_power_down:
377 /* clear SBS - unblock DDR accesses */
378 ldr r6, [r5, #MMDC0_MADPCR0]
379 bic r6, r6, #0x100
380 str r6, [r5, #MMDC0_MADPCR0]
381
382
383#ifdef CONFIG_CACHE_L2X0
384 /* Enable L2. */
385 ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
386 ldr r6, =0x1
387 str r6, [r7, #0x100]
388#endif
389
390 /* Enable L1 data cache. */
391 mrc p15, 0, r6, c1, c0, 0
392 orr r6, r6, #0x4
393 mcr p15, 0, r6, c1, c0, 0
394
395 /* Restore the TTBCR */
396 dsb
397 isb
398
399 /* Read TTBCR and set PD0=0, N = 0 */
400 mrc p15, 0, r6, c2, c0, 2
401 bic r6, r6, #0x11
402 mcr p15, 0, r6, c2, c0, 2
403 dsb
404 isb
405
406 /* flush the TLB */
407 ldr r6, =0x0
408 mcr p15, 0, r6, c8, c3, 0
409
410 dsb
411 isb
412
413 /* Enable Branch Prediction, Z bit in SCTLR. */
414 mrc p15, 0, r6, c1, c0, 0
415 orr r6, r6, #0x800
416 mcr p15, 0, r6, c1, c0, 0
417
418 /* Flush the Branch Target Address Cache (BTAC) */
419 ldr r6, =0x0
420 mcr p15, 0, r6, c7, c1, 6
421
422 nop
423 nop
424 nop
425 nop
426 nop
427
428 nop
429 nop
430 nop
431 nop
432 nop
433
434 nop
435 nop
436 nop
437 nop
438 nop
439
440 nop
441 nop
442 nop
443 nop
444 nop
445
446 nop
447 nop
448 nop
449 nop
450 nop
451
452 /* Restore registers */
453 pop {r2 - r8}
454 mov pc, lr
diff --git a/arch/arm/mach-imx/mmdc.c b/arch/arm/mach-imx/mmdc.c
index 7a9686ad994c..a6f547b3aa33 100644
--- a/arch/arm/mach-imx/mmdc.c
+++ b/arch/arm/mach-imx/mmdc.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright 2011 Freescale Semiconductor, Inc. 2 * Copyright 2011-2014 Freescale Semiconductor, Inc.
3 * Copyright 2011 Linaro Ltd. 3 * Copyright 2011 Linaro Ltd.
4 * 4 *
5 * The code contained herein is licensed under the GNU General Public 5 * The code contained herein is licensed under the GNU General Public
@@ -21,6 +21,12 @@
21#define BP_MMDC_MAPSR_PSD 0 21#define BP_MMDC_MAPSR_PSD 0
22#define BP_MMDC_MAPSR_PSS 4 22#define BP_MMDC_MAPSR_PSS 4
23 23
24#define MMDC_MDMISC 0x18
25#define BM_MMDC_MDMISC_DDR_TYPE 0x18
26#define BP_MMDC_MDMISC_DDR_TYPE 0x3
27
28static int ddr_type;
29
24static int imx_mmdc_probe(struct platform_device *pdev) 30static int imx_mmdc_probe(struct platform_device *pdev)
25{ 31{
26 struct device_node *np = pdev->dev.of_node; 32 struct device_node *np = pdev->dev.of_node;
@@ -31,6 +37,13 @@ static int imx_mmdc_probe(struct platform_device *pdev)
31 mmdc_base = of_iomap(np, 0); 37 mmdc_base = of_iomap(np, 0);
32 WARN_ON(!mmdc_base); 38 WARN_ON(!mmdc_base);
33 39
40 reg = mmdc_base + MMDC_MDMISC;
41 /* Get ddr type */
42 val = readl_relaxed(reg);
43 val &= BM_MMDC_MDMISC_DDR_TYPE;
44 val >>= BP_MMDC_MDMISC_DDR_TYPE;
45 ddr_type = val;
46
34 reg = mmdc_base + MMDC_MAPSR; 47 reg = mmdc_base + MMDC_MAPSR;
35 48
36 /* Enable automatic power saving */ 49 /* Enable automatic power saving */
@@ -51,6 +64,11 @@ static int imx_mmdc_probe(struct platform_device *pdev)
51 return 0; 64 return 0;
52} 65}
53 66
67int imx_mmdc_get_ddr_type(void)
68{
69 return ddr_type;
70}
71
54static struct of_device_id imx_mmdc_dt_ids[] = { 72static struct of_device_id imx_mmdc_dt_ids[] = {
55 { .compatible = "fsl,imx6q-mmdc", }, 73 { .compatible = "fsl,imx6q-mmdc", },
56 { /* sentinel */ } 74 { /* sentinel */ }