From adbd2969e6e6fd584d46ef60a3fa40bf878d7e00 Mon Sep 17 00:00:00 2001 From: Cástor Muñoz Date: Fri, 12 Aug 2016 02:37:45 +0200 Subject: iPod Classic: ADC updates Add code to read USB D+/D- and accessory ADCs, it is shown in HW debug menu, might be useful in future for RB and/or the bootloader to identify external USB chargers. Change-Id: Ia48ca5e06bb7ddc52bb55abedde6734653ce8dba --- firmware/export/pcf5063x.h | 8 +- firmware/target/arm/s5l8702/debug-s5l8702.c | 12 ++- firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c | 84 +++++++++++++++- firmware/target/arm/s5l8702/ipod6g/adc-target.h | 20 +++- firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c | 108 ++++++++++++++------- firmware/target/arm/s5l8702/ipod6g/pmu-target.h | 15 ++- .../target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c | 11 +-- 7 files changed, 201 insertions(+), 57 deletions(-) diff --git a/firmware/export/pcf5063x.h b/firmware/export/pcf5063x.h index f5f177ace7..2751b67b57 100644 --- a/firmware/export/pcf5063x.h +++ b/firmware/export/pcf5063x.h @@ -198,12 +198,12 @@ enum pcf5063X_reg_mbcc2 { enum pcf5063X_reg_adcc1 { PCF5063X_ADCC1_ADCSTART = 0x01, - PCF5063X_ADCC1_RES_10BIT = 0x02, + PCF5063X_ADCC1_RES_10BIT = 0x00, + PCF5063X_ADCC1_RES_8BIT = 0x02, PCF5063X_ADCC1_AVERAGE_NO = 0x00, PCF5063X_ADCC1_AVERAGE_4 = 0x04, PCF5063X_ADCC1_AVERAGE_8 = 0x08, PCF5063X_ADCC1_AVERAGE_16 = 0x0c, - PCF5063X_ADCC1_MUX_BATSNS_RES = 0x00, PCF5063X_ADCC1_MUX_BATSNS_SUBTR = 0x10, PCF5063X_ADCC1_MUX_ADCIN2_RES = 0x20, @@ -211,6 +211,7 @@ enum pcf5063X_reg_adcc1 { PCF5063X_ADCC1_MUX_BATTEMP = 0x60, PCF5063X_ADCC1_MUX_ADCIN1 = 0x70, }; +#define PCF5063X_ADCC1_RES_MASK 0x02 #define PCF5063X_ADCC1_AVERAGE_MASK 0x0c #define PCF5063X_ADCC1_ADCMUX_MASK 0xf0 @@ -219,9 +220,11 @@ enum pcf5063X_reg_adcc2 { PCF5063X_ADCC2_RATIO_BATTEMP = 0x01, PCF5063X_ADCC2_RATIO_ADCIN1 = 0x02, PCF5063X_ADCC2_RATIO_BOTH = 0x03, + PCF5063X_ADCC2_RATIOSETTL_10US = 0x00, PCF5063X_ADCC2_RATIOSETTL_100US = 0x04, }; #define PCF5063X_ADCC2_RATIO_MASK 0x03 +#define PCF5063X_ADCC2_RATIOSETTL_MASK 0x04 enum pcf5063X_reg_adcc3 { PCF5063X_ADCC3_ACCSW_EN = 0x01, @@ -229,6 +232,7 @@ enum pcf5063X_reg_adcc3 { PCF5063X_ADCC3_RES_DIV_TWO = 0x10, PCF5063X_ADCC3_RES_DIV_THREE = 0x00, }; +#define PCF5063X_ADCC3_RES_DIV_MASK 0x10 enum pcf5063X_reg_adcs3 { PCF5063X_ADCS3_REF_NTCSW = 0x00, diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c index a61a728d43..1bf59e94fe 100644 --- a/firmware/target/arm/s5l8702/debug-s5l8702.c +++ b/firmware/target/arm/s5l8702/debug-s5l8702.c @@ -19,6 +19,7 @@ * ****************************************************************************/ +#include #include #include "system.h" #include "config.h" @@ -28,6 +29,7 @@ #include "font.h" #include "storage.h" #include "power.h" +#include "adc.h" #include "pmu-target.h" #include "pcm-target.h" #ifdef HAVE_SERIAL @@ -133,12 +135,20 @@ bool dbg_hw_info(void) _DEBUG_PRINTF("accessory present: %s", pmu_accessory_present() ? "true" : "false"); #endif + line++; + _DEBUG_PRINTF("ADC:"); + _DEBUG_PRINTF("%s: %d mV", adc_name(ADC_BATTERY), + adc_read_battery_voltage()); + _DEBUG_PRINTF("%s: %d Ohms", adc_name(ADC_ACCESSORY), + adc_read_accessory_resistor()); + _DEBUG_PRINTF("USB D+: %d mV", adc_read_usbdata_voltage(true)); + _DEBUG_PRINTF("USB D-: %d mV", adc_read_usbdata_voltage(false)); line++; extern unsigned long i2c_rd_err, i2c_wr_err; _DEBUG_PRINTF("i2c rd/wr errors: %lu/%lu", i2c_rd_err, i2c_wr_err); } #ifdef UC870X_DEBUG - else if(state==2) + else if(state==(max_states-1)) { extern struct uartc_port ser_port; bool opened = !!ser_port.uartc->port_l[ser_port.id]; diff --git a/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c index 201af5ee00..fbe5ef805e 100644 --- a/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/adc-ipod6g.c @@ -18,7 +18,7 @@ * KIND, either express or implied. * ****************************************************************************/ - + #include "config.h" #include "inttypes.h" @@ -28,12 +28,90 @@ #include "pmu-target.h" #include "kernel.h" + +/* MS_TO_TICKS converts a milisecond time period into the + * corresponding amount of ticks. If the time period cannot + * be accurately measured in ticks it will round up. + */ +#define MS_PER_HZ (1000/HZ) +#define MS_TO_TICKS(x) (((x)+MS_PER_HZ-1)/MS_PER_HZ) + +static const struct pmu_adc_channel adc_channels[] = +{ + [ADC_BATTERY] = + { + .name = "Battery", + .adcc1 = PCF5063X_ADCC1_MUX_BATSNS_SUBTR + | PCF5063X_ADCC1_AVERAGE_4 + | PCF5063X_ADCC1_RES_10BIT, + }, + + [ADC_USBDATA] = + { + .name = "USB D+/D-", + .adcc1 = PCF5063X_ADCC1_MUX_ADCIN2_RES + | PCF5063X_ADCC1_AVERAGE_16 + | PCF5063X_ADCC1_RES_10BIT, + .adcc3 = PCF5063X_ADCC3_RES_DIV_THREE, + }, + + [ADC_ACCESSORY] = + { + .name = "Accessory", + .adcc1 = PCF5063X_ADCC1_MUX_ADCIN1 + | PCF5063X_ADCC1_AVERAGE_16 + | PCF5063X_ADCC1_RES_10BIT, + .adcc2 = PCF5063X_ADCC2_RATIO_ADCIN1 + | PCF5063X_ADCC2_RATIOSETTL_10US, + .adcc3 = PCF5063X_ADCC3_ACCSW_EN, + .bias_dly = MS_TO_TICKS(50), + }, +}; + +const char *adc_name(int channel) +{ + return adc_channels[channel].name; +} + +unsigned short adc_read_millivolts(int channel) +{ + const struct pmu_adc_channel *ch = &adc_channels[channel]; + return pmu_adc_raw2mv(ch, pmu_read_adc(ch)); +} + +/* Returns battery voltage [millivolts] */ +unsigned int adc_read_battery_voltage(void) +{ + return adc_read_millivolts(ADC_BATTERY); +} + +/* Returns USB D+/D- voltage from ADC [millivolts] */ +unsigned int adc_read_usbdata_voltage(bool dp) +{ + unsigned int mvolts; + int gpio = dp ? 0xb0300 : 0xb0200; /* select D+/D- */ + GPIOCMD = gpio | 0xf; /* route to ADCIN2 */ + mvolts = adc_read_millivolts(ADC_USBDATA); + GPIOCMD = gpio | 0xe; /* unroute */ + return mvolts; +} + +/* Returns resistor connected to "Accessory identify" pin [ohms] */ +#define IAP_DEVICE_RESISTOR 100000 /* ohms */ +int adc_read_accessory_resistor(void) +{ + int raw = pmu_read_adc(&adc_channels[ADC_ACCESSORY]); + return (1023-raw) ? raw * IAP_DEVICE_RESISTOR / (1023-raw) + : -1 /* open circuit */; +} + + +/* API functions */ unsigned short adc_read(int channel) { - return pmu_read_adc(channel); + return pmu_read_adc(&adc_channels[channel]); } void adc_init(void) { } - diff --git a/firmware/target/arm/s5l8702/ipod6g/adc-target.h b/firmware/target/arm/s5l8702/ipod6g/adc-target.h index d4dce3d31f..bedc0a8447 100644 --- a/firmware/target/arm/s5l8702/ipod6g/adc-target.h +++ b/firmware/target/arm/s5l8702/ipod6g/adc-target.h @@ -21,13 +21,23 @@ #ifndef _ADC_TARGET_H_ #define _ADC_TARGET_H_ -#define NUM_ADC_CHANNELS 4 +#include +#include "config.h" -#define ADC_UNKNOWN_0 0 -#define ADC_UNKNOWN_1 1 -#define ADC_BATTERY 2 -#define ADC_UNKNOWN_3 3 +enum +{ + ADC_BATTERY = 0, + ADC_USBDATA, + ADC_ACCESSORY, + NUM_ADC_CHANNELS +}; #define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */ +unsigned short adc_read_millivolts(int channel); +unsigned int adc_read_battery_voltage(void); +unsigned int adc_read_usbdata_voltage(bool dp); +int adc_read_accessory_resistor(void); +const char *adc_name(int channel); + #endif diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c index c439a30fc1..d282a48d5b 100644 --- a/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/pmu-ipod6g.c @@ -24,12 +24,11 @@ #include "thread.h" #include "pmu-target.h" +#include "adc-target.h" #include "i2c-s5l8702.h" #include "gpio-s5l8702.h" -static struct mutex pmu_adc_mutex; - int pmu_read_multiple(int address, int count, unsigned char* buffer) { return i2c_read(0, 0xe6, address, count, buffer); @@ -54,35 +53,6 @@ int pmu_write(int address, unsigned char val) return pmu_write_multiple(address, 1, &val); } -int pmu_read_adc(unsigned int adc) -{ - int data = 0; - mutex_lock(&pmu_adc_mutex); - pmu_write(0x54, 5 | (adc << 4)); - while ((data & 0x80) == 0) - { - yield(); - data = pmu_read(0x57); - } - int value = (pmu_read(0x55) << 2) | (data & 3); - mutex_unlock(&pmu_adc_mutex); - return value; -} - -/* millivolts */ -int pmu_read_battery_voltage(void) -{ - return (pmu_read_adc(1) * 2000 / 1023) + 2250; -} - -/* milliamps */ -int pmu_read_battery_current(void) -{ -//TODO: Figure out how to read the battery current -// return pmu_read_adc(2); - return 0; -} - void pmu_ldo_on_in_standby(unsigned int ldo, int onoff) { if (ldo < 4) @@ -142,18 +112,87 @@ void pmu_write_rtc(unsigned char* buffer) pmu_write_multiple(0x59, 7, buffer); } +/* + * ADC + */ +#define ADC_FULL_SCALE 2000 +#define ADC_FULL_SCALE_VISA 2400 +#define ADC_SUBTR_OFFSET 2250 + +static struct mutex pmu_adc_mutex; + +/* converts raw 8/10-bit value to millivolts */ +unsigned short pmu_adc_raw2mv( + const struct pmu_adc_channel *ch, unsigned short raw) +{ + int full_scale = ADC_FULL_SCALE; + int offset = 0; + + switch (ch->adcc1 & PCF5063X_ADCC1_ADCMUX_MASK) + { + case PCF5063X_ADCC1_MUX_BATSNS_RES: + case PCF5063X_ADCC1_MUX_ADCIN2_RES: + full_scale *= ((ch->adcc1 & PCF5063X_ADCC3_RES_DIV_MASK) == + PCF5063X_ADCC3_RES_DIV_TWO) ? 2 : 3; + break; + case PCF5063X_ADCC1_MUX_BATSNS_SUBTR: + case PCF5063X_ADCC1_MUX_ADCIN2_SUBTR: + offset = ADC_SUBTR_OFFSET; + break; + case PCF5063X_ADCC1_MUX_BATTEMP: + if (ch->adcc2 & PCF5063X_ADCC2_RATIO_BATTEMP) + full_scale = ADC_FULL_SCALE_VISA; + break; + case PCF5063X_ADCC1_MUX_ADCIN1: + if (ch->adcc2 & PCF5063X_ADCC2_RATIO_ADCIN1) + full_scale = ADC_FULL_SCALE_VISA; + break; + } + + int nrb = ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == + PCF5063X_ADCC1_RES_8BIT) ? 8 : 10; + return (raw * full_scale / ((1<adcc3); + if (ch->bias_dly) + sleep(ch->bias_dly); + uint8_t buf[2] = { ch->adcc2, ch->adcc1 | PCF5063X_ADCC1_ADCSTART }; + pmu_write_multiple(PCF5063X_REG_ADCC2, 2, buf); + + int adcs3 = 0; + while (!(adcs3 & PCF5063X_ADCS3_ADCRDY)) + { + yield(); + adcs3 = pmu_read(PCF5063X_REG_ADCS3); + } + + int raw = pmu_read(PCF5063X_REG_ADCS1); + if ((ch->adcc1 & PCF5063X_ADCC1_RES_MASK) == PCF5063X_ADCC1_RES_10BIT) + raw = (raw << 2) | (adcs3 & PCF5063X_ADCS3_ADCDAT1L_MASK); + + mutex_unlock(&pmu_adc_mutex); + return raw; +} + /* * eINT */ #define Q_EINT 0 -static char pmu_thread_stack[DEFAULT_STACK_SIZE/4]; +static char pmu_thread_stack[DEFAULT_STACK_SIZE/2]; 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 = { +static struct eint_handler pmu_eint = +{ .gpio_n = GPIO_EINT_PMU, .type = EIC_INTTYPE_LEVEL, .level = EIC_INTLEVEL_LOW, @@ -355,10 +394,11 @@ void pmu_preinit(void) PCF5063X_REG_STBYCTL1, 0x0, PCF5063X_REG_STBYCTL2, 0x8c, - /* GPIO1,2 = input, GPIO3 = output */ + /* GPIO1,2 = input, GPIO3 = output High (NoPower default) */ PCF5063X_REG_GPIOCTL, 0x3, PCF5063X_REG_GPIO1CFG, 0x0, PCF5063X_REG_GPIO2CFG, 0x0, + PCF5063X_REG_GPIO3CFG, 0x7, /* DOWN2 converter (SDRAM): 1800 mV, enabled, startup current limit = 15mA*0x10 (TBC) */ diff --git a/firmware/target/arm/s5l8702/ipod6g/pmu-target.h b/firmware/target/arm/s5l8702/ipod6g/pmu-target.h index 5552e2196a..d33db42717 100644 --- a/firmware/target/arm/s5l8702/ipod6g/pmu-target.h +++ b/firmware/target/arm/s5l8702/ipod6g/pmu-target.h @@ -22,6 +22,7 @@ #ifndef __PMU_TARGET_H__ #define __PMU_TARGET_H__ +#include #include #include "config.h" @@ -72,14 +73,22 @@ enum pcf50635_reg_gpiostat { * GPIO3: output, unknown */ +struct pmu_adc_channel +{ + const char *name; + uint8_t adcc1; + uint8_t adcc2; + uint8_t adcc3; + uint8_t bias_dly; /* RB ticks */ +}; unsigned char pmu_read(int address); int pmu_write(int address, unsigned char val); int pmu_read_multiple(int address, int count, unsigned char* buffer); int pmu_write_multiple(int address, int count, unsigned char* buffer); -int pmu_read_adc(unsigned int adc); -int pmu_read_battery_voltage(void); -int pmu_read_battery_current(void); +unsigned short pmu_read_adc(const struct pmu_adc_channel *ch); +unsigned short pmu_adc_raw2mv( + const struct pmu_adc_channel *ch, unsigned short raw); void pmu_init(void); void pmu_ldo_on_in_standby(unsigned int ldo, int onoff); void pmu_ldo_set_voltage(unsigned int ldo, unsigned char voltage); diff --git a/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c index 4553b03685..c5f9c9b9f5 100644 --- a/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/powermgmt-ipod6g.c @@ -24,6 +24,7 @@ #include "pmu-target.h" #include "power.h" #include "audiohw.h" +#include "adc-target.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = { @@ -49,20 +50,12 @@ const unsigned short percent_to_volt_charge[11] = }; #endif /* CONFIG_CHARGING */ -/* ADC should read 0x3ff=6.00V */ -#define BATTERY_SCALE_FACTOR 6000 -/* full-scale ADC readout (2^10) in millivolt */ - - /* Returns battery voltage from ADC [millivolts] */ int _battery_voltage(void) { - int compensation = (10 * (pmu_read_battery_current() - 7)) / 12; - if (charging_state()) return pmu_read_battery_voltage() - compensation; - return pmu_read_battery_voltage() + compensation; + return adc_read_battery_voltage(); } - #ifdef HAVE_ACCESSORY_SUPPLY void accessory_supply_set(bool enable) { -- cgit v1.2.3