summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx31')
-rw-r--r--firmware/target/arm/imx31/debug-imx31.c2
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.c213
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c2
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c2
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h3
-rw-r--r--firmware/target/arm/imx31/mc13783-imx31.c241
-rw-r--r--firmware/target/arm/imx31/spi-imx31.c419
-rw-r--r--firmware/target/arm/imx31/spi-imx31.h38
8 files changed, 536 insertions, 384 deletions
diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c
index d854c9bc36..2c4f8b4023 100644
--- a/firmware/target/arm/imx31/debug-imx31.c
+++ b/firmware/target/arm/imx31/debug-imx31.c
@@ -213,7 +213,7 @@ bool __dbg_ports(void)
213 lcd_puts(0, line++, "PMIC Registers"); 213 lcd_puts(0, line++, "PMIC Registers");
214 line++; 214 line++;
215 215
216 mc13783_read_regset(pmic_regset, pmic_regs, ARRAYLEN(pmic_regs)); 216 mc13783_read_regs(pmic_regset, pmic_regs, ARRAYLEN(pmic_regs));
217 217
218 for (i = 0; i < (int)ARRAYLEN(pmic_regs); i++) 218 for (i = 0; i < (int)ARRAYLEN(pmic_regs); i++)
219 { 219 {
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
index 6e177ba6bd..217c8a8b83 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
@@ -53,7 +53,7 @@ static void update_dptc_counts(unsigned int level, unsigned int wp)
53} 53}
54 54
55 55
56static inline uint32_t check_regulator_setting(uint32_t setting) 56static uint32_t check_regulator_setting(uint32_t setting)
57{ 57{
58 /* Simply a safety check *in case* table gets scrambled */ 58 /* Simply a safety check *in case* table gets scrambled */
59 if (setting < VOLTAGE_SETTING_MIN) 59 if (setting < VOLTAGE_SETTING_MIN)
@@ -374,10 +374,18 @@ static void dvfs_stop(void)
374/** DPTC **/ 374/** DPTC **/
375 375
376/* Request tracking since boot */ 376/* Request tracking since boot */
377static bool dptc_running = false; /* Has driver enabled DPTC? */
378
377unsigned int dptc_nr_dn = 0; 379unsigned int dptc_nr_dn = 0;
378unsigned int dptc_nr_up = 0; 380unsigned int dptc_nr_up = 0;
379unsigned int dptc_nr_pnc = 0; 381unsigned int dptc_nr_pnc = 0;
380 382
383static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */
384static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */
385{ MC13783_SWITCHERS0, MC13783_SWITCHERS1 };
386static uint32_t dptc_reg_shadows[2]; /* shadow regs */
387static uint32_t dptc_regs_buf[2]; /* buffer for async write */
388
381 389
382/* Enable DPTC and unmask interrupt. */ 390/* Enable DPTC and unmask interrupt. */
383static void enable_dptc(void) 391static void enable_dptc(void)
@@ -397,123 +405,91 @@ static void enable_dptc(void)
397} 405}
398 406
399 407
400static void dptc_new_wp(unsigned int wp) 408/* Called after final PMIC read is completed */
409static void dptc_transfer_done_callback(struct spi_transfer_desc *xfer)
401{ 410{
402 unsigned int level = dvfs_level; 411 if (xfer->count != 0)
403 const union dvfs_dptc_voltage_table_entry *entry = &dvfs_dptc_voltage_table[wp]; 412 return;
404
405 uint32_t sw1a = check_regulator_setting(entry->sw1a);
406 uint32_t sw1advs = check_regulator_setting(entry->sw1advs);
407 uint32_t sw1bdvs = check_regulator_setting(entry->sw1bdvs);
408 uint32_t sw1bstby = check_regulator_setting(entry->sw1bstby);
409
410 dptc_wp = wp;
411
412 mc13783_write_masked(MC13783_SWITCHERS0,
413 sw1a << MC13783_SW1A_POS | /* SW1A */
414 sw1advs << MC13783_SW1ADVS_POS, /* SW1ADVS */
415 MC13783_SW1A | MC13783_SW1ADVS);
416
417 mc13783_write_masked(MC13783_SWITCHERS1,
418 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
419 sw1bstby << MC13783_SW1BSTBY_POS, /* SW1BSTBY */
420 MC13783_SW1BDVS | MC13783_SW1BSTBY);
421
422 413
423 udelay(100); /* Wait to settle */ 414 update_dptc_counts(dvfs_level, dptc_wp);
424 415
425 update_dptc_counts(level, wp); 416 if (dptc_running)
417 enable_dptc();
426} 418}
427 419
428 420
429/* DPTC service thread */ 421/* Handle the DPTC interrupt and sometimes the manual setting */
430#ifdef ROCKBOX_HAS_LOGF 422static void dptc_int(unsigned long pmcr0)
431#define DPTC_STACK_SIZE DEFAULT_STACK_SIZE
432#else
433#define DPTC_STACK_SIZE 160
434#endif
435static int dptc_thread_stack[DPTC_STACK_SIZE/sizeof(int)];
436static const char * const dptc_thread_name = "dptc";
437static struct wakeup dptc_wakeup; /* Object to signal upon DPTC event */
438static struct mutex dptc_mutex; /* Avoid mutually disrupting voltage updates */
439static unsigned long dptc_int_data; /* Data passed to thread for each event */
440static bool dptc_running = false; /* Has driver enabled DPTC? */
441
442
443static void dptc_interrupt_thread(void)
444{ 423{
445 int wp; 424 const union dvfs_dptc_voltage_table_entry *entry;
425 uint32_t sw1a, sw1advs, sw1bdvs, sw1bstby;
446 426
447 mutex_lock(&dptc_mutex); 427 int wp = dptc_wp;
428
429 /* Mask DPTC interrupt and disable DPTC until the change request is
430 * serviced. */
431 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
448 432
449 while (1) 433 switch (pmcr0 & CCM_PMCR0_PTVAI)
450 { 434 {
451 mutex_unlock(&dptc_mutex); 435 case CCM_PMCR0_PTVAI_DECREASE:
436 wp++;
437 dptc_nr_dn++;
438 break;
452 439
453 wakeup_wait(&dptc_wakeup, TIMEOUT_BLOCK); 440 case CCM_PMCR0_PTVAI_INCREASE:
441 wp--;
442 dptc_nr_up++;
443 break;
454 444
455 mutex_lock(&dptc_mutex); 445 case CCM_PMCR0_PTVAI_INCREASE_NOW:
446 if (--wp > DPTC_WP_PANIC)
447 wp = DPTC_WP_PANIC;
448 dptc_nr_pnc++;
449 break;
456 450
457 if (!dptc_running) 451 case CCM_PMCR0_PTVAI_NO_INT:
458 continue; 452 break; /* Just maintain at global level */
453 }
459 454
460 wp = dptc_wp; 455 if (wp < 0)
456 wp = 0;
457 else if (wp >= DPTC_NUM_WP)
458 wp = DPTC_NUM_WP - 1;
461 459
462 switch (dptc_int_data & CCM_PMCR0_PTVAI) 460 entry = &dvfs_dptc_voltage_table[wp];
463 {
464 case CCM_PMCR0_PTVAI_DECREASE:
465 wp++;
466 dptc_nr_dn++;
467 break;
468 461
469 case CCM_PMCR0_PTVAI_INCREASE: 462 sw1a = check_regulator_setting(entry->sw1a);
470 wp--; 463 sw1advs = check_regulator_setting(entry->sw1advs);
471 dptc_nr_up++; 464 sw1bdvs = check_regulator_setting(entry->sw1bdvs);
472 break; 465 sw1bstby = check_regulator_setting(entry->sw1bstby);
473 466
474 case CCM_PMCR0_PTVAI_INCREASE_NOW: 467 dptc_regs_buf[0] = dptc_reg_shadows[0] |
475 wp = DPTC_WP_PANIC; 468 sw1a << MC13783_SW1A_POS | /* SW1A */
476 dptc_nr_pnc++; 469 sw1advs << MC13783_SW1ADVS_POS; /* SW1ADVS */
477 break; 470 dptc_regs_buf[1] = dptc_reg_shadows[1] |
471 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
472 sw1bstby << MC13783_SW1BSTBY_POS; /* SW1BSTBY */
478 473
479 case CCM_PMCR0_PTVAI_NO_INT: 474 dptc_wp = wp;
480 logf("DPTC: unexpected INT");
481 continue;
482 }
483 475
484 if (wp < 0) 476 mc13783_write_async(&dptc_pmic_xfer, dptc_pmic_regs,
485 { 477 dptc_regs_buf, 2, dptc_transfer_done_callback);
486 wp = 0; 478}
487 logf("DPTC: already @ highest (%d)", wp);
488 }
489 else if (wp >= DPTC_NUM_WP)
490 {
491 wp = DPTC_NUM_WP - 1;
492 logf("DPTC: already @ lowest (%d)", wp);
493 }
494 else
495 {
496 logf("DPTC: new wp (%d)", wp);
497 }
498 479
499 dptc_new_wp(wp); 480
500 enable_dptc(); 481static void dptc_new_wp(unsigned int wp)
501 } 482{
483 dptc_wp = wp;
484 /* "NO_INT" so the working point isn't incremented, just set. */
485 dptc_int((CCM_PMCR0 & ~CCM_PMCR0_PTVAI) | CCM_PMCR0_PTVAI_NO_INT);
502} 486}
503 487
504 488
505/* Interrupt vector for DPTC */ 489/* Interrupt vector for DPTC */
506static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) 490static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
507{ 491{
508 /* Snapshot the interrupt cause */ 492 dptc_int(CCM_PMCR0);
509 unsigned long pmcr0 = CCM_PMCR0;
510 dptc_int_data = pmcr0;
511
512 /* Mask DPTC interrupt and disable DPTC until the change request is
513 * serviced. */
514 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
515
516 wakeup_signal(&dptc_wakeup);
517} 493}
518 494
519 495
@@ -524,23 +500,27 @@ static void dptc_init(void)
524 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM, 500 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
525 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN); 501 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
526 502
503 /* Shadow the regulator registers */
504 mc13783_read_regs(dptc_pmic_regs, dptc_reg_shadows, 2);
505
506 /* Pre-mask the fields we change */
507 dptc_reg_shadows[0] &= ~(MC13783_SW1A | MC13783_SW1ADVS);
508 dptc_reg_shadows[1] &= ~(MC13783_SW1BDVS | MC13783_SW1BSTBY);
509
527 /* Set default, safe working point. */ 510 /* Set default, safe working point. */
528 dptc_new_wp(DPTC_WP_DEFAULT); 511 dptc_new_wp(DPTC_WP_DEFAULT);
529 512
530 /* Interrupt goes to MCU, specified reference circuits enabled when 513 /* Interrupt goes to MCU, specified reference circuits enabled when
531 * DPTC is active. */ 514 * DPTC is active. */
532 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS | DPTC_DRCE_MASK); 515 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS);
516
517 imx31_regmod32(&CCM_PMCR0, DPTC_DRCE_MASK,
518 CCM_PMCR0_DRCE0 | CCM_PMCR0_DRCE1 |
519 CCM_PMCR0_DRCE2 | CCM_PMCR0_DRCE3);
533 520
534 /* DPTC counting range = 256 system clocks */ 521 /* DPTC counting range = 256 system clocks */
535 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR); 522 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
536 523
537 /* Create PMIC regulator service. */
538 wakeup_init(&dptc_wakeup);
539 mutex_init(&dptc_mutex);
540 create_thread(dptc_interrupt_thread,
541 dptc_thread_stack, sizeof(dptc_thread_stack), 0,
542 dptc_thread_name IF_PRIO(, PRIORITY_REALTIME_1) IF_COP(, CPU));
543
544 logf("DPTC: Initialized"); 524 logf("DPTC: Initialized");
545} 525}
546 526
@@ -548,11 +528,7 @@ static void dptc_init(void)
548/* Start DPTC module */ 528/* Start DPTC module */
549static void dptc_start(void) 529static void dptc_start(void)
550{ 530{
551 int oldstate; 531 int oldlevel = disable_irq_save();
552
553 mutex_lock(&dptc_mutex);
554
555 oldstate = disable_irq_save();
556 532
557 if (!dptc_running) 533 if (!dptc_running)
558 { 534 {
@@ -566,9 +542,7 @@ static void dptc_start(void)
566 enable_dptc(); 542 enable_dptc();
567 } 543 }
568 544
569 restore_irq(oldstate); 545 restore_irq(oldlevel);
570
571 mutex_unlock(&dptc_mutex);
572 546
573 logf("DPTC: started"); 547 logf("DPTC: started");
574} 548}
@@ -577,28 +551,20 @@ static void dptc_start(void)
577/* Stop the DPTC hardware if running and go back to default working point */ 551/* Stop the DPTC hardware if running and go back to default working point */
578static void dptc_stop(void) 552static void dptc_stop(void)
579{ 553{
580 int oldlevel; 554 int oldlevel = disable_irq_save();
581
582 mutex_lock(&dptc_mutex);
583
584 oldlevel = disable_irq_save();
585 555
586 if (dptc_running) 556 if (dptc_running)
587 { 557 {
588 /* Disable DPTC and mask interrupt. */ 558 /* Disable DPTC and mask interrupt. */
589 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM; 559 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
590 avic_disable_int(INT_CCM_CLK); 560 avic_disable_int(INT_CCM_CLK);
591 dptc_int_data = 0;
592
593 dptc_running = false; 561 dptc_running = false;
594 } 562 }
595 563
596 restore_irq(oldlevel);
597
598 /* Go back to default working point. */ 564 /* Go back to default working point. */
599 dptc_new_wp(DPTC_WP_DEFAULT); 565 dptc_new_wp(DPTC_WP_DEFAULT);
600 566
601 mutex_unlock(&dptc_mutex); 567 restore_irq(oldlevel);
602 568
603 logf("DPTC: stopped"); 569 logf("DPTC: stopped");
604} 570}
@@ -618,10 +584,7 @@ void dvfs_dptc_init(void)
618void dvfs_dptc_start(void) 584void dvfs_dptc_start(void)
619{ 585{
620 dvfs_start(); 586 dvfs_start();
621 if (0) /* Hold off for now */ 587 dptc_start();
622 {
623 dptc_start();
624 }
625} 588}
626 589
627 590
@@ -731,12 +694,10 @@ unsigned int dptc_get_wp(void)
731/* If DPTC is not running, set the working point explicitly */ 694/* If DPTC is not running, set the working point explicitly */
732void dptc_set_wp(unsigned int wp) 695void dptc_set_wp(unsigned int wp)
733{ 696{
734 mutex_lock(&dptc_mutex); 697 int oldlevel = disable_irq_save();
735 698
736 if (!dptc_running && wp < DPTC_NUM_WP) 699 if (!dptc_running && wp < DPTC_NUM_WP)
737 {
738 dptc_new_wp(wp); 700 dptc_new_wp(wp);
739 }
740 701
741 mutex_unlock(&dptc_mutex); 702 restore_irq(oldlevel);
742} 703}
diff --git a/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c
index 52293228f8..f7bc0ed37c 100644
--- a/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c
@@ -71,7 +71,7 @@ unsigned short adc_read(int channel)
71 71
72 /* Read all 8 channels that are converted - two channels in each 72 /* Read all 8 channels that are converted - two channels in each
73 * word. */ 73 * word. */
74 mc13783_read_regset(reg_array, channels[input_select], 4); 74 mc13783_read_regs(reg_array, channels[input_select], 4);
75 75
76 last_adc_read[input_select] = current_tick; 76 last_adc_read[input_select] = current_tick;
77 } 77 }
diff --git a/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c
index ab14a3c025..95f894bbce 100644
--- a/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c
@@ -143,7 +143,7 @@ void _backlight_on(void)
143 data[1] |= backlight_pwm_bits; 143 data[1] |= backlight_pwm_bits;
144 144
145 /* Write regs within 30us of each other (requires single xfer) */ 145 /* Write regs within 30us of each other (requires single xfer) */
146 mc13783_write_regset(regs, data, 2); 146 mc13783_write_regs(regs, data, 2);
147 } 147 }
148} 148}
149 149
diff --git a/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h b/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h
index 2356e23252..4876736a2b 100644
--- a/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h
+++ b/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h
@@ -75,9 +75,6 @@
75/* Define mask of which reference circuits are employed for DPTC */ 75/* Define mask of which reference circuits are employed for DPTC */
76#define DPTC_DRCE_MASK (CCM_PMCR0_DRCE1 | CCM_PMCR0_DRCE3) 76#define DPTC_DRCE_MASK (CCM_PMCR0_DRCE1 | CCM_PMCR0_DRCE3)
77 77
78/* When panicing, this working point is used */
79#define DPTC_PANIC_WP
80
81/* Due to a hardware bug in chip revisions < 2.0, when switching between 78/* Due to a hardware bug in chip revisions < 2.0, when switching between
82 * Serial and MCU PLLs, DVFS forces the target PLL to go into reset and 79 * Serial and MCU PLLs, DVFS forces the target PLL to go into reset and
83 * relock, only post divider frequency scaling is possible. 80 * relock, only post divider frequency scaling is possible.
diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c
index a083614488..5146122327 100644
--- a/firmware/target/arm/imx31/mc13783-imx31.c
+++ b/firmware/target/arm/imx31/mc13783-imx31.c
@@ -20,7 +20,6 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "system.h" 21#include "system.h"
22#include "cpu.h" 22#include "cpu.h"
23#include "spi-imx31.h"
24#include "gpio-imx31.h" 23#include "gpio-imx31.h"
25#include "mc13783.h" 24#include "mc13783.h"
26#include "debug.h" 25#include "debug.h"
@@ -29,9 +28,14 @@
29extern const struct mc13783_event_list mc13783_event_list; 28extern const struct mc13783_event_list mc13783_event_list;
30extern struct spi_node mc13783_spi; 29extern struct spi_node mc13783_spi;
31 30
31/* PMIC event service data */
32static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; 32static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)];
33static const char *mc13783_thread_name = "pmic"; 33static const char *mc13783_thread_name = "pmic";
34static struct wakeup mc13783_wake; 34static struct wakeup mc13783_svc_wake;
35
36/* Synchronous thread communication objects */
37static struct mutex mc13783_spi_mutex;
38static struct wakeup mc13783_spi_wake;
35 39
36/* Tracking for which interrupts are enabled */ 40/* Tracking for which interrupts are enabled */
37static uint32_t pmic_int_enabled[2] = 41static uint32_t pmic_int_enabled[2] =
@@ -45,6 +49,34 @@ static const unsigned char pmic_ints_regs[2] =
45 49
46static volatile unsigned int mc13783_thread_id = 0; 50static volatile unsigned int mc13783_thread_id = 0;
47 51
52static void mc13783_xfer_complete_cb(struct spi_transfer_desc *trans);
53
54/* Transfer descriptor for synchronous reads and writes */
55static struct spi_transfer_desc mc13783_transfer =
56{
57 .node = &mc13783_spi,
58 .txbuf = NULL,
59 .rxbuf = NULL,
60 .count = 0,
61 .callback = mc13783_xfer_complete_cb,
62 .next = NULL,
63};
64
65/* Called when a transfer is finished and data is ready/written */
66static void mc13783_xfer_complete_cb(struct spi_transfer_desc *xfer)
67{
68 if (xfer->count != 0)
69 return;
70
71 wakeup_signal(&mc13783_spi_wake);
72}
73
74static inline bool wait_for_transfer_complete(void)
75{
76 return wakeup_wait(&mc13783_spi_wake, HZ*2) == OBJ_WAIT_SUCCEEDED &&
77 mc13783_transfer.count == 0;
78}
79
48static void mc13783_interrupt_thread(void) 80static void mc13783_interrupt_thread(void)
49{ 81{
50 uint32_t pending[2]; 82 uint32_t pending[2];
@@ -56,18 +88,18 @@ static void mc13783_interrupt_thread(void)
56 { 88 {
57 const struct mc13783_event *event, *event_last; 89 const struct mc13783_event *event, *event_last;
58 90
59 wakeup_wait(&mc13783_wake, TIMEOUT_BLOCK); 91 wakeup_wait(&mc13783_svc_wake, TIMEOUT_BLOCK);
60 92
61 if (mc13783_thread_id == 0) 93 if (mc13783_thread_id == 0)
62 break; 94 break;
63 95
64 mc13783_read_regset(pmic_ints_regs, pending, 2); 96 mc13783_read_regs(pmic_ints_regs, pending, 2);
65 97
66 /* Only clear interrupts being dispatched */ 98 /* Only clear interrupts being dispatched */
67 pending[0] &= pmic_int_enabled[0]; 99 pending[0] &= pmic_int_enabled[0];
68 pending[1] &= pmic_int_enabled[1]; 100 pending[1] &= pmic_int_enabled[1];
69 101
70 mc13783_write_regset(pmic_ints_regs, pending, 2); 102 mc13783_write_regs(pmic_ints_regs, pending, 2);
71 103
72 /* Whatever is going to be serviced in this loop has been 104 /* Whatever is going to be serviced in this loop has been
73 * acknowledged. Reenable interrupt and if anything was still 105 * acknowledged. Reenable interrupt and if anything was still
@@ -93,7 +125,7 @@ static void mc13783_interrupt_thread(void)
93 } 125 }
94 126
95 if ((pending[0] | pending[1]) == 0) 127 if ((pending[0] | pending[1]) == 0)
96 break; /* Teminate early if nothing more to service */ 128 break; /* Terminate early if nothing more to service */
97 } 129 }
98 while (++event < event_last); 130 while (++event < event_last);
99 } 131 }
@@ -107,13 +139,16 @@ void mc13783_event(void)
107 /* Mask the interrupt (unmasked when PMIC thread services it). */ 139 /* Mask the interrupt (unmasked when PMIC thread services it). */
108 imx31_regclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE); 140 imx31_regclr32(&MC13783_GPIO_IMR, 1ul << MC13783_GPIO_LINE);
109 MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE); 141 MC13783_GPIO_ISR = (1ul << MC13783_GPIO_LINE);
110 wakeup_signal(&mc13783_wake); 142 wakeup_signal(&mc13783_svc_wake);
111} 143}
112 144
113void mc13783_init(void) 145void mc13783_init(void)
114{ 146{
115 /* Serial interface must have been initialized first! */ 147 /* Serial interface must have been initialized first! */
116 wakeup_init(&mc13783_wake); 148 wakeup_init(&mc13783_svc_wake);
149 mutex_init(&mc13783_spi_mutex);
150
151 wakeup_init(&mc13783_spi_wake);
117 152
118 /* Enable the PMIC SPI module */ 153 /* Enable the PMIC SPI module */
119 spi_enable_module(&mc13783_spi); 154 spi_enable_module(&mc13783_spi);
@@ -139,8 +174,9 @@ void mc13783_close(void)
139 return; 174 return;
140 175
141 mc13783_thread_id = 0; 176 mc13783_thread_id = 0;
142 wakeup_signal(&mc13783_wake); 177 wakeup_signal(&mc13783_svc_wake);
143 thread_wait(thread_id); 178 thread_wait(thread_id);
179 spi_disable_module(&mc13783_spi);
144} 180}
145 181
146bool mc13783_enable_event(enum mc13783_event_ids id) 182bool mc13783_enable_event(enum mc13783_event_ids id)
@@ -150,12 +186,12 @@ bool mc13783_enable_event(enum mc13783_event_ids id)
150 int set = event->set; 186 int set = event->set;
151 uint32_t mask = event->mask; 187 uint32_t mask = event->mask;
152 188
153 spi_lock(&mc13783_spi); 189 mutex_lock(&mc13783_spi_mutex);
154 190
155 pmic_int_enabled[set] |= mask; 191 pmic_int_enabled[set] |= mask;
156 mc13783_clear(pmic_intm_regs[set], mask); 192 mc13783_clear(pmic_intm_regs[set], mask);
157 193
158 spi_unlock(&mc13783_spi); 194 mutex_unlock(&mc13783_spi_mutex);
159 195
160 return true; 196 return true;
161} 197}
@@ -167,66 +203,77 @@ void mc13783_disable_event(enum mc13783_event_ids id)
167 int set = event->set; 203 int set = event->set;
168 uint32_t mask = event->mask; 204 uint32_t mask = event->mask;
169 205
170 spi_lock(&mc13783_spi); 206 mutex_lock(&mc13783_spi_mutex);
171 207
172 pmic_int_enabled[set] &= ~mask; 208 pmic_int_enabled[set] &= ~mask;
173 mc13783_set(pmic_intm_regs[set], mask); 209 mc13783_set(pmic_intm_regs[set], mask);
174 210
175 spi_unlock(&mc13783_spi); 211 mutex_unlock(&mc13783_spi_mutex);
176} 212}
177 213
178uint32_t mc13783_set(unsigned address, uint32_t bits) 214uint32_t mc13783_set(unsigned address, uint32_t bits)
179{ 215{
180 spi_lock(&mc13783_spi); 216 uint32_t data;
217
218 mutex_lock(&mc13783_spi_mutex);
181 219
182 uint32_t data = mc13783_read(address); 220 data = mc13783_read(address);
183 221
184 if (data != MC13783_DATA_ERROR) 222 if (data != MC13783_DATA_ERROR)
185 mc13783_write(address, data | bits); 223 mc13783_write(address, data | bits);
186 224
187 spi_unlock(&mc13783_spi); 225 mutex_unlock(&mc13783_spi_mutex);
188 226
189 return data; 227 return data;
190} 228}
191 229
192uint32_t mc13783_clear(unsigned address, uint32_t bits) 230uint32_t mc13783_clear(unsigned address, uint32_t bits)
193{ 231{
194 spi_lock(&mc13783_spi); 232 uint32_t data;
195 233
196 uint32_t data = mc13783_read(address); 234 mutex_lock(&mc13783_spi_mutex);
235
236 data = mc13783_read(address);
197 237
198 if (data != MC13783_DATA_ERROR) 238 if (data != MC13783_DATA_ERROR)
199 mc13783_write(address, data & ~bits); 239 mc13783_write(address, data & ~bits);
200 240
201 spi_unlock(&mc13783_spi); 241 mutex_unlock(&mc13783_spi_mutex);
202 242
203 return data; 243 return data;
204} 244}
205 245
206int mc13783_write(unsigned address, uint32_t data) 246int mc13783_write(unsigned address, uint32_t data)
207{ 247{
208 struct spi_transfer xfer;
209 uint32_t packet; 248 uint32_t packet;
249 int i;
210 250
211 if (address >= MC13783_NUM_REGS) 251 if (address >= MC13783_NUM_REGS)
212 return -1; 252 return -1;
213 253
214 packet = (1 << 31) | (address << 25) | (data & 0xffffff); 254 packet = (1 << 31) | (address << 25) | (data & 0xffffff);
215 xfer.txbuf = &packet;
216 xfer.rxbuf = &packet;
217 xfer.count = 1;
218 255
219 if (!spi_transfer(&mc13783_spi, &xfer)) 256 mutex_lock(&mc13783_spi_mutex);
220 return -1; 257
258 mc13783_transfer.txbuf = &packet;
259 mc13783_transfer.rxbuf = NULL;
260 mc13783_transfer.count = 1;
261
262 i = -1;
263
264 if (spi_transfer(&mc13783_transfer) && wait_for_transfer_complete())
265 i = 1 - mc13783_transfer.count;
266
267 mutex_unlock(&mc13783_spi_mutex);
221 268
222 return 1 - xfer.count; 269 return i;
223} 270}
224 271
225uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) 272uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask)
226{ 273{
227 uint32_t old; 274 uint32_t old;
228 275
229 spi_lock(&mc13783_spi); 276 mutex_lock(&mc13783_spi_mutex);
230 277
231 old = mc13783_read(address); 278 old = mc13783_read(address);
232 279
@@ -238,86 +285,144 @@ uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask)
238 old = MC13783_DATA_ERROR; 285 old = MC13783_DATA_ERROR;
239 } 286 }
240 287
241 spi_unlock(&mc13783_spi); 288 mutex_unlock(&mc13783_spi_mutex);
242 289
243 return old; 290 return old;
244} 291}
245 292
246int mc13783_write_regset(const unsigned char *regs, const uint32_t *data, 293uint32_t mc13783_read(unsigned address)
247 int count)
248{ 294{
249 int i; 295 uint32_t packet;
250 struct spi_transfer xfer;
251 uint32_t packets[MC13783_NUM_REGS];
252 296
253 if ((unsigned)count > MC13783_NUM_REGS) 297 if (address >= MC13783_NUM_REGS)
254 return -1; 298 return MC13783_DATA_ERROR;
299
300 packet = address << 25;
301
302 mutex_lock(&mc13783_spi_mutex);
303
304 mc13783_transfer.txbuf = &packet;
305 mc13783_transfer.rxbuf = &packet;
306 mc13783_transfer.count = 1;
307
308 if (!spi_transfer(&mc13783_transfer) || !wait_for_transfer_complete())
309 packet = MC13783_DATA_ERROR;
310
311 mutex_unlock(&mc13783_spi_mutex);
312
313 return packet;
314}
315
316int mc13783_read_regs(const unsigned char *regs, uint32_t *buffer,
317 int count)
318{
319 int i;
255 320
256 for (i = 0; i < count; i++) 321 for (i = 0; i < count; i++)
257 { 322 {
258 uint32_t reg = regs[i]; 323 unsigned reg = regs[i];
259 324
260 if (reg >= MC13783_NUM_REGS) 325 if (reg >= MC13783_NUM_REGS)
261 return -1; 326 return -1;
262 327
263 packets[i] = (1 << 31) | (reg << 25) | (data[i] & 0xffffff); 328 buffer[i] = reg << 25;
264 } 329 }
265 330
266 xfer.txbuf = packets; 331 mutex_lock(&mc13783_spi_mutex);
267 xfer.rxbuf = packets;
268 xfer.count = count;
269 332
270 if (!spi_transfer(&mc13783_spi, &xfer)) 333 mc13783_transfer.txbuf = buffer;
271 return -1; 334 mc13783_transfer.rxbuf = buffer;
335 mc13783_transfer.count = count;
336
337 i = -1;
338
339 if (spi_transfer(&mc13783_transfer) && wait_for_transfer_complete())
340 i = count - mc13783_transfer.count;
272 341
273 return count - xfer.count; 342 mutex_unlock(&mc13783_spi_mutex);
343
344 return i;
274} 345}
275 346
276uint32_t mc13783_read(unsigned address) 347int mc13783_write_regs(const unsigned char *regs, uint32_t *buffer,
348 int count)
277{ 349{
278 uint32_t packet; 350 int i;
279 struct spi_transfer xfer;
280 351
281 if (address >= MC13783_NUM_REGS) 352 for (i = 0; i < count; i++)
282 return MC13783_DATA_ERROR; 353 {
354 unsigned reg = regs[i];
283 355
284 packet = address << 25; 356 if (reg >= MC13783_NUM_REGS)
357 return -1;
285 358
286 xfer.txbuf = &packet; 359 buffer[i] = (1 << 31) | (reg << 25) | (buffer[i] & 0xffffff);
287 xfer.rxbuf = &packet; 360 }
288 xfer.count = 1;
289 361
290 if (!spi_transfer(&mc13783_spi, &xfer)) 362 mutex_lock(&mc13783_spi_mutex);
291 return MC13783_DATA_ERROR;
292 363
293 return packet; 364 mc13783_transfer.txbuf = buffer;
365 mc13783_transfer.rxbuf = NULL;
366 mc13783_transfer.count = count;
367
368 i = -1;
369
370 if (spi_transfer(&mc13783_transfer) && wait_for_transfer_complete())
371 i = count - mc13783_transfer.count;
372
373 mutex_unlock(&mc13783_spi_mutex);
374
375 return i;
294} 376}
295 377
296int mc13783_read_regset(const unsigned char *regs, uint32_t *buffer, 378#if 0 /* Not needed right now */
297 int count) 379bool mc13783_read_async(struct spi_transfer_desc *xfer,
380 const unsigned char *regs, uint32_t *buffer,
381 int count, spi_transfer_cb_fn_type callback)
298{ 382{
299 int i; 383 int i;
300 struct spi_transfer xfer;
301
302 if ((unsigned)count > MC13783_NUM_REGS)
303 return -1;
304 384
305 for (i = 0; i < count; i++) 385 for (i = 0; i < count; i++)
306 { 386 {
307 unsigned reg = regs[i]; 387 unsigned reg = regs[i];
308 388
309 if (reg >= MC13783_NUM_REGS) 389 if (reg >= MC13783_NUM_REGS)
310 return -1; 390 return false;
311 391
312 buffer[i] = reg << 25; 392 buffer[i] = reg << 25;
313 } 393 }
314 394
315 xfer.txbuf = buffer; 395 xfer->node = &mc13783_spi;
316 xfer.rxbuf = buffer; 396 xfer->txbuf = buffer;
317 xfer.count = count; 397 xfer->rxbuf = buffer;
398 xfer->count = count;
399 xfer->callback = callback;
318 400
319 if (!spi_transfer(&mc13783_spi, &xfer)) 401 return spi_transfer(xfer);
320 return -1; 402}
403#endif
404
405bool mc13783_write_async(struct spi_transfer_desc *xfer,
406 const unsigned char *regs, uint32_t *buffer,
407 int count, spi_transfer_cb_fn_type callback)
408{
409 int i;
410
411 for (i = 0; i < count; i++)
412 {
413 unsigned reg = regs[i];
414
415 if (reg >= MC13783_NUM_REGS)
416 return false;
417
418 buffer[i] = (1 << 31) | (reg << 25) | (buffer[i] & 0xffffff);
419 }
420
421 xfer->node = &mc13783_spi;
422 xfer->txbuf = buffer;
423 xfer->rxbuf = NULL;
424 xfer->count = count;
425 xfer->callback = callback;
321 426
322 return count - xfer.count; 427 return spi_transfer(xfer);
323} 428}
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}
diff --git a/firmware/target/arm/imx31/spi-imx31.h b/firmware/target/arm/imx31/spi-imx31.h
index cf536b646d..b5e31d46f2 100644
--- a/firmware/target/arm/imx31/spi-imx31.h
+++ b/firmware/target/arm/imx31/spi-imx31.h
@@ -61,29 +61,39 @@ struct spi_node
61 unsigned long periodreg; /* CSPI periodreg setup */ 61 unsigned long periodreg; /* CSPI periodreg setup */
62}; 62};
63 63
64struct spi_transfer 64struct spi_transfer_desc;
65
66typedef void (*spi_transfer_cb_fn_type)(struct spi_transfer_desc *);
67
68struct spi_transfer_desc
65{ 69{
66 const void *txbuf; 70 const struct spi_node *node; /* node for this transfer */
67 void *rxbuf; 71 const void *txbuf; /* buffer to transmit */
68 int count; 72 void *rxbuf; /* buffer to receive */
73 int count; /* number of elements */
74 spi_transfer_cb_fn_type callback; /* function to call when done */
75 struct spi_transfer_desc *next; /* next transfer queued,
76 spi layer sets this */
69}; 77};
70 78
79/* NOTE: SPI updates the descrptor during the operation. Do not write
80 * to it until completion notification is received. If no callback is
81 * specified, the caller must find a way to ensure integrity.
82 *
83 * -1 will be written to 'count' if an error occurs, otherwise it will
84 * be zero when completed.
85 */
86
71/* One-time init of SPI driver */ 87/* One-time init of SPI driver */
72void spi_init(void); 88void spi_init(void);
73 89
74/* Enable the specified module for the node */ 90/* Enable the specified module for the node */
75void spi_enable_module(struct spi_node *node); 91void spi_enable_module(const struct spi_node *node);
76 92
77/* Disabled the specified module for the node */ 93/* Disabled the specified module for the node */
78void spi_disable_module(struct spi_node *node); 94void spi_disable_module(const struct spi_node *node);
79
80/* Lock module mutex */
81void spi_lock(struct spi_node *node);
82
83/* Unlock module mutex */
84void spi_unlock(struct spi_node *node);
85 95
86/* Send and/or receive data on the specified node */ 96/* Send and/or receive data on the specified node (asychronous) */
87int spi_transfer(struct spi_node *node, struct spi_transfer *trans); 97bool spi_transfer(struct spi_transfer_desc *xfer);
88 98
89#endif /* SPI_IMX31_H */ 99#endif /* SPI_IMX31_H */