diff options
Diffstat (limited to 'firmware/target/arm/as3525/ascodec-as3525.c')
-rw-r--r-- | firmware/target/arm/as3525/ascodec-as3525.c | 98 |
1 files changed, 68 insertions, 30 deletions
diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c index 87a1447c63..52d50ef077 100644 --- a/firmware/target/arm/as3525/ascodec-as3525.c +++ b/firmware/target/arm/as3525/ascodec-as3525.c | |||
@@ -185,10 +185,18 @@ void ascodec_init(void) | |||
185 | 185 | ||
186 | I2C2_IMR = 0x00; /* disable interrupts */ | 186 | I2C2_IMR = 0x00; /* disable interrupts */ |
187 | I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */ | 187 | I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */ |
188 | VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO | INTERRUPT_AUDIO; | 188 | VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO; |
189 | #if CONFIG_CPU == AS3525 /* interrupts do not work correctly on as3525v2 */ | ||
190 | VIC_INT_ENABLE = INTERRUPT_AUDIO; | ||
191 | #endif | ||
189 | 192 | ||
190 | /* Generate irq for usb+charge status change */ | 193 | /* Generate irq for usb+charge status change */ |
191 | ascodec_write(AS3514_IRQ_ENRD0, /*IRQ_CHGSTAT |*/ IRQ_USBSTAT); | 194 | ascodec_write(AS3514_IRQ_ENRD0, |
195 | #ifdef CONFIG_CHARGING /* m200v4 can't charge */ | ||
196 | IRQ_CHGSTAT | IRQ_ENDOFCH | | ||
197 | #endif | ||
198 | IRQ_USBSTAT); | ||
199 | |||
192 | /* Generate irq for push-pull, active high, irq on rtc+adc change */ | 200 | /* Generate irq for push-pull, active high, irq on rtc+adc change */ |
193 | ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE | | 201 | ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE | |
194 | /*IRQ_RTC |*/ IRQ_ADC); | 202 | /*IRQ_RTC |*/ IRQ_ADC); |
@@ -342,19 +350,8 @@ static void ascodec_wait(struct ascodec_request *req) | |||
342 | void ascodec_async_write(unsigned int index, unsigned int value, | 350 | void ascodec_async_write(unsigned int index, unsigned int value, |
343 | struct ascodec_request *req) | 351 | struct ascodec_request *req) |
344 | { | 352 | { |
345 | switch(index) { | 353 | if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */ |
346 | case AS3514_CVDD_DCDC3: | ||
347 | /* prevent setting of the LREG_CP_not bit */ | ||
348 | value &= ~(1 << 5); | 354 | value &= ~(1 << 5); |
349 | break; | ||
350 | case AS3514_IRQ_ENRD0: | ||
351 | /* save value in register shadow | ||
352 | * for ascodec_(en|dis)able_endofch_irq() */ | ||
353 | ascodec_enrd0_shadow = value; | ||
354 | break; | ||
355 | default: | ||
356 | break; | ||
357 | } | ||
358 | 355 | ||
359 | ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1); | 356 | ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1); |
360 | req->data[0] = value; | 357 | req->data[0] = value; |
@@ -429,24 +426,37 @@ int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data) | |||
429 | return i; | 426 | return i; |
430 | } | 427 | } |
431 | 428 | ||
432 | #if CONFIG_CPU == AS3525 | 429 | /* |
433 | static void ascodec_read_cb(unsigned const char *data, unsigned int len) | 430 | * Reading AS3514_IRQ_ENRD0 clears all interrupt bits, so we cache the results |
431 | * and clear individual bits when a specific interrupt is checked: | ||
432 | * - we clear the ENDOFCH (end of charge) interrupt when it's read | ||
433 | * - we set the usb and charger presence when the status change is detected | ||
434 | * | ||
435 | * on AS3525(v1) ENRD0 is only read in an interrupt handler | ||
436 | * on AS3525v2 the interrupt handler doesn't work (yet), so we read the register | ||
437 | * synchronously. | ||
438 | * - To avoid race conditions all the reads to this register must be atomic. | ||
439 | * We don't need to disable interrupts when reading it because all the reads | ||
440 | * (in powermgmt-ascodec.c and power-as3525.c) are performed by the same | ||
441 | * thread (the power thread). | ||
442 | */ | ||
443 | static void cache_enrd0(int enrd0) | ||
434 | { | 444 | { |
435 | if (len != 3) /* some error happened? */ | 445 | if (enrd0 & CHG_ENDOFCH) { /* chg finished */ |
436 | return; | 446 | ascodec_enrd0_shadow |= CHG_ENDOFCH; |
437 | |||
438 | if (data[0] & CHG_ENDOFCH) { /* chg finished */ | ||
439 | IFDEBUG(int_chg_finished++); | 447 | IFDEBUG(int_chg_finished++); |
440 | } | 448 | } |
441 | if (data[0] & CHG_CHANGED) { /* chg status changed */ | 449 | if (enrd0 & CHG_CHANGED) { /* chg status changed */ |
442 | if (data[0] & CHG_STATUS) { | 450 | if (enrd0 & CHG_STATUS) { |
451 | ascodec_enrd0_shadow |= CHG_STATUS; | ||
443 | IFDEBUG(int_chg_insert++); | 452 | IFDEBUG(int_chg_insert++); |
444 | } else { | 453 | } else { |
454 | ascodec_enrd0_shadow &= ~CHG_STATUS; | ||
445 | IFDEBUG(int_chg_remove++); | 455 | IFDEBUG(int_chg_remove++); |
446 | } | 456 | } |
447 | } | 457 | } |
448 | if (data[0] & USB_CHANGED) { /* usb status changed */ | 458 | if (enrd0 & USB_CHANGED) { /* usb status changed */ |
449 | if (data[0] & USB_STATUS) { | 459 | if (enrd0 & USB_STATUS) { |
450 | IFDEBUG(int_usb_insert++); | 460 | IFDEBUG(int_usb_insert++); |
451 | usb_insert_int(); | 461 | usb_insert_int(); |
452 | } else { | 462 | } else { |
@@ -454,6 +464,16 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len) | |||
454 | usb_remove_int(); | 464 | usb_remove_int(); |
455 | } | 465 | } |
456 | } | 466 | } |
467 | } | ||
468 | |||
469 | #if CONFIG_CPU == AS3525 | ||
470 | static void ascodec_read_cb(unsigned const char *data, unsigned int len) | ||
471 | { | ||
472 | if (len != 3) /* some error happened? */ | ||
473 | return; | ||
474 | |||
475 | cache_enrd0(data[0]); | ||
476 | |||
457 | if (data[2] & IRQ_RTC) { /* rtc irq */ | 477 | if (data[2] & IRQ_RTC) { /* rtc irq */ |
458 | /* | 478 | /* |
459 | * Can be configured for once per second or once per minute, | 479 | * Can be configured for once per second or once per minute, |
@@ -468,22 +488,40 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len) | |||
468 | VIC_INT_ENABLE = INTERRUPT_AUDIO; | 488 | VIC_INT_ENABLE = INTERRUPT_AUDIO; |
469 | } | 489 | } |
470 | 490 | ||
491 | #endif /* CONFIG_CPU == AS3525 */ | ||
492 | |||
471 | void ascodec_wait_adc_finished(void) | 493 | void ascodec_wait_adc_finished(void) |
472 | { | 494 | { |
495 | #if CONFIG_CPU == AS3525 | ||
473 | wakeup_wait(&adc_wkup, TIMEOUT_BLOCK); | 496 | wakeup_wait(&adc_wkup, TIMEOUT_BLOCK); |
497 | #else | ||
498 | /* no interrupts, busy wait | ||
499 | * XXX: make sure this is the only reader of IRQ_ENRD2 | ||
500 | */ | ||
501 | while(!(ascodec_read(AS3514_IRQ_ENRD2) & IRQ_ADC)) | ||
502 | yield(); | ||
503 | #endif | ||
474 | } | 504 | } |
475 | #endif /* CONFIG_CPU == AS3525 */ | ||
476 | |||
477 | 505 | ||
478 | void ascodec_enable_endofch_irq(void) | 506 | #ifdef CONFIG_CHARGING |
507 | bool ascodec_endofch(void) | ||
479 | { | 508 | { |
480 | ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow | CHG_ENDOFCH); | 509 | #if CONFIG_CPU != AS3525 |
510 | cache_enrd0(ascodec_read(AS3514_IRQ_ENRD0)); | ||
511 | #endif | ||
512 | bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH; | ||
513 | ascodec_enrd0_shadow &= ~CHG_ENDOFCH; // clear interrupt | ||
514 | return ret; | ||
481 | } | 515 | } |
482 | 516 | ||
483 | void ascodec_disable_endofch_irq(void) | 517 | bool ascodec_chg_status(void) |
484 | { | 518 | { |
485 | ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow & ~CHG_ENDOFCH); | 519 | #if CONFIG_CPU != AS3525 |
520 | cache_enrd0(ascodec_read(AS3514_IRQ_ENRD0)); | ||
521 | #endif | ||
522 | return ascodec_enrd0_shadow & CHG_STATUS; | ||
486 | } | 523 | } |
524 | #endif /* CONFIG_CHARGING */ | ||
487 | 525 | ||
488 | /* | 526 | /* |
489 | * NOTE: | 527 | * NOTE: |