diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-04-09 01:21:53 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-04-09 01:21:53 +0000 |
commit | 7abf2b53a462612808d46d6d77a7f35261a0e5a3 (patch) | |
tree | 241304f7cd2b5d1c2a9e091fe56a33d2d2f8e816 /firmware/target/arm/imx31/spi-imx31.c | |
parent | 43304b87b0662d1619ac60e5297a1694aa580310 (diff) | |
download | rockbox-7abf2b53a462612808d46d6d77a7f35261a0e5a3.tar.gz rockbox-7abf2b53a462612808d46d6d77a7f35261a0e5a3.zip |
Gigabeat S/i.MX31: Sort files in the /target tree into things that are SoC-generic (into /imx31) and player-specific (into /gigabeat-s, based upon current appearances). Move i2s clock init into the appropriate file. Housekeeping only-- no functional changes.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/spi-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/spi-imx31.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/firmware/target/arm/imx31/spi-imx31.c b/firmware/target/arm/imx31/spi-imx31.c new file mode 100644 index 0000000000..ac063f9b10 --- /dev/null +++ b/firmware/target/arm/imx31/spi-imx31.c | |||
@@ -0,0 +1,352 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2007 Will Robertson | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "system.h" | ||
23 | #include "spi-imx31.h" | ||
24 | #include "avic-imx31.h" | ||
25 | #include "ccm-imx31.h" | ||
26 | #include "debug.h" | ||
27 | #include "kernel.h" | ||
28 | |||
29 | /* Forward interrupt handler declarations */ | ||
30 | #if (SPI_MODULE_MASK & USE_CSPI1_MODULE) | ||
31 | static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void); | ||
32 | #endif | ||
33 | #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) | ||
34 | static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void); | ||
35 | #endif | ||
36 | #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) | ||
37 | static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void); | ||
38 | #endif | ||
39 | |||
40 | /* State data associatated with each CSPI module */ | ||
41 | static struct spi_module_descriptor | ||
42 | { | ||
43 | struct cspi_map * const base; | ||
44 | int enab; | ||
45 | struct spi_node *last; | ||
46 | enum IMX31_CG_LIST cg; | ||
47 | enum IMX31_INT_LIST ints; | ||
48 | int byte_size; | ||
49 | void (*handler)(void); | ||
50 | struct mutex m; | ||
51 | struct wakeup w; | ||
52 | struct spi_transfer *trans; | ||
53 | int rxcount; | ||
54 | } spi_descs[SPI_NUM_CSPI] = | ||
55 | /* Init non-zero members */ | ||
56 | { | ||
57 | #if (SPI_MODULE_MASK & USE_CSPI1_MODULE) | ||
58 | { | ||
59 | .base = (struct cspi_map *)CSPI1_BASE_ADDR, | ||
60 | .cg = CG_CSPI1, | ||
61 | .ints = INT_CSPI1, | ||
62 | .handler = CSPI1_HANDLER, | ||
63 | }, | ||
64 | #endif | ||
65 | #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) | ||
66 | { | ||
67 | .base = (struct cspi_map *)CSPI2_BASE_ADDR, | ||
68 | .cg = CG_CSPI2, | ||
69 | .ints = INT_CSPI2, | ||
70 | .handler = CSPI2_HANDLER, | ||
71 | }, | ||
72 | #endif | ||
73 | #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) | ||
74 | { | ||
75 | .base = (struct cspi_map *)CSPI3_BASE_ADDR, | ||
76 | .cg = CG_CSPI3, | ||
77 | .ints = INT_CSPI3, | ||
78 | .handler = CSPI3_HANDLER, | ||
79 | }, | ||
80 | #endif | ||
81 | }; | ||
82 | |||
83 | /* Common code for interrupt handlers */ | ||
84 | static void spi_interrupt(enum spi_module_number spi) | ||
85 | { | ||
86 | struct spi_module_descriptor *desc = &spi_descs[spi]; | ||
87 | struct cspi_map * const base = desc->base; | ||
88 | struct spi_transfer *trans = desc->trans; | ||
89 | int inc = desc->byte_size + 1; | ||
90 | |||
91 | if (desc->rxcount > 0) | ||
92 | { | ||
93 | /* Data received - empty out RXFIFO */ | ||
94 | while ((base->statreg & CSPI_STATREG_RR) != 0) | ||
95 | { | ||
96 | uint32_t word = base->rxdata; | ||
97 | |||
98 | switch (desc->byte_size & 3) | ||
99 | { | ||
100 | case 3: | ||
101 | *(unsigned char *)(trans->rxbuf + 3) = word >> 24; | ||
102 | case 2: | ||
103 | *(unsigned char *)(trans->rxbuf + 2) = word >> 16; | ||
104 | case 1: | ||
105 | *(unsigned char *)(trans->rxbuf + 1) = word >> 8; | ||
106 | case 0: | ||
107 | *(unsigned char *)(trans->rxbuf + 0) = word; | ||
108 | } | ||
109 | |||
110 | trans->rxbuf += inc; | ||
111 | |||
112 | if (--desc->rxcount < 4) | ||
113 | { | ||
114 | unsigned long intreg = base->intreg; | ||
115 | |||
116 | if (desc->rxcount <= 0) | ||
117 | { | ||
118 | /* No more to receive - stop RX interrupts */ | ||
119 | intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); | ||
120 | base->intreg = intreg; | ||
121 | break; | ||
122 | } | ||
123 | else if (!(intreg & CSPI_INTREG_RREN)) | ||
124 | { | ||
125 | /* < 4 words expected - switch to RX ready */ | ||
126 | intreg &= ~CSPI_INTREG_RHEN; | ||
127 | base->intreg = intreg | CSPI_INTREG_RREN; | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | if (trans->count > 0) | ||
134 | { | ||
135 | /* Data to transmit - fill TXFIFO or write until exhausted */ | ||
136 | while ((base->statreg & CSPI_STATREG_TF) == 0) | ||
137 | { | ||
138 | uint32_t word = 0; | ||
139 | |||
140 | switch (desc->byte_size & 3) | ||
141 | { | ||
142 | case 3: | ||
143 | word = *(unsigned char *)(trans->txbuf + 3) << 24; | ||
144 | case 2: | ||
145 | word |= *(unsigned char *)(trans->txbuf + 2) << 16; | ||
146 | case 1: | ||
147 | word |= *(unsigned char *)(trans->txbuf + 1) << 8; | ||
148 | case 0: | ||
149 | word |= *(unsigned char *)(trans->txbuf + 0); | ||
150 | } | ||
151 | |||
152 | trans->txbuf += inc; | ||
153 | |||
154 | base->txdata = word; | ||
155 | |||
156 | if (--trans->count <= 0) | ||
157 | { | ||
158 | /* Out of data - stop TX interrupts */ | ||
159 | base->intreg &= ~CSPI_INTREG_THEN; | ||
160 | break; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /* If all interrupts have been remasked - we're done */ | ||
166 | if (base->intreg == 0) | ||
167 | { | ||
168 | base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; | ||
169 | wakeup_signal(&desc->w); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /* Interrupt handlers for each CSPI module */ | ||
174 | #if (SPI_MODULE_MASK & USE_CSPI1_MODULE) | ||
175 | static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void) | ||
176 | { | ||
177 | spi_interrupt(CSPI1_NUM); | ||
178 | } | ||
179 | #endif | ||
180 | |||
181 | #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) | ||
182 | static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void) | ||
183 | { | ||
184 | spi_interrupt(CSPI2_NUM); | ||
185 | } | ||
186 | #endif | ||
187 | |||
188 | #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) | ||
189 | static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void) | ||
190 | { | ||
191 | spi_interrupt(CSPI3_NUM); | ||
192 | } | ||
193 | #endif | ||
194 | |||
195 | /* Write the context for the node and remember it to avoid unneeded reconfigure */ | ||
196 | static bool spi_set_context(struct spi_node *node, | ||
197 | struct spi_module_descriptor *desc) | ||
198 | { | ||
199 | struct cspi_map * const base = desc->base; | ||
200 | |||
201 | if ((base->conreg & CSPI_CONREG_EN) == 0) | ||
202 | return false; | ||
203 | |||
204 | if (node != desc->last) | ||
205 | { | ||
206 | /* Switch the module's node */ | ||
207 | desc->last = node; | ||
208 | desc->byte_size = (((node->conreg >> 8) & 0x1f) + 1 + 7) / 8 - 1; | ||
209 | |||
210 | /* Keep reserved and start bits cleared. Keep enabled bit. */ | ||
211 | base->conreg = | ||
212 | (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) | ||
213 | | CSPI_CONREG_EN; | ||
214 | /* Set the wait-states */ | ||
215 | base->periodreg = node->periodreg & 0xffff; | ||
216 | /* Clear out any spuriously-pending interrupts */ | ||
217 | base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; | ||
218 | } | ||
219 | |||
220 | return true; | ||
221 | } | ||
222 | |||
223 | static void spi_reset(struct cspi_map * const base) | ||
224 | { | ||
225 | /* Reset */ | ||
226 | base->conreg &= ~CSPI_CONREG_EN; | ||
227 | base->conreg |= CSPI_CONREG_EN; | ||
228 | base->intreg = 0; | ||
229 | base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO; | ||
230 | } | ||
231 | |||
232 | /* Initialize each of the used SPI descriptors */ | ||
233 | void spi_init(void) | ||
234 | { | ||
235 | int i; | ||
236 | |||
237 | for (i = 0; i < SPI_NUM_CSPI; i++) | ||
238 | { | ||
239 | struct spi_module_descriptor * const desc = &spi_descs[i]; | ||
240 | mutex_init(&desc->m); | ||
241 | wakeup_init(&desc->w); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | /* Get mutually-exclusive access to the node */ | ||
246 | void spi_lock(struct spi_node *node) | ||
247 | { | ||
248 | mutex_lock(&spi_descs[node->num].m); | ||
249 | } | ||
250 | |||
251 | /* Release mutual exclusion */ | ||
252 | void spi_unlock(struct spi_node *node) | ||
253 | { | ||
254 | mutex_unlock(&spi_descs[node->num].m); | ||
255 | } | ||
256 | |||
257 | /* Enable the specified module for the node */ | ||
258 | void spi_enable_module(struct spi_node *node) | ||
259 | { | ||
260 | struct spi_module_descriptor * const desc = &spi_descs[node->num]; | ||
261 | |||
262 | mutex_lock(&desc->m); | ||
263 | |||
264 | if (++desc->enab == 1) | ||
265 | { | ||
266 | /* First enable for this module */ | ||
267 | struct cspi_map * const base = desc->base; | ||
268 | |||
269 | /* Enable clock-gating register */ | ||
270 | ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); | ||
271 | /* Reset */ | ||
272 | spi_reset(base); | ||
273 | desc->last = NULL; | ||
274 | /* Enable interrupt at controller level */ | ||
275 | avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, | ||
276 | desc->handler); | ||
277 | } | ||
278 | |||
279 | mutex_unlock(&desc->m); | ||
280 | } | ||
281 | |||
282 | /* Disabled the specified module for the node */ | ||
283 | void spi_disable_module(struct spi_node *node) | ||
284 | { | ||
285 | struct spi_module_descriptor * const desc = &spi_descs[node->num]; | ||
286 | |||
287 | mutex_lock(&desc->m); | ||
288 | |||
289 | if (desc->enab > 0 && --desc->enab == 0) | ||
290 | { | ||
291 | /* Last enable for this module */ | ||
292 | struct cspi_map * const base = desc->base; | ||
293 | |||
294 | /* Disable interrupt at controller level */ | ||
295 | avic_disable_int(desc->ints); | ||
296 | |||
297 | /* Disable interface */ | ||
298 | base->conreg &= ~CSPI_CONREG_EN; | ||
299 | |||
300 | /* Disable interface clock */ | ||
301 | ccm_module_clock_gating(desc->cg, CGM_OFF); | ||
302 | } | ||
303 | |||
304 | mutex_unlock(&desc->m); | ||
305 | } | ||
306 | |||
307 | /* Send and/or receive data on the specified node */ | ||
308 | int spi_transfer(struct spi_node *node, struct spi_transfer *trans) | ||
309 | { | ||
310 | struct spi_module_descriptor * const desc = &spi_descs[node->num]; | ||
311 | int retval; | ||
312 | |||
313 | if (trans->count <= 0) | ||
314 | return true; | ||
315 | |||
316 | mutex_lock(&desc->m); | ||
317 | |||
318 | retval = spi_set_context(node, desc); | ||
319 | |||
320 | if (retval) | ||
321 | { | ||
322 | struct cspi_map * const base = desc->base; | ||
323 | unsigned long intreg; | ||
324 | |||
325 | desc->trans = trans; | ||
326 | desc->rxcount = trans->count; | ||
327 | |||
328 | /* Enable needed interrupts - FIFOs will start filling */ | ||
329 | intreg = CSPI_INTREG_THEN; | ||
330 | |||
331 | intreg |= (trans->count < 4) ? | ||
332 | CSPI_INTREG_RREN : /* Must grab data on every word */ | ||
333 | CSPI_INTREG_RHEN; /* Enough data to wait for half-full */ | ||
334 | |||
335 | base->intreg = intreg; | ||
336 | |||
337 | /* Start transfer */ | ||
338 | base->conreg |= CSPI_CONREG_XCH; | ||
339 | |||
340 | if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) | ||
341 | { | ||
342 | base->intreg = 0; /* Stop SPI ints */ | ||
343 | spi_reset(base); /* Reset module (esp. to empty FIFOs) */ | ||
344 | desc->last = NULL; /* Force reconfigure */ | ||
345 | retval = false; | ||
346 | } | ||
347 | } | ||
348 | |||
349 | mutex_unlock(&desc->m); | ||
350 | |||
351 | return retval; | ||
352 | } | ||