diff options
Diffstat (limited to 'firmware/target/arm/imx233/ssp-imx233.c')
-rw-r--r-- | firmware/target/arm/imx233/ssp-imx233.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/ssp-imx233.c b/firmware/target/arm/imx233/ssp-imx233.c new file mode 100644 index 0000000000..c6be869ce6 --- /dev/null +++ b/firmware/target/arm/imx233/ssp-imx233.c | |||
@@ -0,0 +1,307 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 by amaury Pouly | ||
11 | * | ||
12 | * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing | ||
13 | * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License | ||
17 | * as published by the Free Software Foundation; either version 2 | ||
18 | * of the License, or (at your option) any later version. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | #include "system.h" | ||
25 | #include "kernel.h" | ||
26 | #include "ssp-imx233.h" | ||
27 | #include "clkctrl-imx233.h" | ||
28 | #include "pinctrl-imx233.h" | ||
29 | #include "dma-imx233.h" | ||
30 | |||
31 | /* Used for DMA */ | ||
32 | struct ssp_dma_command_t | ||
33 | { | ||
34 | struct apb_dma_command_t dma; | ||
35 | /* PIO words */ | ||
36 | uint32_t ctrl0; | ||
37 | uint32_t cmd0; | ||
38 | uint32_t cmd1; | ||
39 | }; | ||
40 | |||
41 | static int ssp_in_use = 0; | ||
42 | static struct mutex ssp_mutex[2]; | ||
43 | static struct semaphore ssp_sema[2]; | ||
44 | static struct ssp_dma_command_t ssp_dma_cmd[2]; | ||
45 | |||
46 | void INT_SSP(int ssp) | ||
47 | { | ||
48 | /* reset dma channel on error */ | ||
49 | if(imx233_dma_is_channel_error_irq(APB_SSP(ssp))) | ||
50 | imx233_dma_reset_channel(APB_SSP(ssp)); | ||
51 | /* clear irq flags */ | ||
52 | imx233_dma_clear_channel_interrupt(APB_SSP(ssp)); | ||
53 | semaphore_release(&ssp_sema[ssp - 1]); | ||
54 | } | ||
55 | |||
56 | void INT_SSP1_DMA(void) | ||
57 | { | ||
58 | INT_SSP(1); | ||
59 | } | ||
60 | |||
61 | void INT_SSP2_DMA(void) | ||
62 | { | ||
63 | INT_SSP(2); | ||
64 | } | ||
65 | |||
66 | void INT_SSP1_ERROR(void) | ||
67 | { | ||
68 | panicf("ssp1 error"); | ||
69 | } | ||
70 | |||
71 | void INT_SSP2_ERROR(void) | ||
72 | { | ||
73 | panicf("ssp2 error"); | ||
74 | } | ||
75 | |||
76 | void imx233_ssp_init(void) | ||
77 | { | ||
78 | /* power down */ | ||
79 | __REG_SET(HW_SSP_CTRL0(1)) = __BLOCK_CLKGATE; | ||
80 | __REG_SET(HW_SSP_CTRL0(2)) = __BLOCK_CLKGATE; | ||
81 | |||
82 | ssp_in_use = 0; | ||
83 | semaphore_init(&ssp_sema[0], 1, 0); | ||
84 | semaphore_init(&ssp_sema[1], 1, 0); | ||
85 | mutex_init(&ssp_mutex[0]); | ||
86 | mutex_init(&ssp_mutex[1]); | ||
87 | } | ||
88 | |||
89 | void imx233_ssp_start(int ssp) | ||
90 | { | ||
91 | /* Gate block */ | ||
92 | __REG_CLR(HW_SSP_CTRL0(ssp)) = __BLOCK_CLKGATE; | ||
93 | while(HW_SSP_CTRL0(ssp) & __BLOCK_CLKGATE); | ||
94 | /* Gate dma channel */ | ||
95 | imx233_dma_clkgate_channel(APB_SSP(ssp), true); | ||
96 | /* If first block to start, start SSP clock */ | ||
97 | if(ssp_in_use == 0) | ||
98 | { | ||
99 | /* fracdiv = 18 => clk_io = pll = 480Mhz | ||
100 | * intdiv = 4 => clk_ssp = 120Mhz */ | ||
101 | imx233_set_fractional_divisor(CLK_IO, 18); | ||
102 | imx233_enable_clock(CLK_SSP, false); | ||
103 | imx233_set_clock_divisor(CLK_SSP, 4); | ||
104 | imx233_set_bypass_pll(CLK_SSP, false); /* use IO */ | ||
105 | imx233_enable_clock(CLK_SSP, true); | ||
106 | } | ||
107 | ssp_in_use++; | ||
108 | } | ||
109 | |||
110 | void imx233_ssp_stop(int ssp) | ||
111 | { | ||
112 | /* Gate off */ | ||
113 | __REG_SET(HW_SSP_CTRL0(ssp)) = __BLOCK_CLKGATE; | ||
114 | /* Gate off dma */ | ||
115 | imx233_dma_clkgate_channel(APB_SSP(ssp), false); | ||
116 | /* If last block to stop, stop SSP clock */ | ||
117 | ssp_in_use--; | ||
118 | if(ssp_in_use == 0) | ||
119 | { | ||
120 | imx233_enable_clock(CLK_SSP, false); | ||
121 | imx233_set_fractional_divisor(CLK_IO, 0); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | void imx233_ssp_softreset(int ssp) | ||
126 | { | ||
127 | imx233_reset_block(&HW_SSP_CTRL0(ssp)); | ||
128 | } | ||
129 | |||
130 | void imx233_ssp_set_timings(int ssp, int divide, int rate) | ||
131 | { | ||
132 | __REG_CLR(HW_SSP_TIMING(ssp)) = | ||
133 | HW_SSP_TIMING__CLOCK_DIVIDE_BM | HW_SSP_TIMING__CLOCK_RATE_BM; | ||
134 | __REG_SET(HW_SSP_TIMING(ssp)) = | ||
135 | divide << HW_SSP_TIMING__CLOCK_DIVIDE_BP | rate; | ||
136 | } | ||
137 | |||
138 | void imx233_ssp_set_timeout(int ssp, int timeout) | ||
139 | { | ||
140 | __REG_CLR(HW_SSP_TIMING(ssp)) = HW_SSP_TIMING__CLOCK_TIMEOUT_BM; | ||
141 | __REG_SET(HW_SSP_TIMING(ssp)) = | ||
142 | timeout << HW_SSP_TIMING__CLOCK_TIMEOUT_BP; | ||
143 | } | ||
144 | |||
145 | #if 0 | ||
146 | static void setup_ssp_sd_pins(int ssp) | ||
147 | { | ||
148 | imx233_set_pin_function(1, 29, PINCTRL_FUNCTION_GPIO); | ||
149 | imx233_enable_gpio_output(1, 29, true); | ||
150 | imx233_set_gpio_output(1, 29, false); | ||
151 | |||
152 | if(ssp == 1) | ||
153 | { | ||
154 | /* SSP_SCK: drive 8mA */ | ||
155 | imx233_set_pin_drive_strength(2, 6, PINCTRL_DRIVE_8mA); | ||
156 | /* SSP_{SCK,DATA{3,2,1,0},DETECT,CMD} */ | ||
157 | imx233_set_pin_function(2, 6, PINCTRL_FUNCTION_MAIN); | ||
158 | imx233_set_pin_function(2, 5, PINCTRL_FUNCTION_MAIN); | ||
159 | imx233_set_pin_function(2, 4, PINCTRL_FUNCTION_MAIN); | ||
160 | imx233_set_pin_function(2, 3, PINCTRL_FUNCTION_MAIN); | ||
161 | imx233_set_pin_function(2, 2, PINCTRL_FUNCTION_MAIN); | ||
162 | imx233_set_pin_function(2, 1, PINCTRL_FUNCTION_MAIN); | ||
163 | imx233_set_pin_function(2, 0, PINCTRL_FUNCTION_MAIN); | ||
164 | /* SSP_CMD: pullup */ | ||
165 | imx233_enable_pin_pullup(2, 0, true); | ||
166 | imx233_enable_pin_pullup(2, 2, true); | ||
167 | imx233_enable_pin_pullup(2, 3, true); | ||
168 | imx233_enable_pin_pullup(2, 4, true); | ||
169 | imx233_enable_pin_pullup(2, 5, true); | ||
170 | } | ||
171 | else | ||
172 | { | ||
173 | |||
174 | } | ||
175 | } | ||
176 | #endif | ||
177 | |||
178 | void imx233_ssp_setup_ssp2_sd_mmc_pins(bool enable_pullups, unsigned bus_width, | ||
179 | unsigned drive_strength) | ||
180 | { | ||
181 | /* SSP_{CMD,SCK} */ | ||
182 | imx233_set_pin_drive_strength(0, 20, drive_strength); | ||
183 | imx233_set_pin_drive_strength(0, 24, drive_strength); | ||
184 | imx233_set_pin_function(0, 20, PINCTRL_FUNCTION_ALT2); | ||
185 | imx233_set_pin_function(0, 24, PINCTRL_FUNCTION_ALT2); | ||
186 | imx233_enable_pin_pullup(0, 20, enable_pullups); | ||
187 | /* SSP_DATA{0-7}*/ | ||
188 | imx233_set_pin_drive_strength(0, 0, drive_strength); | ||
189 | imx233_set_pin_function(0, 0, PINCTRL_FUNCTION_ALT2); | ||
190 | imx233_enable_pin_pullup(0, 0, enable_pullups); | ||
191 | |||
192 | if(bus_width >= 4) | ||
193 | { | ||
194 | imx233_set_pin_drive_strength(0, 1, drive_strength); | ||
195 | imx233_set_pin_drive_strength(0, 2, drive_strength); | ||
196 | imx233_set_pin_drive_strength(0, 3, drive_strength); | ||
197 | imx233_set_pin_function(0, 1, PINCTRL_FUNCTION_ALT2); | ||
198 | imx233_set_pin_function(0, 2, PINCTRL_FUNCTION_ALT2); | ||
199 | imx233_set_pin_function(0, 3, PINCTRL_FUNCTION_ALT2); | ||
200 | imx233_enable_pin_pullup(0, 1, enable_pullups); | ||
201 | imx233_enable_pin_pullup(0, 2, enable_pullups); | ||
202 | imx233_enable_pin_pullup(0, 3, enable_pullups); | ||
203 | } | ||
204 | if(bus_width >= 8) | ||
205 | { | ||
206 | imx233_set_pin_drive_strength(0, 4, drive_strength); | ||
207 | imx233_set_pin_drive_strength(0, 5, drive_strength); | ||
208 | imx233_set_pin_drive_strength(0, 6, drive_strength); | ||
209 | imx233_set_pin_drive_strength(0, 7, drive_strength); | ||
210 | imx233_set_pin_function(0, 4, PINCTRL_FUNCTION_ALT2); | ||
211 | imx233_set_pin_function(0, 5, PINCTRL_FUNCTION_ALT2); | ||
212 | imx233_set_pin_function(0, 6, PINCTRL_FUNCTION_ALT2); | ||
213 | imx233_set_pin_function(0, 7, PINCTRL_FUNCTION_ALT2); | ||
214 | imx233_enable_pin_pullup(0, 4, enable_pullups); | ||
215 | imx233_enable_pin_pullup(0, 5, enable_pullups); | ||
216 | imx233_enable_pin_pullup(0, 6, enable_pullups); | ||
217 | imx233_enable_pin_pullup(0, 7, enable_pullups); | ||
218 | } | ||
219 | |||
220 | imx233_enable_gpio_output_mask(0, 0x11000ff, false); | ||
221 | imx233_set_gpio_output_mask(0, 0x11000ff, false); | ||
222 | } | ||
223 | |||
224 | void imx233_ssp_set_mode(int ssp, unsigned mode) | ||
225 | { | ||
226 | switch(mode) | ||
227 | { | ||
228 | case HW_SSP_CTRL1__SSP_MODE__SD_MMC: | ||
229 | /* clear mode and word length */ | ||
230 | __REG_CLR(HW_SSP_CTRL1(ssp)) = | ||
231 | HW_SSP_CTRL1__SSP_MODE_BM | HW_SSP_CTRL1__WORD_LENGTH_BM; | ||
232 | /* set mode, set word length to 8-bit, polarity and enable dma */ | ||
233 | __REG_SET(HW_SSP_CTRL1(ssp)) = mode | | ||
234 | HW_SSP_CTRL1__WORD_LENGTH__EIGHT_BITS << HW_SSP_CTRL1__WORD_LENGTH_BP | | ||
235 | HW_SSP_CTRL1__POLARITY | HW_SSP_CTRL1__DMA_ENABLE; | ||
236 | break; | ||
237 | default: return; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | enum imx233_ssp_error_t imx233_ssp_sd_mmc_transfer(int ssp, uint8_t cmd, uint32_t cmd_arg, | ||
242 | enum imx233_ssp_resp_t resp, void *buffer, int xfer_size, bool read, uint32_t *resp_ptr) | ||
243 | { | ||
244 | mutex_lock(&ssp_mutex[ssp - 1]); | ||
245 | /* Enable all interrupts */ | ||
246 | imx233_enable_interrupt(INT_SRC_SSP_DMA(ssp), true); | ||
247 | imx233_dma_enable_channel_interrupt(APB_SSP(ssp), true); | ||
248 | /* Assume only one block so ignore block_count and block_size */ | ||
249 | ssp_dma_cmd[ssp - 1].cmd0 = cmd | HW_SSP_CMD0__APPEND_8CYC; | ||
250 | ssp_dma_cmd[ssp - 1].cmd1 = cmd_arg; | ||
251 | /* setup all flags and run */ | ||
252 | ssp_dma_cmd[ssp - 1].ctrl0 = xfer_size | HW_SSP_CTRL0__ENABLE | | ||
253 | HW_SSP_CTRL0__IGNORE_CRC | | ||
254 | (resp != SSP_NO_RESP ? HW_SSP_CTRL0__GET_RESP | HW_SSP_CTRL0__WAIT_FOR_IRQ : 0) | | ||
255 | (resp == SSP_LONG_RESP ? HW_SSP_CTRL0__LONG_RESP : 0) | | ||
256 | HW_SSP_CTRL0__BUS_WIDTH__ONE_BIT << HW_SSP_CTRL0__BUS_WIDTH_BP | | ||
257 | (buffer ? HW_SSP_CTRL0__DATA_XFER : 0) | | ||
258 | (read ? HW_SSP_CTRL0__READ : 0); | ||
259 | /* setup the dma parameters */ | ||
260 | ssp_dma_cmd[ssp - 1].dma.buffer = buffer; | ||
261 | ssp_dma_cmd[ssp - 1].dma.next = NULL; | ||
262 | ssp_dma_cmd[ssp - 1].dma.cmd = | ||
263 | (buffer == NULL ? HW_APB_CHx_CMD__COMMAND__NO_XFER : | ||
264 | read ? HW_APB_CHx_CMD__COMMAND__WRITE : HW_APB_CHx_CMD__COMMAND__READ) | | ||
265 | HW_APB_CHx_CMD__IRQONCMPLT | HW_APB_CHx_CMD__SEMAPHORE | | ||
266 | HW_APB_CHx_CMD__WAIT4ENDCMD | HW_APB_CHx_CMD__HALTONTERMINATE | | ||
267 | (3 << HW_APB_CHx_CMD__CMDWORDS_BP) | | ||
268 | (xfer_size << HW_APB_CHx_CMD__XFER_COUNT_BP); | ||
269 | |||
270 | imx233_dma_start_command(APB_SSP(ssp), &ssp_dma_cmd[ssp - 1].dma); | ||
271 | |||
272 | /* the SSP hardware already has a timeout but we never know; 1 sec is a maximum | ||
273 | * for all operations */ | ||
274 | enum imx233_ssp_error_t ret; | ||
275 | |||
276 | if(semaphore_wait(&ssp_sema[ssp - 1], HZ) == OBJ_WAIT_TIMEDOUT) | ||
277 | ret = SSP_TIMEOUT; | ||
278 | else if((HW_SSP_CTRL1(ssp) & HW_SSP_CTRL1__ALL_IRQ) == 0) | ||
279 | ret = SSP_SUCCESS; | ||
280 | else if(HW_SSP_CTRL1(ssp) & (HW_SSP_CTRL1__RESP_TIMEOUT_IRQ | | ||
281 | HW_SSP_CTRL1__DATA_TIMEOUT_IRQ | HW_SSP_CTRL1__RECV_TIMEOUT_IRQ)) | ||
282 | ret = SSP_TIMEOUT; | ||
283 | else | ||
284 | ret = SSP_ERROR; | ||
285 | |||
286 | if(ret == SSP_SUCCESS && resp_ptr != NULL) | ||
287 | { | ||
288 | if(resp != SSP_NO_RESP) | ||
289 | *resp_ptr++ = HW_SSP_SDRESP0(ssp); | ||
290 | if(resp == SSP_LONG_RESP) | ||
291 | { | ||
292 | *resp_ptr++ = HW_SSP_SDRESP1(ssp); | ||
293 | *resp_ptr++ = HW_SSP_SDRESP2(ssp); | ||
294 | *resp_ptr++ = HW_SSP_SDRESP3(ssp); | ||
295 | } | ||
296 | } | ||
297 | mutex_unlock(&ssp_mutex[ssp - 1]); | ||
298 | return ret; | ||
299 | } | ||
300 | |||
301 | void imx233_ssp_sd_mmc_power_up_sequence(int ssp) | ||
302 | { | ||
303 | __REG_CLR(HW_SSP_CMD0(ssp)) = HW_SSP_CMD0__SLOW_CLKING_EN; | ||
304 | __REG_SET(HW_SSP_CMD0(ssp)) = HW_SSP_CMD0__CONT_CLKING_EN; | ||
305 | mdelay(1); | ||
306 | __REG_CLR(HW_SSP_CMD0(ssp)) = HW_SSP_CMD0__CONT_CLKING_EN; | ||
307 | } | ||