summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/spi-imx31.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-05-04 10:07:53 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-05-04 10:07:53 +0000
commit931e06de64100e28031627964321da3fdb449378 (patch)
tree72a073d7ec3ede9554b394887d43d19fda6e8177 /firmware/target/arm/imx31/spi-imx31.c
parent7480afb3c59f4aebff262e1ce47395a3924ca994 (diff)
downloadrockbox-931e06de64100e28031627964321da3fdb449378.tar.gz
rockbox-931e06de64100e28031627964321da3fdb449378.zip
i.MX31/Gigabeat S: Actually enable DPTC which can set optimal voltage for 528MHz. Requires an SPI and PMIC interface rework because of the low-latency needs for the DPTC to work best with minimal panicing. SPI can work with multitasking and asynchronously from interrupt handlers or normal code.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25800 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/spi-imx31.c')
-rw-r--r--firmware/target/arm/imx31/spi-imx31.c419
1 files changed, 249 insertions, 170 deletions
diff --git a/firmware/target/arm/imx31/spi-imx31.c b/firmware/target/arm/imx31/spi-imx31.c
index ac063f9b10..3f66257c95 100644
--- a/firmware/target/arm/imx31/spi-imx31.c
+++ b/firmware/target/arm/imx31/spi-imx31.c
@@ -38,19 +38,18 @@ static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void);
38#endif 38#endif
39 39
40/* State data associatated with each CSPI module */ 40/* State data associatated with each CSPI module */
41static struct spi_module_descriptor 41static struct spi_module_desc
42{ 42{
43 struct cspi_map * const base; 43 struct cspi_map * const base; /* CSPI module address */
44 int enab; 44 struct spi_transfer_desc *head; /* Running job */
45 struct spi_node *last; 45 struct spi_transfer_desc *tail; /* Most recent job added */
46 enum IMX31_CG_LIST cg; 46 const struct spi_node *last_node; /* Last node used for module */
47 enum IMX31_INT_LIST ints; 47 void (*handler)(void); /* Interrupt handler */
48 int byte_size; 48 int rxcount; /* Independent copy of txcount */
49 void (*handler)(void); 49 int8_t enab; /* Enable count */
50 struct mutex m; 50 int8_t byte_size; /* Size of transfers in bytes */
51 struct wakeup w; 51 int8_t cg; /* Clock-gating value */
52 struct spi_transfer *trans; 52 int8_t ints; /* AVIC vector number */
53 int rxcount;
54} spi_descs[SPI_NUM_CSPI] = 53} spi_descs[SPI_NUM_CSPI] =
55/* Init non-zero members */ 54/* Init non-zero members */
56{ 55{
@@ -80,93 +79,224 @@ static struct spi_module_descriptor
80#endif 79#endif
81}; 80};
82 81
82/* Reset the module */
83static void spi_reset(struct spi_module_desc * const desc)
84{
85 /* Reset by leaving it disabled */
86 struct cspi_map * const base = desc->base;
87 base->conreg &= ~CSPI_CONREG_EN;
88}
89
90/* Write the context for the node and remember it to avoid unneeded reconfigure */
91static bool spi_set_context(struct spi_module_desc *desc,
92 struct spi_transfer_desc *xfer)
93{
94 const struct spi_node * const node = xfer->node;
95 struct cspi_map * const base = desc->base;
96
97 if (desc->enab == 0)
98 return false;
99
100 if (node == desc->last_node)
101 return true;
102
103 /* Errata says CSPI should be disabled when writing PERIODREG. */
104 base->conreg &= ~CSPI_CONREG_EN;
105
106 /* Switch the module's node */
107 desc->last_node = node;
108 desc->byte_size = (((node->conreg >> 8) & 0x1f) + 1 + 7) / 8 - 1;
109
110 /* Set the wait-states */
111 base->periodreg = node->periodreg & 0xffff;
112
113 /* Keep reserved and start bits cleared. Keep enabled bit. */
114 base->conreg =
115 (node->conreg & ~(0xfcc8e000 | CSPI_CONREG_XCH | CSPI_CONREG_SMC));
116 return true;
117}
118
119
120/* Fill the TX fifo. Returns the number of remaining words. */
121static int tx_fill_fifo(struct spi_module_desc * const desc,
122 struct cspi_map * const base,
123 struct spi_transfer_desc * const xfer)
124{
125 int count = xfer->count;
126 int size = desc->byte_size;
127
128 while ((base->statreg & CSPI_STATREG_TF) == 0)
129 {
130 uint32_t word = 0;
131
132 switch (size & 3)
133 {
134 case 3:
135 word = *(unsigned char *)(xfer->txbuf + 3) << 24;
136 case 2:
137 word |= *(unsigned char *)(xfer->txbuf + 2) << 16;
138 case 1:
139 word |= *(unsigned char *)(xfer->txbuf + 1) << 8;
140 case 0:
141 word |= *(unsigned char *)(xfer->txbuf + 0);
142 }
143
144 xfer->txbuf += size + 1; /* Increment buffer */
145
146 base->txdata = word; /* Write to FIFO */
147
148 if (--count == 0)
149 break;
150 }
151
152 xfer->count = count;
153
154 return count;
155}
156
157/* Start a transfer on the SPI */
158static bool start_transfer(struct spi_module_desc * const desc,
159 struct spi_transfer_desc * const xfer)
160{
161 struct cspi_map * const base = desc->base;
162 unsigned long intreg;
163
164 if (!spi_set_context(desc, xfer))
165 return false;
166
167 base->conreg |= CSPI_CONREG_EN; /* Enable module */
168
169 desc->rxcount = xfer->count;
170
171 intreg = (xfer->count < 8) ?
172 CSPI_INTREG_TCEN : /* Trans. complete: TX will run out in prefill */
173 CSPI_INTREG_THEN; /* INT when TX half-empty */
174
175 intreg |= (xfer->count < 4) ?
176 CSPI_INTREG_RREN : /* Must grab data on every word */
177 CSPI_INTREG_RHEN; /* Enough data to wait for half-full */
178
179 tx_fill_fifo(desc, base, xfer);
180
181 base->statreg = CSPI_STATREG_TC; /* Ack 'complete' */
182 base->intreg = intreg; /* Enable interrupts */
183 base->conreg |= CSPI_CONREG_XCH; /* Begin transfer */
184
185 return true;
186}
187
83/* Common code for interrupt handlers */ 188/* Common code for interrupt handlers */
84static void spi_interrupt(enum spi_module_number spi) 189static void spi_interrupt(enum spi_module_number spi)
85{ 190{
86 struct spi_module_descriptor *desc = &spi_descs[spi]; 191 struct spi_module_desc *desc = &spi_descs[spi];
87 struct cspi_map * const base = desc->base; 192 struct cspi_map * const base = desc->base;
88 struct spi_transfer *trans = desc->trans; 193 unsigned long intreg = base->intreg;
194 struct spi_transfer_desc *xfer = desc->head;
89 int inc = desc->byte_size + 1; 195 int inc = desc->byte_size + 1;
90 196
91 if (desc->rxcount > 0) 197 /* Data received - empty out RXFIFO */
198 while ((base->statreg & CSPI_STATREG_RR) != 0)
92 { 199 {
93 /* Data received - empty out RXFIFO */ 200 uint32_t word = base->rxdata;
94 while ((base->statreg & CSPI_STATREG_RR) != 0)
95 {
96 uint32_t word = base->rxdata;
97 201
202 if (desc->rxcount <= 0)
203 continue;
204
205 if (xfer->rxbuf != NULL)
206 {
207 /* There is a receive buffer */
98 switch (desc->byte_size & 3) 208 switch (desc->byte_size & 3)
99 { 209 {
100 case 3: 210 case 3:
101 *(unsigned char *)(trans->rxbuf + 3) = word >> 24; 211 *(unsigned char *)(xfer->rxbuf + 3) = word >> 24;
102 case 2: 212 case 2:
103 *(unsigned char *)(trans->rxbuf + 2) = word >> 16; 213 *(unsigned char *)(xfer->rxbuf + 2) = word >> 16;
104 case 1: 214 case 1:
105 *(unsigned char *)(trans->rxbuf + 1) = word >> 8; 215 *(unsigned char *)(xfer->rxbuf + 1) = word >> 8;
106 case 0: 216 case 0:
107 *(unsigned char *)(trans->rxbuf + 0) = word; 217 *(unsigned char *)(xfer->rxbuf + 0) = word;
108 } 218 }
109 219
110 trans->rxbuf += inc; 220 xfer->rxbuf += inc;
221 }
111 222
112 if (--desc->rxcount < 4) 223 if (--desc->rxcount < 4)
224 {
225 if (desc->rxcount == 0)
226 {
227 /* No more to receive - stop RX interrupts */
228 intreg &= ~(CSPI_INTREG_RHEN | CSPI_INTREG_RREN);
229 base->intreg = intreg;
230 }
231 else if (intreg & CSPI_INTREG_RHEN)
113 { 232 {
114 unsigned long intreg = base->intreg; 233 /* < 4 words expected - switch to RX ready */
115 234 intreg &= ~CSPI_INTREG_RHEN;
116 if (desc->rxcount <= 0) 235 intreg |= CSPI_INTREG_RREN;
117 { 236 base->intreg = intreg;
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 } 237 }
130 } 238 }
131 } 239 }
132 240
133 if (trans->count > 0) 241 if (xfer->count > 0)
134 { 242 {
135 /* Data to transmit - fill TXFIFO or write until exhausted */ 243 /* Data to transmit - fill TXFIFO or write until exhausted. */
136 while ((base->statreg & CSPI_STATREG_TF) == 0) 244 if (tx_fill_fifo(desc, base, xfer) != 0)
245 return;
246
247 /* Out of data - stop TX interrupts, enable TC interrupt. */
248 intreg &= ~CSPI_INTREG_THEN;
249 intreg |= CSPI_INTREG_TCEN;
250 base->intreg = intreg;
251 }
252
253 if ((intreg & CSPI_INTREG_TCEN) && (base->statreg & CSPI_STATREG_TC))
254 {
255 /* Outbound transfer is complete. */
256 intreg &= ~CSPI_INTREG_TCEN;
257 base->intreg = intreg;
258 base->statreg = CSPI_STATREG_TC; /* Ack 'complete' */
259 }
260
261 if (intreg != 0)
262 return;
263
264 /* All interrupts are masked; we're done with current transfer. */
265 for (;;)
266 {
267 struct spi_transfer_desc *next = xfer->next;
268 spi_transfer_cb_fn_type callback = xfer->callback;
269 xfer->next = NULL;
270
271 base->conreg &= ~CSPI_CONREG_EN; /* Disable module */
272
273 if (next == xfer)
137 { 274 {
138 uint32_t word = 0; 275 /* Last job on queue */
276 desc->head = NULL;
139 277
140 switch (desc->byte_size & 3) 278 if (callback != NULL)
141 { 279 callback(xfer);
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 280
152 trans->txbuf += inc; 281 /* Callback may have restarted transfers. */
282 }
283 else
284 {
285 /* Queue next job. */
286 desc->head = next;
153 287
154 base->txdata = word; 288 if (callback != NULL)
289 callback(xfer);
155 290
156 if (--trans->count <= 0) 291 if (!start_transfer(desc, next))
157 { 292 {
158 /* Out of data - stop TX interrupts */ 293 xfer = next;
159 base->intreg &= ~CSPI_INTREG_THEN; 294 xfer->count = -1;
160 break; 295 continue; /* Failed: try next */
161 } 296 }
162 } 297 }
163 }
164 298
165 /* If all interrupts have been remasked - we're done */ 299 break;
166 if (base->intreg == 0)
167 {
168 base->statreg = CSPI_STATREG_TC | CSPI_STATREG_BO;
169 wakeup_signal(&desc->w);
170 } 300 }
171} 301}
172 302
@@ -192,105 +322,50 @@ static __attribute__((interrupt("IRQ"))) void CSPI3_HANDLER(void)
192} 322}
193#endif 323#endif
194 324
195/* Write the context for the node and remember it to avoid unneeded reconfigure */ 325/* Initialize the SPI driver */
196static 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
223static 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 */
233void spi_init(void) 326void spi_init(void)
234{ 327{
235 int i; 328 unsigned i;
236
237 for (i = 0; i < SPI_NUM_CSPI; i++) 329 for (i = 0; i < SPI_NUM_CSPI; i++)
238 { 330 {
239 struct spi_module_descriptor * const desc = &spi_descs[i]; 331 struct spi_module_desc * const desc = &spi_descs[i];
240 mutex_init(&desc->m); 332 ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT);
241 wakeup_init(&desc->w); 333 spi_reset(desc);
334 ccm_module_clock_gating(desc->cg, CGM_OFF);
242 } 335 }
243} 336}
244 337
245/* Get mutually-exclusive access to the node */
246void spi_lock(struct spi_node *node)
247{
248 mutex_lock(&spi_descs[node->num].m);
249}
250
251/* Release mutual exclusion */
252void 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 */ 338/* Enable the specified module for the node */
258void spi_enable_module(struct spi_node *node) 339void spi_enable_module(const struct spi_node *node)
259{ 340{
260 struct spi_module_descriptor * const desc = &spi_descs[node->num]; 341 struct spi_module_desc * const desc = &spi_descs[node->num];
261
262 mutex_lock(&desc->m);
263 342
264 if (++desc->enab == 1) 343 if (++desc->enab == 1)
265 { 344 {
266 /* First enable for this module */
267 struct cspi_map * const base = desc->base;
268
269 /* Enable clock-gating register */ 345 /* Enable clock-gating register */
270 ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT); 346 ccm_module_clock_gating(desc->cg, CGM_ON_RUN_WAIT);
271 /* Reset */ 347 /* Reset */
272 spi_reset(base); 348 spi_reset(desc);
273 desc->last = NULL; 349 desc->last_node = NULL;
274 /* Enable interrupt at controller level */ 350 /* Enable interrupt at controller level */
275 avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT, 351 avic_enable_int(desc->ints, INT_TYPE_IRQ, INT_PRIO_DEFAULT,
276 desc->handler); 352 desc->handler);
277 } 353 }
278
279 mutex_unlock(&desc->m);
280} 354}
281 355
282/* Disabled the specified module for the node */ 356/* Disable the specified module for the node */
283void spi_disable_module(struct spi_node *node) 357void spi_disable_module(const struct spi_node *node)
284{ 358{
285 struct spi_module_descriptor * const desc = &spi_descs[node->num]; 359 struct spi_module_desc * const desc = &spi_descs[node->num];
286
287 mutex_lock(&desc->m);
288 360
289 if (desc->enab > 0 && --desc->enab == 0) 361 if (desc->enab > 0 && --desc->enab == 0)
290 { 362 {
291 /* Last enable for this module */ 363 /* Last enable for this module */
292 struct cspi_map * const base = desc->base; 364 struct cspi_map * const base = desc->base;
293 365
366 /* Wait for outstanding transactions */
367 while (*(void ** volatile)&desc->head != NULL);
368
294 /* Disable interrupt at controller level */ 369 /* Disable interrupt at controller level */
295 avic_disable_int(desc->ints); 370 avic_disable_int(desc->ints);
296 371
@@ -300,53 +375,57 @@ void spi_disable_module(struct spi_node *node)
300 /* Disable interface clock */ 375 /* Disable interface clock */
301 ccm_module_clock_gating(desc->cg, CGM_OFF); 376 ccm_module_clock_gating(desc->cg, CGM_OFF);
302 } 377 }
303
304 mutex_unlock(&desc->m);
305} 378}
306 379
307/* Send and/or receive data on the specified node */ 380/* Send and/or receive data on the specified node */
308int spi_transfer(struct spi_node *node, struct spi_transfer *trans) 381bool spi_transfer(struct spi_transfer_desc *xfer)
309{ 382{
310 struct spi_module_descriptor * const desc = &spi_descs[node->num]; 383 bool retval;
311 int retval; 384 struct spi_module_desc * desc;
312 385 int oldlevel;
313 if (trans->count <= 0)
314 return true;
315
316 mutex_lock(&desc->m);
317 386
318 retval = spi_set_context(node, desc); 387 if (xfer->count == 0)
388 return true; /* No data? No problem. */
319 389
320 if (retval) 390 if (xfer->count < 0 || xfer->next != NULL || xfer->node == NULL)
321 { 391 {
322 struct cspi_map * const base = desc->base; 392 /* Can't pass a busy descriptor, requires a node and negative size
323 unsigned long intreg; 393 * is invalid to pass. */
324 394 return false;
325 desc->trans = trans; 395 }
326 desc->rxcount = trans->count;
327
328 /* Enable needed interrupts - FIFOs will start filling */
329 intreg = CSPI_INTREG_THEN;
330 396
331 intreg |= (trans->count < 4) ? 397 oldlevel = disable_irq_save();
332 CSPI_INTREG_RREN : /* Must grab data on every word */
333 CSPI_INTREG_RHEN; /* Enough data to wait for half-full */
334 398
335 base->intreg = intreg; 399 desc = &spi_descs[xfer->node->num];
336 400
337 /* Start transfer */ 401 if (desc->head == NULL)
338 base->conreg |= CSPI_CONREG_XCH; 402 {
403 /* No transfers in progress; start interface. */
404 retval = start_transfer(desc, xfer);
339 405
340 if (wakeup_wait(&desc->w, HZ) != OBJ_WAIT_SUCCEEDED) 406 if (retval)
341 { 407 {
342 base->intreg = 0; /* Stop SPI ints */ 408 /* Start ok: actually put it in the queue. */
343 spi_reset(base); /* Reset module (esp. to empty FIFOs) */ 409 desc->head = xfer;
344 desc->last = NULL; /* Force reconfigure */ 410 desc->tail = xfer;
345 retval = false; 411 xfer->next = xfer; /* First, self-reference terminate */
346 } 412 }
413 else
414 {
415 xfer->count = -1; /* Signal error */
416 }
417 }
418 else
419 {
420 /* Already running: simply add to end and the final INT on the
421 * running transfer will pick it up. */
422 desc->tail->next = xfer; /* Add to tail */
423 desc->tail = xfer; /* New tail */
424 xfer->next = xfer; /* Self-reference terminate */
425 retval = true;
347 } 426 }
348 427
349 mutex_unlock(&desc->m); 428 restore_irq(oldlevel);
350 429
351 return retval; 430 return retval;
352} 431}