diff options
Diffstat (limited to 'firmware/target/arm/imx31/gigabeat-s/spi-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/spi-imx31.c | 355 |
1 files changed, 307 insertions, 48 deletions
diff --git a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c index 10ee3f44c0..bcbe85a76b 100644 --- a/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/spi-imx31.c | |||
@@ -18,66 +18,325 @@ | |||
18 | ****************************************************************************/ | 18 | ****************************************************************************/ |
19 | #include "cpu.h" | 19 | #include "cpu.h" |
20 | #include "spi-imx31.h" | 20 | #include "spi-imx31.h" |
21 | #include "avic-imx31.h" | ||
22 | #include "clkctl-imx31.h" | ||
21 | #include "debug.h" | 23 | #include "debug.h" |
22 | #include "kernel.h" | 24 | #include "kernel.h" |
23 | 25 | ||
24 | /* This is all based on communicating with the MC13783 PMU which is on | 26 | /* Forward interrupt handler declarations */ |
25 | * CSPI2 with the chip select at 0. The LCD controller resides on | 27 | #if (SPI_MODULE_MASK & USE_CSPI1_MODULE) |
26 | * CSPI3 cs1, but we have no idea how to communicate to it */ | 28 | static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void); |
27 | 29 | #endif | |
28 | void spi_init(void) { | 30 | #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) |
29 | CSPI_CONREG2 |= (2 << 20); // Burst will be triggered at SPI_RDY low | 31 | static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void); |
30 | CSPI_CONREG2 |= (2 << 16); // Clock = IPG_CLK/16 - we want about 20mhz | 32 | #endif |
31 | CSPI_CONREG2 |= (31 << 8); // All 32 bits are to be transferred | 33 | #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) |
32 | CSPI_CONREG2 |= (1 << 3); // Start burst on TXFIFO write. | 34 | static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void); |
33 | CSPI_CONREG2 |= (1 << 1); // Master mode. | 35 | #endif |
34 | CSPI_CONREG2 |= 1; // Enable CSPI2; | 36 | |
37 | /* State data associatated with each CSPI module */ | ||
38 | static struct spi_module_descriptor | ||
39 | { | ||
40 | volatile unsigned long *base; | ||
41 | int enab; | ||
42 | struct spi_node *last; | ||
43 | enum IMX31_CG_LIST cg; | ||
44 | enum IMX31_INT_LIST ints; | ||
45 | int byte_size; | ||
46 | void (*handler)(void); | ||
47 | struct mutex m; | ||
48 | struct wakeup w; | ||
49 | struct spi_transfer *trans; | ||
50 | int rxcount; | ||
51 | } spi_descs[SPI_NUM_CSPI] = | ||
52 | /* Init non-zero members */ | ||
53 | { | ||
54 | #if (SPI_MODULE_MASK & USE_CSPI1_MODULE) | ||
55 | { | ||
56 | .base = (unsigned long *)CSPI1_BASE_ADDR, | ||
57 | .cg = CG_CSPI1, | ||
58 | .ints = CSPI1, | ||
59 | .handler = CSPI1_HANDLER, | ||
60 | }, | ||
61 | #endif | ||
62 | #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) | ||
63 | { | ||
64 | .base = (unsigned long *)CSPI2_BASE_ADDR, | ||
65 | .cg = CG_CSPI2, | ||
66 | .ints = CSPI2, | ||
67 | .handler = CSPI2_HANDLER, | ||
68 | }, | ||
69 | #endif | ||
70 | #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) | ||
71 | { | ||
72 | .base = (unsigned long *)CSPI3_BASE_ADDR, | ||
73 | .cg = CG_CSPI3, | ||
74 | .ints = CSPI3, | ||
75 | .handler = CSPI3_HANDLER, | ||
76 | }, | ||
77 | #endif | ||
78 | }; | ||
79 | |||
80 | /* Common code for interrupt handlers */ | ||
81 | static void spi_interrupt(enum spi_module_number spi) | ||
82 | { | ||
83 | struct spi_module_descriptor *desc = &spi_descs[spi]; | ||
84 | volatile unsigned long * const base = desc->base; | ||
85 | struct spi_transfer *trans = desc->trans; | ||
86 | int inc = desc->byte_size + 1; | ||
87 | |||
88 | if (desc->rxcount > 0) | ||
89 | { | ||
90 | /* Data received - empty out RXFIFO */ | ||
91 | while ((base[CSPI_STATREG_I] & CSPI_STATREG_RR) != 0) | ||
92 | { | ||
93 | uint32_t word = base[CSPI_RXDATA_I]; | ||
94 | |||
95 | switch (desc->byte_size & 3) | ||
96 | { | ||
97 | case 3: | ||
98 | *(unsigned char *)(trans->rxbuf + 3) = word >> 24; | ||
99 | case 2: | ||
100 | *(unsigned char *)(trans->rxbuf + 2) = word >> 16; | ||
101 | case 1: | ||
102 | *(unsigned char *)(trans->rxbuf + 1) = word >> 8; | ||
103 | case 0: | ||
104 | *(unsigned char *)(trans->rxbuf + 0) = word; | ||
105 | } | ||
106 | |||
107 | trans->rxbuf += inc; | ||
108 | |||
109 | if (--desc->rxcount < 4) | ||
110 | { | ||
111 | unsigned long intreg = base[CSPI_INTREG_I]; | ||
112 | |||
113 | if (desc->rxcount <= 0) | ||
114 | { | ||
115 | /* No more to receive - stop RX interrupts */ | ||
116 | intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN); | ||
117 | base[CSPI_INTREG_I] = intreg; | ||
118 | break; | ||
119 | } | ||
120 | else if (!(intreg & CSPI_INTREG_RREN)) | ||
121 | { | ||
122 | /* < 4 words expected - switch to RX ready */ | ||
123 | intreg &= ~CSPI_INTREG_RHEN; | ||
124 | base[CSPI_INTREG_I] = intreg | CSPI_INTREG_RREN; | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | if (trans->count > 0) | ||
131 | { | ||
132 | /* Data to transmit - fill TXFIFO or write until exhausted */ | ||
133 | while ((base[CSPI_STATREG_I] & CSPI_STATREG_TF) == 0) | ||
134 | { | ||
135 | uint32_t word = 0; | ||
136 | |||
137 | switch (desc->byte_size & 3) | ||
138 | { | ||
139 | case 3: | ||
140 | word = *(unsigned char *)(trans->txbuf + 3) << 24; | ||
141 | case 2: | ||
142 | word |= *(unsigned char *)(trans->txbuf + 2) << 16; | ||
143 | case 1: | ||
144 | word |= *(unsigned char *)(trans->txbuf + 1) << 8; | ||
145 | case 0: | ||
146 | word |= *(unsigned char *)(trans->txbuf + 0); | ||
147 | } | ||
148 | |||
149 | trans->txbuf += inc; | ||
150 | |||
151 | base[CSPI_TXDATA_I] = word; | ||
152 | |||
153 | if (--trans->count <= 0) | ||
154 | { | ||
155 | /* Out of data - stop TX interrupts */ | ||
156 | base[CSPI_INTREG_I] &= ~CSPI_INTREG_THEN; | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | /* If all interrupts have been remasked - we're done */ | ||
163 | if (base[CSPI_INTREG_I] == 0) | ||
164 | { | ||
165 | base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; | ||
166 | wakeup_signal(&desc->w); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /* Interrupt handlers for each CSPI module */ | ||
171 | #if (SPI_MODULE_MASK & USE_CSPI1_MODULE) | ||
172 | static __attribute__((interrupt("IRQ"))) void CSPI1_HANDLER(void) | ||
173 | { | ||
174 | spi_interrupt(CSPI1_NUM); | ||
175 | } | ||
176 | #endif | ||
177 | |||
178 | #if (SPI_MODULE_MASK & USE_CSPI2_MODULE) | ||
179 | static __attribute__((interrupt("IRQ"))) void CSPI2_HANDLER(void) | ||
180 | { | ||
181 | spi_interrupt(CSPI2_NUM); | ||
182 | } | ||
183 | #endif | ||
184 | |||
185 | #if (SPI_MODULE_MASK & USE_CSPI3_MODULE) | ||
186 | static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void) | ||
187 | { | ||
188 | spi_interrupt(CSPI3_NUM); | ||
189 | } | ||
190 | #endif | ||
191 | |||
192 | /* Write the context for the node and remember it to avoid unneeded reconfigure */ | ||
193 | static bool spi_set_context(struct spi_node *node, | ||
194 | struct spi_module_descriptor *desc) | ||
195 | { | ||
196 | volatile unsigned long * const base = desc->base; | ||
197 | |||
198 | if ((base[CSPI_CONREG_I] & CSPI_CONREG_EN) == 0) | ||
199 | return false; | ||
200 | |||
201 | if (node != desc->last) | ||
202 | { | ||
203 | /* Switch the module's node */ | ||
204 | desc->last = node; | ||
205 | desc->byte_size = (((node->conreg >> 8) & 0x1f) + 1 + 7) / 8 - 1; | ||
206 | |||
207 | /* Keep reserved and start bits cleared. Keep enabled bit. */ | ||
208 | base[CSPI_CONREG_I] = | ||
209 | (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC)) | ||
210 | | CSPI_CONREG_EN; | ||
211 | /* Set the wait-states */ | ||
212 | base[CSPI_PERIODREG_I] = node->periodreg & 0xffff; | ||
213 | /* Clear out any spuriously-pending interrupts */ | ||
214 | base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; | ||
215 | } | ||
216 | |||
217 | return true; | ||
35 | } | 218 | } |
36 | 219 | ||
37 | static int spi_transfer(int address, long data, long* buffer, bool read) { | 220 | /* Initialize each of the used SPI descriptors */ |
38 | return -1; /* Disable for now - hangs - and we'll use interrupts */ | 221 | void spi_init(void) |
222 | { | ||
223 | int i; | ||
39 | 224 | ||
40 | unsigned long packet = 0; | 225 | for (i = 0; i < SPI_NUM_CSPI; i++) |
41 | if(!read) { | 226 | { |
42 | /* Set the appropriate bit in the packet to indicate a write */ | 227 | struct spi_module_descriptor * const desc = &spi_descs[i]; |
43 | packet |= (1<<31); | 228 | mutex_init(&desc->m); |
229 | wakeup_init(&desc->w); | ||
44 | } | 230 | } |
45 | /* Set the address of the packet */ | 231 | } |
46 | packet |= (address << 25); | 232 | |
47 | 233 | /* Get mutually-exclusive access to the node */ | |
48 | /* Ensure data only occupies 24 bits, then mash the data into the packet */ | 234 | void spi_lock(struct spi_node *node) |
49 | data &= ~(DATAMASK); | 235 | { |
50 | packet |= data; | 236 | mutex_lock(&spi_descs[node->num].m); |
51 | 237 | } | |
52 | /* Wait for some room in TXFIFO */ | 238 | |
53 | while(CSPI_STATREG2 & (1<<2)); | 239 | /* Release mutual exclusion */ |
54 | 240 | void spi_unlock(struct spi_node *node) | |
55 | /* Send the packet */ | 241 | { |
56 | CSPI_TXDATA2 = packet; | 242 | mutex_unlock(&spi_descs[node->num].m); |
57 | 243 | } | |
58 | /* Poll the XCH bit to wait for the end of the transfer, with | 244 | |
59 | * a one second timeout */ | 245 | /* Enable the specified module for the node */ |
60 | int newtick = current_tick + HZ; | 246 | void spi_enable_module(struct spi_node *node) |
61 | while((CSPI_CONREG2 & (1<<2)) && (current_tick < newtick)); | 247 | { |
62 | 248 | struct spi_module_descriptor * const desc = &spi_descs[node->num]; | |
63 | if(newtick > current_tick) { | 249 | |
64 | *buffer = CSPI_RXDATA2; | 250 | mutex_lock(&desc->m); |
65 | return 0; | 251 | |
66 | } else { | 252 | if (++desc->enab == 1) |
67 | /* Indicate the fact that the transfer timed out */ | 253 | { |
68 | return -1; | 254 | /* First enable for this module */ |
255 | volatile unsigned long * const base = desc->base; | ||
256 | |||
257 | /* Enable clock-gating register */ | ||
258 | imx31_clkctl_module_clock_gating(desc->cg, CGM_ON_ALL); | ||
259 | |||
260 | /* Reset */ | ||
261 | base[CSPI_CONREG_I] &= ~CSPI_CONREG_EN; | ||
262 | base[CSPI_CONREG_I] |= CSPI_CONREG_EN; | ||
263 | base[CSPI_INTREG_I] = 0; | ||
264 | base[CSPI_STATREG_I] = CSPI_STATREG_TC | CSPI_STATREG_BO; | ||
265 | |||
266 | /* Enable interrupt at controller level */ | ||
267 | avic_enable_int(desc->ints, IRQ, 6, desc->handler); | ||
69 | } | 268 | } |
269 | |||
270 | mutex_unlock(&desc->m); | ||
70 | } | 271 | } |
71 | 272 | ||
72 | void spi_send(int address, unsigned long data) { | 273 | /* Disabled the specified module for the node */ |
73 | long dummy; | 274 | void spi_disable_module(struct spi_node *node) |
74 | if(spi_transfer(address, data, &dummy, false)) { | 275 | { |
75 | DEBUGF("SPI Send timed out"); | 276 | struct spi_module_descriptor * const desc = &spi_descs[node->num]; |
277 | |||
278 | mutex_lock(&desc->m); | ||
279 | |||
280 | if (desc->enab > 0 && --desc->enab == 0) | ||
281 | { | ||
282 | /* Last enable for this module */ | ||
283 | volatile unsigned long * const base = desc->base; | ||
284 | |||
285 | /* Disable interrupt at controller level */ | ||
286 | avic_disable_int(desc->ints); | ||
287 | |||
288 | /* Disable interface */ | ||
289 | base[CSPI_CONREG_I] &= ~CSPI_CONREG_EN; | ||
290 | |||
291 | /* Disable interface clock */ | ||
292 | imx31_clkctl_module_clock_gating(desc->cg, CGM_OFF); | ||
76 | } | 293 | } |
294 | |||
295 | mutex_unlock(&desc->m); | ||
77 | } | 296 | } |
78 | 297 | ||
79 | void spi_read(int address, unsigned long* buffer) { | 298 | /* Send and/or receive data on the specified node */ |
80 | if(spi_transfer(address, 0, buffer, true)) { | 299 | int spi_transfer(struct spi_node *node, struct spi_transfer *trans) |
81 | DEBUGF("SPI read timed out"); | 300 | { |
301 | struct spi_module_descriptor * const desc = &spi_descs[node->num]; | ||
302 | int retval; | ||
303 | |||
304 | if (trans->count <= 0) | ||
305 | return true; | ||
306 | |||
307 | mutex_lock(&desc->m); | ||
308 | |||
309 | retval = spi_set_context(node, desc); | ||
310 | |||
311 | if (retval) | ||
312 | { | ||
313 | volatile unsigned long * const base = desc->base; | ||
314 | unsigned long intreg; | ||
315 | |||
316 | desc->trans = trans; | ||
317 | desc->rxcount = trans->count; | ||
318 | |||
319 | /* Enable needed interrupts - FIFOs will start filling */ | ||
320 | intreg = CSPI_INTREG_THEN; | ||
321 | |||
322 | intreg |= (trans->count < 4) ? | ||
323 | CSPI_INTREG_RREN : /* Must grab data on every word */ | ||
324 | CSPI_INTREG_RHEN; /* Enough data to wait for half-full */ | ||
325 | |||
326 | base[CSPI_INTREG_I] = intreg; | ||
327 | |||
328 | /* Start transfer */ | ||
329 | base[CSPI_CONREG_I] |= CSPI_CONREG_XCH; | ||
330 | |||
331 | if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) | ||
332 | { | ||
333 | base[CSPI_INTREG_I] = 0; | ||
334 | base[CSPI_CONREG_I] &= ~CSPI_CONREG_XCH; | ||
335 | retval = false; | ||
336 | } | ||
82 | } | 337 | } |
338 | |||
339 | mutex_unlock(&desc->m); | ||
340 | |||
341 | return retval; | ||
83 | } | 342 | } |