From 98bd2231eca3e6cb015fa58877d648857cb816e6 Mon Sep 17 00:00:00 2001 From: Cástor Muñoz Date: Sun, 22 May 2016 02:33:58 +0200 Subject: iPod Classic: introduce PMU interrupts PMU interrupts are used to detect USB Vbus, wall adaptor, accessories and holdswitch. A thread is needed to poll the PMU throught I2C, ATM it does nothing but showing the state of the inputs on the HW debug menu, funcionallity for each individual input will be added in next patches. Change-Id: If93bf2044d1052729237a7fd1431c8493e09f1c7 --- firmware/export/pcf5063x.h | 11 ++ firmware/target/arm/s5l8702/debug-s5l8702.c | 29 ++-- firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c | 162 +++++++++++++++++++++- firmware/target/arm/s5l8702/ipod6g/pmu-target.h | 25 +++- firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c | 2 + firmware/target/arm/s5l8702/system-s5l8702.c | 1 - 6 files changed, 214 insertions(+), 16 deletions(-) diff --git a/firmware/export/pcf5063x.h b/firmware/export/pcf5063x.h index 164417f483..f5f177ace7 100644 --- a/firmware/export/pcf5063x.h +++ b/firmware/export/pcf5063x.h @@ -159,6 +159,17 @@ enum pcf5063X_reg_oocwake { PCF5063X_OOCWAKE_ADP = 0x80, }; +enum pcf5063X_reg_oocstat { + PCF5063X_OOCSTAT_ONKEY = 0x01, + PCF5063X_OOCSTAT_EXTON1 = 0x02, + PCF5063X_OOCSTAT_EXTON2 = 0x04, + PCF5063X_OOCSTAT_EXTON3 = 0x08, + PCF5063X_OOCSTAT_BUBPRES = 0x10, + PCF5063X_OOCSTAT_SYSOK = 0x20, + PCF5063X_OOCSTAT_BATOK = 0x40, + PCF5063X_OOCSTAT_TMPOK = 0x80, +}; + enum pcf5063X_reg_mbcc1 { PCF5063X_MBCC1_CHGENA = 0x01, /* Charger enable */ PCF5063X_MBCC1_AUTOSTOP = 0x02, diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c index 614019bac1..48a20a97c2 100644 --- a/firmware/target/arm/s5l8702/debug-s5l8702.c +++ b/firmware/target/arm/s5l8702/debug-s5l8702.c @@ -102,14 +102,14 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("PMU:"); for(i=0;i<7;i++) { - char *device[] = {"(unknown)", - "(unknown)", - "(unknown)", - "(unknown)", - "(unknown)", - "(unknown)", - "(unknown)"}; - _DEBUG_PRINTF("ldo%d %s: %dmV %s",i, + char *device[] = {"unknown", + "unknown", + "LCD", + "AUDIO", + "unknown", + "CLICKWHEEL", + "ACCESSORY"}; + _DEBUG_PRINTF("ldo%d %s: %dmV (%s)",i, pmu_read(0x2e + (i << 1))?" on":"off", 900 + pmu_read(0x2d + (i << 1))*100, device[i]); @@ -120,6 +120,19 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("charging: %s", charging_state() ? "true" : "false"); _DEBUG_PRINTF("backlight: %s", pmu_read(0x29) ? "on" : "off"); _DEBUG_PRINTF("brightness value: %d", pmu_read(0x28)); + line++; + _DEBUG_PRINTF("USB present: %s", + pmu_usb_present() ? "true" : "false"); +#if CONFIG_CHARGING + _DEBUG_PRINTF("FW present: %s", + pmu_firewire_present() ? "true" : "false"); +#endif + _DEBUG_PRINTF("holdswitch locked: %s", + pmu_holdswitch_locked() ? "true" : "false"); +#ifdef IPOD_ACCESSORY_PROTOCOL + _DEBUG_PRINTF("accessory present: %s", + pmu_accessory_present() ? "true" : "false"); +#endif } #ifdef UC870X_DEBUG else if(state==2) diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c index 920c93ad5d..4200308861 100644 --- a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c @@ -21,8 +21,12 @@ #include "config.h" #include "kernel.h" -#include "i2c-s5l8702.h" +#include "thread.h" + #include "pmu-target.h" +#include "i2c-s5l8702.h" +#include "gpio-s5l8702.h" + static struct mutex pmu_adc_mutex; @@ -50,11 +54,6 @@ int pmu_write(int address, unsigned char val) return pmu_write_multiple(address, 1, &val); } -void pmu_init(void) -{ - mutex_init(&pmu_adc_mutex); -} - int pmu_read_adc(unsigned int adc) { int data = 0; @@ -143,6 +142,157 @@ void pmu_write_rtc(unsigned char* buffer) pmu_write_multiple(0x59, 7, buffer); } +/* + * eINT + */ +#define Q_EINT 0 + +static char pmu_thread_stack[DEFAULT_STACK_SIZE/4]; +static struct event_queue pmu_queue; +static unsigned char ints_msk[6]; + +static void pmu_eint_isr(struct eint_handler*); + +static struct eint_handler pmu_eint = { + .gpio_n = GPIO_EINT_PMU, + .type = EIC_INTTYPE_LEVEL, + .level = EIC_INTLEVEL_LOW, + .isr = pmu_eint_isr, +}; + +static int pmu_input_holdswitch; +static int pmu_input_usb; + +int pmu_holdswitch_locked(void) +{ + return pmu_input_holdswitch; +} + +int pmu_usb_present(void) +{ + return pmu_input_usb; +} + +#ifdef IPOD_ACCESSORY_PROTOCOL +static int pmu_input_accessory; + +int pmu_accessory_present(void) +{ + return pmu_input_accessory; +} +#endif + +#if CONFIG_CHARGING +static int pmu_input_firewire; + +int pmu_firewire_present(void) +{ + return pmu_input_firewire; +} + +static void pmu_read_inputs_mbcs(void) +{ + pmu_input_firewire = !!(pmu_read(PCF5063X_REG_MBCS1) + & PCF5063X_MBCS1_ADAPTPRES); +} +#endif + +static void pmu_read_inputs_gpio(void) +{ + pmu_input_holdswitch = !(pmu_read(PCF50635_REG_GPIOSTAT) + & PCF50635_GPIOSTAT_GPIO2); +} + +static void pmu_read_inputs_ooc(void) +{ + unsigned char oocstat = pmu_read(PCF5063X_REG_OOCSTAT); + pmu_input_usb = !!(oocstat & PCF5063X_OOCSTAT_EXTON2); +#ifdef IPOD_ACCESSORY_PROTOCOL + pmu_input_accessory = !(oocstat & PCF5063X_OOCSTAT_EXTON3); +#endif +} + +static void pmu_eint_isr(struct eint_handler *h) +{ + eint_unregister(h); + queue_post(&pmu_queue, Q_EINT, 0); +} + +static void NORETURN_ATTR pmu_thread(void) +{ + struct queue_event ev; + unsigned char ints[6]; + + while (true) + { + queue_wait_w_tmo(&pmu_queue, &ev, TIMEOUT_BLOCK); + switch (ev.id) + { + case Q_EINT: + /* read (clear) PMU interrupts, this will also + raise the PMU IRQ pin */ + pmu_read_multiple(PCF5063X_REG_INT1, 2, ints); + ints[5] = pmu_read(PCF50635_REG_INT6); + +#if CONFIG_CHARGING + if (ints[0] & ~ints_msk[0]) pmu_read_inputs_mbcs(); +#endif + if (ints[1] & ~ints_msk[1]) pmu_read_inputs_ooc(); + if (ints[5] & ~ints_msk[5]) pmu_read_inputs_gpio(); + + eint_register(&pmu_eint); + break; + + case SYS_TIMEOUT: + break; + } + } +} + +/* main init */ +void pmu_init(void) +{ + mutex_init(&pmu_adc_mutex); + queue_init(&pmu_queue, false); + + create_thread(pmu_thread, + pmu_thread_stack, sizeof(pmu_thread_stack), 0, + "PMU" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU)); + + /* configure PMU interrutps */ + for (int i = 0; i < 6; i++) + ints_msk[i] = 0xff; + +#if CONFIG_CHARGING + ints_msk[0] &= ~PCF5063X_INT1_ADPINS & /* FireWire */ + ~PCF5063X_INT1_ADPREM; +#endif + ints_msk[1] &= ~PCF5063X_INT2_EXTON2R & /* USB */ + ~PCF5063X_INT2_EXTON2F; +#ifdef IPOD_ACCESSORY_PROTOCOL + ints_msk[1] &= ~PCF5063X_INT2_EXTON3R & /* Accessory */ + ~PCF5063X_INT2_EXTON3F; +#endif + ints_msk[5] &= ~PCF50635_INT6_GPIO2; /* Holdswitch */ + + pmu_write_multiple(PCF5063X_REG_INT1M, 5, ints_msk); + pmu_write(PCF50635_REG_INT6M, ints_msk[5]); + + /* clear all */ + unsigned char ints[5]; + pmu_read_multiple(PCF5063X_REG_INT1, 5, ints); + pmu_read(PCF50635_REG_INT6); + + /* get initial values */ +#if CONFIG_CHARGING + pmu_read_inputs_mbcs(); +#endif + pmu_read_inputs_ooc(); + pmu_read_inputs_gpio(); + + eint_register(&pmu_eint); +} + /* * preinit */ diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-target.h b/firmware/target/arm/s5l8702/ipod6g/pmu-target.h index d090f72a67..2274b4061f 100644 --- a/firmware/target/arm/s5l8702/ipod6g/pmu-target.h +++ b/firmware/target/arm/s5l8702/ipod6g/pmu-target.h @@ -30,7 +30,21 @@ /* undocummented PMU registers */ #define PCF50635_REG_INT6 0x85 #define PCF50635_REG_INT6M 0x86 -#define PCF50635_REG_GPIOSTAT 0x87 /* bit1: GPIO2 status (TBC) */ +#define PCF50635_REG_GPIOSTAT 0x87 + +enum pcf50635_reg_int6 { + PCF50635_INT6_GPIO1 = 0x01, /* TBC */ + PCF50635_INT6_GPIO2 = 0x02, +}; + +enum pcf50635_reg_gpiostat { + PCF50635_GPIOSTAT_GPIO1 = 0x01, /* TBC */ + PCF50635_GPIOSTAT_GPIO2 = 0x02, +}; + + +/* GPIO for external PMU interrupt */ +#define GPIO_EINT_PMU 0x7b /* LDOs */ #define LDO_UNK1 1 /* TBC: SoC voltage (USB) */ @@ -77,6 +91,15 @@ void pmu_read_rtc(unsigned char* buffer); void pmu_write_rtc(unsigned char* buffer); void pmu_hdd_power(bool on); +int pmu_holdswitch_locked(void); +int pmu_usb_present(void); +#if CONFIG_CHARGING +int pmu_firewire_present(void); +#endif +#ifdef IPOD_ACCESSORY_PROTOCOL +int pmu_accessory_present(void); +#endif + void pmu_preinit(void); #ifdef BOOTLOADER unsigned char pmu_rd(int address); diff --git a/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c index 57358b8cb6..d3224d2a66 100644 --- a/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/power-ipod6g.c @@ -41,6 +41,8 @@ void power_off(void) void power_init(void) { + pmu_init(); + idepowered = false; /* DOWN1CTL: CPU DVM step time = 30us (default: no DVM) */ diff --git a/firmware/target/arm/s5l8702/system-s5l8702.c b/firmware/target/arm/s5l8702/system-s5l8702.c index cbe8bfcaf7..459f4c36c6 100644 --- a/firmware/target/arm/s5l8702/system-s5l8702.c +++ b/firmware/target/arm/s5l8702/system-s5l8702.c @@ -207,7 +207,6 @@ void system_init(void) #endif gpio_init(); eint_init(); - pmu_init(); dma_init(); #ifdef HAVE_SERIAL uart_init(); -- cgit