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 #include -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 */ }