diff options
author | Anson Huang <b20788@freescale.com> | 2014-05-21 03:20:16 -0400 |
---|---|---|
committer | Anson Huang <b20788@freescale.com> | 2014-06-04 22:21:44 -0400 |
commit | fa3f525fa079abb61e139bb2b84b477db6238e02 (patch) | |
tree | fdcb81af087178de08b313643be4c15874546819 | |
parent | b0ac571af5a7ad9dc92a7cb2efcdd2d3578439f3 (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/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx6.c | 55 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq_lpddr2.c | 17 | ||||
-rw-r--r-- | arch/arm/mach-imx/common.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/lpddr2_freq_imx6sx.S | 454 | ||||
-rw-r--r-- | arch/arm/mach-imx/mmdc.c | 20 |
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) | |||
111 | obj-y += busfreq-imx6.o | 111 | obj-y += busfreq-imx6.o |
112 | obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o | 112 | obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o |
113 | obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o imx6sl_wfi.o | 113 | obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o imx6sl_wfi.o |
114 | obj-$(CONFIG_SOC_IMX6SX) += ddr3_freq_imx6sx.o | 114 | obj-$(CONFIG_SOC_IMX6SX) += ddr3_freq_imx6sx.o lpddr2_freq_imx6sx.o |
115 | 115 | ||
116 | endif | 116 | endif |
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 | |||
58 | static int ddr_type; | ||
54 | int high_bus_freq_mode; | 59 | int high_bus_freq_mode; |
55 | int med_bus_freq_mode; | 60 | int med_bus_freq_mode; |
56 | int audio_bus_freq_mode; | 61 | int audio_bus_freq_mode; |
@@ -75,7 +80,6 @@ extern int update_ddr_freq_imx6sx(int ddr_rate); | |||
75 | extern int update_lpddr2_freq(int ddr_rate); | 80 | extern int update_lpddr2_freq(int ddr_rate); |
76 | 81 | ||
77 | DEFINE_MUTEX(bus_freq_mutex); | 82 | DEFINE_MUTEX(bus_freq_mutex); |
78 | static DEFINE_SPINLOCK(freq_lock); | ||
79 | 83 | ||
80 | static struct clk *mmdc_clk; | 84 | static struct clk *mmdc_clk; |
81 | static struct clk *pll2_400; | 85 | static 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 | ||
187 | static void enter_lpm_imx6sl(void) | 203 | static 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 | ||
300 | static void exit_lpm_imx6sl(void) | 310 | static 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 @@ | |||
46 | static struct device *busfreq_dev; | 46 | static struct device *busfreq_dev; |
47 | static void *ddr_freq_change_iram_base; | 47 | static void *ddr_freq_change_iram_base; |
48 | static int curr_ddr_rate; | 48 | static int curr_ddr_rate; |
49 | static DEFINE_SPINLOCK(freq_lock); | ||
49 | 50 | ||
50 | void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL; | 51 | void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL; |
51 | 52 | ||
@@ -53,6 +54,7 @@ extern unsigned int ddr_normal_rate; | |||
53 | extern int low_bus_freq_mode; | 54 | extern int low_bus_freq_mode; |
54 | extern int ultra_low_bus_freq_mode; | 55 | extern int ultra_low_bus_freq_mode; |
55 | extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode); | 56 | extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode); |
57 | extern void imx6sx_lpddr2_freq_change(u32 freq, int bus_freq_mode); | ||
56 | 58 | ||
57 | extern unsigned long save_ttbr1(void); | 59 | extern unsigned long save_ttbr1(void); |
58 | extern void restore_ttbr1(unsigned long ttbr1); | 60 | extern 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. */ |
62 | int update_lpddr2_freq(int ddr_rate) | 64 | int 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); | |||
153 | extern void imx6_set_cache_lpm_in_wait(bool enable); | 153 | extern void imx6_set_cache_lpm_in_wait(bool enable); |
154 | extern void imx6sl_set_wait_clk(bool enter); | 154 | extern void imx6sl_set_wait_clk(bool enter); |
155 | extern void imx6_enet_mac_init(const char *compatible); | 155 | extern void imx6_enet_mac_init(const char *compatible); |
156 | extern int imx_mmdc_get_ddr_type(void); | ||
156 | 157 | ||
157 | extern void imx_cpu_die(unsigned int cpu); | 158 | extern void imx_cpu_die(unsigned int cpu); |
158 | extern int imx_cpu_kill(unsigned int cpu); | 159 | extern 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 | |||
35 | 1: | ||
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 | */ | ||
89 | skip_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 | */ | ||
120 | skip_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. */ | ||
167 | force_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. */ | ||
193 | force_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 | ||
208 | ENTRY(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 | ||
298 | delay: | ||
299 | ldr r7, =0 | ||
300 | cont: | ||
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 | |||
314 | poll_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 | |||
330 | set_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 | |||
345 | set_to_24MHz: | ||
346 | switch_to_24MHz | ||
347 | b done | ||
348 | set_to_100MHz: | ||
349 | switch_to_100MHz | ||
350 | done: | ||
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 | |||
356 | poll_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 | |||
376 | skip_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 | |||
28 | static int ddr_type; | ||
29 | |||
24 | static int imx_mmdc_probe(struct platform_device *pdev) | 30 | static 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 | ||
67 | int imx_mmdc_get_ddr_type(void) | ||
68 | { | ||
69 | return ddr_type; | ||
70 | } | ||
71 | |||
54 | static struct of_device_id imx_mmdc_dt_ids[] = { | 72 | static struct of_device_id imx_mmdc_dt_ids[] = { |
55 | { .compatible = "fsl,imx6q-mmdc", }, | 73 | { .compatible = "fsl,imx6q-mmdc", }, |
56 | { /* sentinel */ } | 74 | { /* sentinel */ } |