as3514/as3543 fixes

- Enable end of charge monitoring once, it doesn't need to be disabled
- Acknowledge the first (wrong) end of charge interrupt on charger enable
  (this had been broken in r25299)
- Centralize reads to ENRD* registers and cache the results when needed
    - on PP it is not needed because reads are atomic, we only check for
      end of charge when the charging, and for charger presence when
      discharging

as3525v2 (using as3543) specifics
- I got the datasheet today from AMS, thanks to them for being so fast
  and not require me to sign tons of papers!
- USB detection now works on as3525v2 using the as3543. Clip+ won't
  reboot to OF yet, it needs mkamsboot support first (usbstack disabled)
- Charging should work, the CHARGER register is at a different place, it
  is an extended PMU register -> use ascodec_read/write_charger() to
  access it
- real interrupts are not used yet for ENRD, we get thousands of
  interrupts per second, apparently only limited by the i2c clock.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26116 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Rafaël Carré 2010-05-17 20:53:25 +00:00
parent 2ed7745dde
commit 88c55d7290
10 changed files with 156 additions and 106 deletions

View file

@ -185,10 +185,18 @@ void ascodec_init(void)
I2C2_IMR = 0x00; /* disable interrupts */
I2C2_INT_CLR |= I2C2_RIS; /* clear interrupt status */
VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO | INTERRUPT_AUDIO;
VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO;
#if CONFIG_CPU == AS3525 /* interrupts do not work correctly on as3525v2 */
VIC_INT_ENABLE = INTERRUPT_AUDIO;
#endif
/* Generate irq for usb+charge status change */
ascodec_write(AS3514_IRQ_ENRD0, /*IRQ_CHGSTAT |*/ IRQ_USBSTAT);
ascodec_write(AS3514_IRQ_ENRD0,
#ifdef CONFIG_CHARGING /* m200v4 can't charge */
IRQ_CHGSTAT | IRQ_ENDOFCH |
#endif
IRQ_USBSTAT);
/* Generate irq for push-pull, active high, irq on rtc+adc change */
ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE |
/*IRQ_RTC |*/ IRQ_ADC);
@ -342,19 +350,8 @@ static void ascodec_wait(struct ascodec_request *req)
void ascodec_async_write(unsigned int index, unsigned int value,
struct ascodec_request *req)
{
switch(index) {
case AS3514_CVDD_DCDC3:
/* prevent setting of the LREG_CP_not bit */
if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */
value &= ~(1 << 5);
break;
case AS3514_IRQ_ENRD0:
/* save value in register shadow
* for ascodec_(en|dis)able_endofch_irq() */
ascodec_enrd0_shadow = value;
break;
default:
break;
}
ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1);
req->data[0] = value;
@ -429,24 +426,37 @@ int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data)
return i;
}
#if CONFIG_CPU == AS3525
static void ascodec_read_cb(unsigned const char *data, unsigned int len)
/*
* Reading AS3514_IRQ_ENRD0 clears all interrupt bits, so we cache the results
* and clear individual bits when a specific interrupt is checked:
* - we clear the ENDOFCH (end of charge) interrupt when it's read
* - we set the usb and charger presence when the status change is detected
*
* on AS3525(v1) ENRD0 is only read in an interrupt handler
* on AS3525v2 the interrupt handler doesn't work (yet), so we read the register
* synchronously.
* - To avoid race conditions all the reads to this register must be atomic.
* We don't need to disable interrupts when reading it because all the reads
* (in powermgmt-ascodec.c and power-as3525.c) are performed by the same
* thread (the power thread).
*/
static void cache_enrd0(int enrd0)
{
if (len != 3) /* some error happened? */
return;
if (data[0] & CHG_ENDOFCH) { /* chg finished */
if (enrd0 & CHG_ENDOFCH) { /* chg finished */
ascodec_enrd0_shadow |= CHG_ENDOFCH;
IFDEBUG(int_chg_finished++);
}
if (data[0] & CHG_CHANGED) { /* chg status changed */
if (data[0] & CHG_STATUS) {
if (enrd0 & CHG_CHANGED) { /* chg status changed */
if (enrd0 & CHG_STATUS) {
ascodec_enrd0_shadow |= CHG_STATUS;
IFDEBUG(int_chg_insert++);
} else {
ascodec_enrd0_shadow &= ~CHG_STATUS;
IFDEBUG(int_chg_remove++);
}
}
if (data[0] & USB_CHANGED) { /* usb status changed */
if (data[0] & USB_STATUS) {
if (enrd0 & USB_CHANGED) { /* usb status changed */
if (enrd0 & USB_STATUS) {
IFDEBUG(int_usb_insert++);
usb_insert_int();
} else {
@ -454,6 +464,16 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len)
usb_remove_int();
}
}
}
#if CONFIG_CPU == AS3525
static void ascodec_read_cb(unsigned const char *data, unsigned int len)
{
if (len != 3) /* some error happened? */
return;
cache_enrd0(data[0]);
if (data[2] & IRQ_RTC) { /* rtc irq */
/*
* Can be configured for once per second or once per minute,
@ -468,23 +488,41 @@ static void ascodec_read_cb(unsigned const char *data, unsigned int len)
VIC_INT_ENABLE = INTERRUPT_AUDIO;
}
void ascodec_wait_adc_finished(void)
{
wakeup_wait(&adc_wkup, TIMEOUT_BLOCK);
}
#endif /* CONFIG_CPU == AS3525 */
void ascodec_enable_endofch_irq(void)
void ascodec_wait_adc_finished(void)
{
ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow | CHG_ENDOFCH);
#if CONFIG_CPU == AS3525
wakeup_wait(&adc_wkup, TIMEOUT_BLOCK);
#else
/* no interrupts, busy wait
* XXX: make sure this is the only reader of IRQ_ENRD2
*/
while(!(ascodec_read(AS3514_IRQ_ENRD2) & IRQ_ADC))
yield();
#endif
}
void ascodec_disable_endofch_irq(void)
#ifdef CONFIG_CHARGING
bool ascodec_endofch(void)
{
ascodec_write(AS3514_IRQ_ENRD0, ascodec_enrd0_shadow & ~CHG_ENDOFCH);
#if CONFIG_CPU != AS3525
cache_enrd0(ascodec_read(AS3514_IRQ_ENRD0));
#endif
bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH;
ascodec_enrd0_shadow &= ~CHG_ENDOFCH; // clear interrupt
return ret;
}
bool ascodec_chg_status(void)
{
#if CONFIG_CPU != AS3525
cache_enrd0(ascodec_read(AS3514_IRQ_ENRD0));
#endif
return ascodec_enrd0_shadow & CHG_STATUS;
}
#endif /* CONFIG_CHARGING */
/*
* NOTE:
* After the conversion to interrupts, ascodec_(lock|unlock) are only used by

View file

@ -52,7 +52,7 @@
/*
* How many bytes we using in struct ascodec_request for the data buffer.
* 4 fits the alignment best right now.
* We don't actually use more than 2 at the moment (in adc_read).
* We don't actually use more than 3 at the moment (when reading interrupts)
* Upper limit would be 255 since DACNT is 8 bits!
*/
#define ASCODEC_REQ_MAXLEN 4
@ -74,19 +74,6 @@ void ascodec_init(void);
int ascodec_write(unsigned int index, unsigned int value);
#if CONFIG_CPU == AS3525v2
static inline void ascodec_write_pmu(unsigned int index, unsigned int subreg,
unsigned int value)
{
/* we disable interrupts to make sure no operation happen on the i2c bus
* between selecting the sub register and writing to it */
int oldstatus = disable_irq_save();
ascodec_write(AS3543_PMU_ENABLE, 8|subreg);
ascodec_write(index, value);
restore_irq(oldstatus);
}
#endif
int ascodec_read(unsigned int index);
int ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data);
@ -119,18 +106,55 @@ void ascodec_lock(void);
void ascodec_unlock(void);
#if CONFIG_CPU == AS3525
void ascodec_wait_adc_finished(void);
#else
static inline void ascodec_wait_adc_finished(void)
static inline void ascodec_monitor_endofch(void) {} /* already enabled */
bool ascodec_endofch(void);
bool ascodec_chg_status(void);
#if CONFIG_CPU == AS3525v2
static inline void ascodec_write_pmu(unsigned int index, unsigned int subreg,
unsigned int value)
{
/* FIXME: Doesn't work yet on AS3525v2 */
/* we disable interrupts to make sure no operation happen on the i2c bus
* between selecting the sub register and writing to it */
int oldstatus = disable_irq_save();
ascodec_write(AS3543_PMU_ENABLE, 8|subreg);
ascodec_write(index, value);
restore_irq(oldstatus);
}
static inline int ascodec_read_pmu(unsigned int index, unsigned int subreg)
{
/* we disable interrupts to make sure no operation happen on the i2c bus
* between selecting the sub register and reading it */
int oldstatus = disable_irq_save();
ascodec_write(AS3543_PMU_ENABLE, 8|subreg);
int ret = ascodec_read(index);
restore_irq(oldstatus);
return ret;
}
#endif /* CONFIG_CPU == AS3525v2 */
static inline void ascodec_write_charger(int value)
{
#if CONFIG_CPU == AS3525
ascodec_write(AS3514_CHARGER, value);
#else
ascodec_write_pmu(AS3543_CHARGER, 1, value);
#endif
}
void ascodec_enable_endofch_irq(void);
void ascodec_disable_endofch_irq(void);
static inline int ascodec_read_charger(void)
{
#if CONFIG_CPU == AS3525
return ascodec_read(AS3514_CHARGER);
#else
return ascodec_read_pmu(AS3543_CHARGER, 1);
#endif
}
#endif /* !SIMULATOR */

View file

@ -41,7 +41,7 @@ void power_init(void)
#if CONFIG_CHARGING
unsigned int power_input_status(void)
{
return (ascodec_read(AS3514_IRQ_ENRD0) & (1<<5)) ?
return ascodec_chg_status() ?
POWER_INPUT_MAIN_CHARGER : POWER_INPUT_NONE;
/* TODO: Handle USB and other sources properly */

View file

@ -29,13 +29,7 @@
#include "power.h"
#include "as3525.h"
#if CONFIG_CPU == AS3525
static int usb_status = USB_EXTRACTED;
#else
#if defined(SANSA_CLIPV2)
#define USB_DETECT_PIN 6
#endif
#endif
void usb_enable(bool on)
{
@ -51,12 +45,8 @@ void usb_enable(bool on)
void usb_init_device(void)
{
#ifdef USB_DETECT_PIN
GPIOA_DIR &= ~(1 << USB_DETECT_PIN); /* set as input */
#endif
}
#if CONFIG_CPU == AS3525
void usb_insert_int(void)
{
usb_status = USB_INSERTED;
@ -71,14 +61,3 @@ int usb_detect(void)
{
return usb_status;
}
#else
int usb_detect(void)
{
#ifdef USB_DETECT_PIN
if (GPIOA_PIN( USB_DETECT_PIN ))
return USB_INSERTED;
else
#endif
return USB_EXTRACTED;
}
#endif

View file

@ -23,9 +23,7 @@
void usb_init_device(void);
int usb_detect(void);
#if CONFIG_CPU == AS3525
void usb_insert_int(void);
void usb_remove_int(void);
#endif /* CONFIG_CPU == AS3525 */
#endif /* USB_TARGET_H */