diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-05-04 10:07:53 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-05-04 10:07:53 +0000 |
commit | 931e06de64100e28031627964321da3fdb449378 (patch) | |
tree | 72a073d7ec3ede9554b394887d43d19fda6e8177 | |
parent | 7480afb3c59f4aebff262e1ce47395a3924ca994 (diff) | |
download | rockbox-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
-rw-r--r-- | firmware/drivers/rtc/rtc_mc13783.c | 6 | ||||
-rw-r--r-- | firmware/export/config/gigabeats.h | 2 | ||||
-rw-r--r-- | firmware/export/mc13783.h | 18 | ||||
-rw-r--r-- | firmware/target/arm/imx31/debug-imx31.c | 2 | ||||
-rw-r--r-- | firmware/target/arm/imx31/dvfs_dptc-imx31.c | 213 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/adc-gigabeat-s.c | 2 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/backlight-gigabeat-s.c | 2 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h | 3 | ||||
-rw-r--r-- | firmware/target/arm/imx31/mc13783-imx31.c | 241 | ||||
-rw-r--r-- | firmware/target/arm/imx31/spi-imx31.c | 419 | ||||
-rw-r--r-- | firmware/target/arm/imx31/spi-imx31.h | 38 |
11 files changed, 555 insertions, 391 deletions
diff --git a/firmware/drivers/rtc/rtc_mc13783.c b/firmware/drivers/rtc/rtc_mc13783.c index e36faa6089..9cef3e904c 100644 --- a/firmware/drivers/rtc/rtc_mc13783.c +++ b/firmware/drivers/rtc/rtc_mc13783.c | |||
@@ -104,8 +104,8 @@ int rtc_read_datetime(struct tm *tm) | |||
104 | * greater */ | 104 | * greater */ |
105 | do | 105 | do |
106 | { | 106 | { |
107 | if (mc13783_read_regset(rtc_registers, regs, | 107 | if (mc13783_read_regs(rtc_registers, regs, |
108 | RTC_NUM_REGS) < RTC_NUM_REGS) | 108 | RTC_NUM_REGS) < RTC_NUM_REGS) |
109 | { | 109 | { |
110 | /* Couldn't read registers */ | 110 | /* Couldn't read registers */ |
111 | return 0; | 111 | return 0; |
@@ -204,7 +204,7 @@ int rtc_write_datetime(const struct tm *tm) | |||
204 | 204 | ||
205 | regs[RTC_REG_DAY] = day + tm->tm_mday - 1 - base_yearday; | 205 | regs[RTC_REG_DAY] = day + tm->tm_mday - 1 - base_yearday; |
206 | 206 | ||
207 | if (mc13783_write_regset(rtc_registers, regs, 2) == 2) | 207 | if (mc13783_write_regs(rtc_registers, regs, 2) == 2) |
208 | { | 208 | { |
209 | return 7; | 209 | return 7; |
210 | } | 210 | } |
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h index e64ceb0171..2e489239b9 100644 --- a/firmware/export/config/gigabeats.h +++ b/firmware/export/config/gigabeats.h | |||
@@ -147,7 +147,7 @@ | |||
147 | #define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) | 147 | #define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) |
148 | 148 | ||
149 | /* Define this if target has an additional number of threads specific to it */ | 149 | /* Define this if target has an additional number of threads specific to it */ |
150 | #define TARGET_EXTRA_THREADS 3 | 150 | #define TARGET_EXTRA_THREADS 2 |
151 | 151 | ||
152 | /* Type of mobile power - check this out */ | 152 | /* Type of mobile power - check this out */ |
153 | #define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */ | 153 | #define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */ |
diff --git a/firmware/export/mc13783.h b/firmware/export/mc13783.h index 0a83527c57..e513fa1717 100644 --- a/firmware/export/mc13783.h +++ b/firmware/export/mc13783.h | |||
@@ -21,6 +21,8 @@ | |||
21 | #ifndef _MC13783_H_ | 21 | #ifndef _MC13783_H_ |
22 | #define _MC13783_H_ | 22 | #define _MC13783_H_ |
23 | 23 | ||
24 | #include "spi-imx31.h" | ||
25 | |||
24 | enum mc13783_regs_enum | 26 | enum mc13783_regs_enum |
25 | { | 27 | { |
26 | MC13783_INTERRUPT_STATUS0 = 0, | 28 | MC13783_INTERRUPT_STATUS0 = 0, |
@@ -1261,11 +1263,21 @@ void mc13783_init(void); | |||
1261 | void mc13783_close(void); | 1263 | void mc13783_close(void); |
1262 | uint32_t mc13783_set(unsigned address, uint32_t bits); | 1264 | uint32_t mc13783_set(unsigned address, uint32_t bits); |
1263 | uint32_t mc13783_clear(unsigned address, uint32_t bits); | 1265 | uint32_t mc13783_clear(unsigned address, uint32_t bits); |
1266 | uint32_t mc13783_read(unsigned address); | ||
1264 | int mc13783_write(unsigned address, uint32_t data); | 1267 | int mc13783_write(unsigned address, uint32_t data); |
1265 | uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask); | 1268 | uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask); |
1266 | int mc13783_write_regset(const unsigned char *regs, const uint32_t *data, int count); | 1269 | /* buffer must be available as packet workspace */ |
1267 | uint32_t mc13783_read(unsigned address); | 1270 | int mc13783_read_regs(const unsigned char *regs, uint32_t *buffer, int count); |
1268 | int mc13783_read_regset(const unsigned char *regs, uint32_t *buffer, int count); | 1271 | /* buffer must be available as packet workspace */ |
1272 | int mc13783_write_regs(const unsigned char *regs, uint32_t *buffer, int count); | ||
1273 | /* buffer must be available as packet workspace */ | ||
1274 | bool mc13783_read_async(struct spi_transfer_desc *xfer, | ||
1275 | const unsigned char *regs, uint32_t *buffer, | ||
1276 | int count, spi_transfer_cb_fn_type callback); | ||
1277 | /* buffer must be available as packet workspace */ | ||
1278 | bool mc13783_write_async(struct spi_transfer_desc *xfer, | ||
1279 | const unsigned char *regs, uint32_t *buffer, | ||
1280 | int count, spi_transfer_cb_fn_type callback); | ||
1269 | 1281 | ||
1270 | #define MC13783_DATA_ERROR UINT32_MAX | 1282 | #define MC13783_DATA_ERROR UINT32_MAX |
1271 | 1283 | ||
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 | ||
56 | static inline uint32_t check_regulator_setting(uint32_t setting) | 56 | static 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 */ |
377 | static bool dptc_running = false; /* Has driver enabled DPTC? */ | ||
378 | |||
377 | unsigned int dptc_nr_dn = 0; | 379 | unsigned int dptc_nr_dn = 0; |
378 | unsigned int dptc_nr_up = 0; | 380 | unsigned int dptc_nr_up = 0; |
379 | unsigned int dptc_nr_pnc = 0; | 381 | unsigned int dptc_nr_pnc = 0; |
380 | 382 | ||
383 | static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */ | ||
384 | static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */ | ||
385 | { MC13783_SWITCHERS0, MC13783_SWITCHERS1 }; | ||
386 | static uint32_t dptc_reg_shadows[2]; /* shadow regs */ | ||
387 | static 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. */ |
383 | static void enable_dptc(void) | 391 | static void enable_dptc(void) |
@@ -397,123 +405,91 @@ static void enable_dptc(void) | |||
397 | } | 405 | } |
398 | 406 | ||
399 | 407 | ||
400 | static void dptc_new_wp(unsigned int wp) | 408 | /* Called after final PMIC read is completed */ |
409 | static 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 | 422 | static void dptc_int(unsigned long pmcr0) |
431 | #define DPTC_STACK_SIZE DEFAULT_STACK_SIZE | ||
432 | #else | ||
433 | #define DPTC_STACK_SIZE 160 | ||
434 | #endif | ||
435 | static int dptc_thread_stack[DPTC_STACK_SIZE/sizeof(int)]; | ||
436 | static const char * const dptc_thread_name = "dptc"; | ||
437 | static struct wakeup dptc_wakeup; /* Object to signal upon DPTC event */ | ||
438 | static struct mutex dptc_mutex; /* Avoid mutually disrupting voltage updates */ | ||
439 | static unsigned long dptc_int_data; /* Data passed to thread for each event */ | ||
440 | static bool dptc_running = false; /* Has driver enabled DPTC? */ | ||
441 | |||
442 | |||
443 | static 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(); | 481 | static 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 */ |
506 | static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) | 490 | static __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 */ |
549 | static void dptc_start(void) | 529 | static 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 */ |
578 | static void dptc_stop(void) | 552 | static 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) | |||
618 | void dvfs_dptc_start(void) | 584 | void 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 */ |
732 | void dptc_set_wp(unsigned int wp) | 695 | void 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 @@ | |||
29 | extern const struct mc13783_event_list mc13783_event_list; | 28 | extern const struct mc13783_event_list mc13783_event_list; |
30 | extern struct spi_node mc13783_spi; | 29 | extern struct spi_node mc13783_spi; |
31 | 30 | ||
31 | /* PMIC event service data */ | ||
32 | static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; | 32 | static int mc13783_thread_stack[DEFAULT_STACK_SIZE/sizeof(int)]; |
33 | static const char *mc13783_thread_name = "pmic"; | 33 | static const char *mc13783_thread_name = "pmic"; |
34 | static struct wakeup mc13783_wake; | 34 | static struct wakeup mc13783_svc_wake; |
35 | |||
36 | /* Synchronous thread communication objects */ | ||
37 | static struct mutex mc13783_spi_mutex; | ||
38 | static struct wakeup mc13783_spi_wake; | ||
35 | 39 | ||
36 | /* Tracking for which interrupts are enabled */ | 40 | /* Tracking for which interrupts are enabled */ |
37 | static uint32_t pmic_int_enabled[2] = | 41 | static uint32_t pmic_int_enabled[2] = |
@@ -45,6 +49,34 @@ static const unsigned char pmic_ints_regs[2] = | |||
45 | 49 | ||
46 | static volatile unsigned int mc13783_thread_id = 0; | 50 | static volatile unsigned int mc13783_thread_id = 0; |
47 | 51 | ||
52 | static void mc13783_xfer_complete_cb(struct spi_transfer_desc *trans); | ||
53 | |||
54 | /* Transfer descriptor for synchronous reads and writes */ | ||
55 | static 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 */ | ||
66 | static 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 | |||
74 | static 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 | |||
48 | static void mc13783_interrupt_thread(void) | 80 | static 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 | ||
113 | void mc13783_init(void) | 145 | void 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 | ||
146 | bool mc13783_enable_event(enum mc13783_event_ids id) | 182 | bool 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 | ||
178 | uint32_t mc13783_set(unsigned address, uint32_t bits) | 214 | uint32_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 | ||
192 | uint32_t mc13783_clear(unsigned address, uint32_t bits) | 230 | uint32_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 | ||
206 | int mc13783_write(unsigned address, uint32_t data) | 246 | int 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 | ||
225 | uint32_t mc13783_write_masked(unsigned address, uint32_t data, uint32_t mask) | 272 | uint32_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 | ||
246 | int mc13783_write_regset(const unsigned char *regs, const uint32_t *data, | 293 | uint32_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 | |||
316 | int 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 | ||
276 | uint32_t mc13783_read(unsigned address) | 347 | int 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 | ||
296 | int mc13783_read_regset(const unsigned char *regs, uint32_t *buffer, | 378 | #if 0 /* Not needed right now */ |
297 | int count) | 379 | bool 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 | |||
405 | bool 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 */ |
41 | static struct spi_module_descriptor | 41 | static 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 */ | ||
83 | static 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 */ | ||
91 | static 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. */ | ||
121 | static 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 */ | ||
158 | static 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 */ |
84 | static void spi_interrupt(enum spi_module_number spi) | 189 | static 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 */ |
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) | 326 | void 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 */ | ||
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 */ | 338 | /* Enable the specified module for the node */ |
258 | void spi_enable_module(struct spi_node *node) | 339 | void 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 */ |
283 | void spi_disable_module(struct spi_node *node) | 357 | void 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 */ |
308 | int spi_transfer(struct spi_node *node, struct spi_transfer *trans) | 381 | bool 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 | ||
64 | struct spi_transfer | 64 | struct spi_transfer_desc; |
65 | |||
66 | typedef void (*spi_transfer_cb_fn_type)(struct spi_transfer_desc *); | ||
67 | |||
68 | struct 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 */ |
72 | void spi_init(void); | 88 | void spi_init(void); |
73 | 89 | ||
74 | /* Enable the specified module for the node */ | 90 | /* Enable the specified module for the node */ |
75 | void spi_enable_module(struct spi_node *node); | 91 | void 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 */ |
78 | void spi_disable_module(struct spi_node *node); | 94 | void spi_disable_module(const struct spi_node *node); |
79 | |||
80 | /* Lock module mutex */ | ||
81 | void spi_lock(struct spi_node *node); | ||
82 | |||
83 | /* Unlock module mutex */ | ||
84 | void 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) */ |
87 | int spi_transfer(struct spi_node *node, struct spi_transfer *trans); | 97 | bool spi_transfer(struct spi_transfer_desc *xfer); |
88 | 98 | ||
89 | #endif /* SPI_IMX31_H */ | 99 | #endif /* SPI_IMX31_H */ |