Mike Montour wrote:

I'll try to send an updated u-boot patch in the next day or two.

I've attached a patch with my most recent changes, and there is a corresponding GTA02v5 binary at http://members.shaw.ca/mmontour/neo/u-boot-mm-2008083001.udfu for anyone who wants to try it out (at his/her own risk). There might be problems on pre-production GTA02v5s that don't have the LED-current-limiting rework.

This version flashes the Aux LED once at startup and then checks the battery level. If the battery is very low it gives three quick flashes on the Aux LED and then shuts off. If the battery level is above this threshold but still below 3.4V it loops for about 10s waiting to detect >= 500mA USB power, flashing the Aux LED at a slower rate. Pressing the Aux button will bypass these checks.

When the device is turned off cleanly (through the neo1973_poweroff() function), the PMU is programmed not to wake on USB and to limit the battery fast-charge current to 100mA. This probably violates the USB battery charging specification, but it is necessary (at least on my hardware revision) to recover from the case of a fully-discharged battery whose internal cutoff has triggered.

This patch is still a work in progress (probably not suitable for merging into the stable tree), but I'm going to take a break from it for a while and look more at Qi and the Linux PCF50633 driver.
diff --git a/board/neo1973/common/udc.c b/board/neo1973/common/udc.c
index d547cb8..fe146ee 100644
--- a/board/neo1973/common/udc.c
+++ b/board/neo1973/common/udc.c
@@ -5,7 +5,7 @@
 #include <pcf50606.h>
 #include <pcf50633.h>
 
-int udc_usb_maxcurrent = 0;
+int udc_usb_maxcurrent = 100;
 
 void udc_ctrl(enum usbd_event event, int param)
 {
@@ -39,7 +39,7 @@ void udc_ctrl(enum usbd_event event, int param)
                if (param)
                        udc_usb_maxcurrent = 500;
                else
-                       udc_usb_maxcurrent = 0;
+                       udc_usb_maxcurrent = 100;
 #endif
                break;
        default:
diff --git a/board/neo1973/gta02/gta02.c b/board/neo1973/gta02/gta02.c
index 6db164e..d396237 100644
--- a/board/neo1973/gta02/gta02.c
+++ b/board/neo1973/gta02/gta02.c
@@ -47,7 +47,9 @@ DECLARE_GLOBAL_DATA_PTR;
 #define POWER_KEY_SECONDS      1
 
 /* If the battery voltage is below this, we can't provide stable power */
-#define        SAFE_POWER_MILLIVOLT    3400
+#define LOWPOWER_SAFE_MILLIVOLT        3400
+/* A lower threshold that should be OK when we're on >=500mA USB */ 
+#define        HIGHPOWER_SAFE_MILLIVOLT 3100
 
 #if defined(CONFIG_ARCH_GTA02_v1)
 //#define M_MDIV       0x7f            /* Fout = 405.00MHz */
@@ -74,7 +76,6 @@ unsigned int neo1973_wakeup_cause;
 extern unsigned char booted_from_nand;
 extern unsigned char booted_from_nor;
 extern int nobootdelay;
-extern int udc_usb_maxcurrent;
 
 char __cfg_prompt[20] = "GTA02vXX # ";
 
@@ -320,12 +321,6 @@ static void set_revision(void)
 #endif
 }
 
-static void poll_charger(void)
-{
-       if (pcf50633_read_charger_type() == 1000)
-               pcf50633_usb_maxcurrent(1000);
-}
-
 static int have_int(uint8_t mask1, uint8_t mask2);
 
 static void clear_pmu_int(void)
@@ -376,69 +371,93 @@ static void cpu_idle(void)
        local_irq_restore(flags);
 }
 
-static int battery_is_good(void)
+static int battery_level()
 {
-       /* battery is absent -> don't boot */
-       if (pcf50633_reg_read(PCF50633_REG_BVMCTL) & 1)
-               return 0;
-
-       /* we could try to boot, but we'll probably die on the way */
-       if (pcf50633_read_battvolt() < SAFE_POWER_MILLIVOLT)
-               return 0;
-
-       return 1;
+       /* Measure the battery level after a short LED cycle with the
+        * charger disabled (so that we correctly detect 0V in the case
+        * where no battery is present 
+        */
+       int level = 0;
+       
+       neo1973_led(GTA02_LED_AUX_RED, 1);
+       pcf50633_reg_set_bit_mask(PCF50633_REG_MBCC1, 1, 0);
+       udelay(200000);
+       
+       if (!(pcf50633_reg_read(PCF50633_REG_BVMCTL) & 1))
+       {
+               /* Only measure voltage if there's a "present" flag */
+               level = pcf50633_read_battvolt();
+       }
+       neo1973_led(GTA02_LED_AUX_RED, 0);
+       pcf50633_reg_set_bit_mask(PCF50633_REG_MBCC1, 1, 1);
+       
+       return level;
 }
 
-static void wait_for_power(void)
+static int wait_for_power(void)
 {
        int seconds = 0;
        int led_cycle = 1;
 
+       int was_low = 0;
+       
+       int bat = battery_level();
+       if(bat < HIGHPOWER_SAFE_MILLIVOLT && !neo1973_aux_key_pressed())
+       {
+               /* Low-battery shutdown, flash the LED and then shut down. 
+                * PCF50633 will charge the battery at 100mA in the off state.
+                */
+               int i;
+               for(i=1; i<7; i++)
+               {
+                       udelay(100000);
+                       neo1973_led(GTA02_LED_AUX_RED, i % 2);
+               }
+               neo1973_poweroff();
+       }
+       
+       /* Wait until there's high USB power or enough battery charge that 
+        * we can boot with 100mA USB. User can override this delay by
+        * pressing Aux. 
+        */
+       int loops = 0;
        while (1) {
-               poll_charger();
-
-               /* track what the time-critical udc callback allows us */
-               if (pcf50633_usb_last_maxcurrent != udc_usb_maxcurrent)
-                       pcf50633_usb_maxcurrent(udc_usb_maxcurrent);
-
-               /* we have plenty of external power -> try to boot */
-               if (pcf50633_usb_last_maxcurrent >= 500)
+               /* Clear PMU interrupts */
+               /* Should check for high-temperature etc. and shut down */
+               
+               pcf50633_reg_read(PCF50633_REG_INT1);
+               pcf50633_reg_read(PCF50633_REG_INT2);
+               pcf50633_reg_read(PCF50633_REG_INT3);
+               pcf50633_reg_read(PCF50633_REG_INT4);
+               pcf50633_reg_read(PCF50633_REG_INT5);
+               
+               int new_max = pcf50633_read_charger_type();
+
+               if(pcf50633_usb_last_maxcurrent != new_max)
+                       pcf50633_usb_maxcurrent(new_max);
+
+               if(bat >= LOWPOWER_SAFE_MILLIVOLT ||
+                        pcf50633_usb_last_maxcurrent >= 500 ||
+                       neo1973_aux_key_pressed()) {
                        break;
-
-               /* cpu_idle sits with interrupts off destroying USB operation
-                * don't run it unless we are in trouble
-                */
-               if (!battery_is_good())
-                       cpu_idle();
-               else
-                       udelay(1000000);
-
-               if (neo1973_new_second()) {
-                       /*
-                        * Probe the battery only if the current LED cycle is
-                        * about to end, so that it had time to discharge.
+               } else if(pcf50633_usb_last_maxcurrent == 0 ||
+                       bat < HIGHPOWER_SAFE_MILLIVOLT ||
+                       loops > 10) {
+                       /* Give up and shut down if the battery gets
+                        * too low, if there's no USB power, or if we're
+                        * still on 100mA USB after 10 loops (the device
+                        * will continue to charge at 100mA in the off state)
                         */
-                       if (led_cycle && battery_is_good())
-                               break;
-                       seconds++;
+                       neo1973_poweroff();
+               } else {
+                       was_low = 1; /* Will force u-boot to enter the menu */
+                       udelay(800000);
                }
-
-               led_cycle = !seconds || (seconds & 1);
-
-               /*
-                * Blink the AUX LED, unless it's broken (which is the case in
-                * GTA02v5 it is) and draws excessive current, which we just
-                * can't afford in this delicate situation.
-                */
-               if (gta02_revision > 5)
-                       neo1973_led(GTA02_LED_AUX_RED, led_cycle);
-
-               /* alternate LED and charger cycles */
-               pcf50633_reg_set_bit_mask(PCF50633_REG_MBCC1, 1, !led_cycle);
+               bat = battery_level();
+               loops++;
        }
-
-       /* switch off the AUX LED */
-       neo1973_led(GTA02_LED_AUX_RED, 0);
+       
+       return was_low;
 }
 
 static void pcf50633_late_init(void)
@@ -456,6 +475,7 @@ static void pcf50633_late_init(void)
        pcf50633_reg_write(PCF50633_REG_LDO5ENA, recent);
        pcf50633_reg_write(PCF50633_REG_LDO6ENA, recent);
 
+       pcf50633_reg_write(PCF50633_REG_OOCWAKE, 0xd3); /* wake from 
ONKEY,EXTON!,RTC,USB,ADP */
        pcf50633_reg_write(PCF50633_REG_MBCC5, 0xff); /* 1A USB fast charge */
 
        pcf50633_reg_set_bit_mask(PCF50633_REG_MBCC1, 1, 1); /* charge ! */
@@ -469,6 +489,7 @@ int board_late_init(void)
        int menu_vote = 0; /* <= 0: no, > 0: yes */
        int seconds = 0;
        int enter_bootmenu;
+       int was_lowbat;
        char *env_stop_in_menu;
 
        set_revision();
@@ -479,9 +500,18 @@ int board_late_init(void)
        /* obtain wake-up reason */
        int1 = pcf50633_reg_read(PCF50633_REG_INT1);
        int2 = pcf50633_reg_read(PCF50633_REG_INT2);
+       
+       if ((int1 & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) &&
+        !(int2 & (PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF))){
+               /* we've been woken up by charger event without ONKEY,
+                * Go back to the off state and wait for ONKEY
+                */
+               neo1973_poweroff();
+       }
 
-       wait_for_power();
+       was_lowbat = wait_for_power();
        pcf50633_late_init();
+       
        cpu_speed(M_MDIV, M_PDIV, M_SDIV, 5); /* 400MHZ, 1:4:8 */
 
        /* issue a short pulse with the vibrator */
@@ -512,6 +542,9 @@ int board_late_init(void)
        sprintf(buf, "0x%02x", int2);
        setenv("pcf50633_int2", buf);
 
+       if (was_lowbat)
+               goto continue_boot;
+
        if (int1 & PCF50633_INT1_ALARM) {
                /* we've been woken up by RTC alarm, boot */
                neo1973_wakeup_cause = NEO1973_WAKEUP_ALARM;
@@ -557,7 +590,7 @@ continue_boot:
        if (env_stop_in_menu && strcmp(env_stop_in_menu, "yes") == 0)
                menu_vote = 1;
 
-       enter_bootmenu = menu_vote > 0 || booted_from_nor;
+       enter_bootmenu = menu_vote > 0 || booted_from_nor || was_lowbat;
        glamo_core_init();
        smedia3362_lcm_reset(1);
        if (!enter_bootmenu && getenv("splashimage"))
@@ -608,6 +641,19 @@ void neo1973_poweroff(void)
 {
        printf("poweroff\n");
        udc_disconnect();
+       
+       /* Set PMU for safe charging in standby */
+       pcf50633_reg_set_bit_mask(PCF50633_REG_MBCC1, 1, 1); /* charge ! */
+       pcf50633_reg_write(PCF50633_REG_MBCC5, 0x19);   /* 25/255 == 98mA 
soft-start usb fast */
+       pcf50633_reg_write(PCF50633_REG_OOCWAKE, 0x11); /* wake from 
ONKEY,EXTON!,RTC,USB,ADP */
+       
+       /* Clear interrupts */
+       pcf50633_reg_read(PCF50633_REG_INT1);
+       pcf50633_reg_read(PCF50633_REG_INT2);
+       pcf50633_reg_read(PCF50633_REG_INT3);
+       pcf50633_reg_read(PCF50633_REG_INT4);
+       pcf50633_reg_read(PCF50633_REG_INT5);
+       
        pcf50633_reg_write(PCF50633_REG_OOCSHDWN, 0x01);
        /* don't return to caller */
        while (1) ;
diff --git a/board/neo1973/gta02/pcf50633.c b/board/neo1973/gta02/pcf50633.c
index 69d7fb0..7704d15 100644
--- a/board/neo1973/gta02/pcf50633.c
+++ b/board/neo1973/gta02/pcf50633.c
@@ -14,11 +14,11 @@ const u_int8_t pcf50633_initial_regs[__NUM_PCF50633_REGS] = 
{
        [PCF50633_REG_INT4M]    = 0x00,
        [PCF50633_REG_INT5M]    = 0x00,
 
-       [PCF50633_REG_OOCWAKE]  = 0xd3, /* wake from ONKEY,EXTON!,RTC,USB,ADP */
+       [PCF50633_REG_OOCWAKE]  = 0x11, /* ONKEY and RTC only */
        [PCF50633_REG_OOCTIM1]  = 0xaa, /* debounce 14ms everything */
        [PCF50633_REG_OOCTIM2]  = 0x4a,
        [PCF50633_REG_OOCMODE]  = 0x55,
-       [PCF50633_REG_OOCCTL]   = 0x47,
+       [PCF50633_REG_OOCCTL]   = 0x47, 
 
        [PCF50633_REG_GPIOCTL]  = 0x01, /* only GPIO1 is input */
        [PCF50633_REG_GPIO2CFG] = 0x00,
diff --git a/drivers/misc/pcf50633.c b/drivers/misc/pcf50633.c
index fa7f8e4..9cfa3c7 100644
--- a/drivers/misc/pcf50633.c
+++ b/drivers/misc/pcf50633.c
@@ -13,6 +13,8 @@
 
 #define PCF50633_I2C_ADDR              0x73
 
+extern int udc_usb_maxcurrent;
+
 void __pcf50633_reg_write(u_int8_t reg, u_int8_t val)
 {
        i2c_write(PCF50633_I2C_ADDR, reg, 1, &val, 1);
@@ -152,8 +154,10 @@ int pcf50633_read_charger_type(void)
        if (ret < ((ADC_NOMINAL_RES_1A + ADC_NOMINAL_RES_NC_R_USB) / 2))
                return 1000;
 
-       /* there is no resistor, so it must be USB pwr */
-       return 100; /* USB power then */
+       /* there is no resistor, so it must be USB pwr 
+        * Return the value set by the udc callback
+        */
+       return udc_usb_maxcurrent; /* USB power then */
 
 }
 

Reply via email to