From e5b5041583e389e6866b210683a09c97a10077d3 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sat, 17 Mar 2012 17:39:27 +0100 Subject: imx233/fuze+: rework lradc/adc code, add external temperature sensing(battery) Rework code to be more useful: - move battery channel init to lradc - always init lradc from system (previously from adc) - don't reserve channels for vddio, nmos or pmos - implement external temperature sensing using current source - use this for battery sensing on the Fuze+ (calibration needed) Change-Id: I5f9a24b9243db7d1e6bdb16b84bc891e61d0c318 --- firmware/target/arm/imx233/adc-imx233.c | 77 ++++++++++------------ firmware/target/arm/imx233/adc-imx233.h | 4 ++ firmware/target/arm/imx233/lradc-imx233.c | 75 +++++++++++++++++++++ firmware/target/arm/imx233/lradc-imx233.h | 13 +++- .../arm/imx233/sansa-fuzeplus/adc-fuzeplus.c | 8 ++- .../target/arm/imx233/sansa-fuzeplus/adc-target.h | 7 +- firmware/target/arm/imx233/system-imx233.c | 2 + 7 files changed, 135 insertions(+), 51 deletions(-) diff --git a/firmware/target/arm/imx233/adc-imx233.c b/firmware/target/arm/imx233/adc-imx233.c index 08adba442d..b24f7f6c43 100644 --- a/firmware/target/arm/imx233/adc-imx233.c +++ b/firmware/target/arm/imx233/adc-imx233.c @@ -25,46 +25,11 @@ #include "system.h" #include "adc-imx233.h" -/* dedicate two channels to temperature sensing - * dedicate channel 7 to battery - * and channel 6 to vddio */ -static int pmos_chan, nmos_chan; -static int battery_chan, vddio_chan; -static int battery_delay_chan; - void adc_init(void) { - imx233_lradc_init(); - /* reserve channels 6 for vddio and 7 for battery (special for conversion) */ - battery_chan = 7; - vddio_chan = 6; - imx233_lradc_reserve_channel(battery_chan); - imx233_lradc_reserve_channel(vddio_chan); - /* reserve any channels for PMOS and NMOS */ - pmos_chan = imx233_lradc_acquire_channel(TIMEOUT_NOBLOCK); - if(pmos_chan < 0) panicf("No LRADC channel for PMOS !"); - nmos_chan = imx233_lradc_acquire_channel(TIMEOUT_NOBLOCK); - if(nmos_chan < 0) panicf("No LRADC channel for NMOS !"); - - /* setup them for the simplest use: no accumulation, no division*/ - imx233_lradc_setup_channel(battery_chan, false, false, 0, HW_LRADC_CHANNEL_BATTERY); - imx233_lradc_setup_channel(vddio_chan, false, false, 0, HW_LRADC_CHANNEL_VDDIO); - imx233_lradc_setup_channel(nmos_chan, false, false, 0, HW_LRADC_CHANNEL_NMOS_THIN); - imx233_lradc_setup_channel(pmos_chan, false, false, 0, HW_LRADC_CHANNEL_PMOS_THIN); - /* setup delay channel for battery for automatic reading and scaling */ - battery_delay_chan = 0; - imx233_lradc_reserve_delay(battery_delay_chan); - /* setup delay to trigger battery channel and retrigger itself. - * The counter runs at 2KHz so a delay of 200 will trigger 10 - * conversions per seconds */ - imx233_lradc_setup_delay(battery_delay_chan, 1 << battery_chan, - 1 << battery_delay_chan, 0, 200); - imx233_lradc_kick_delay(battery_delay_chan); - /* enable automatic conversion, use Li-Ion type battery */ - imx233_lradc_setup_battery_conversion(true, HW_LRADC_CONVERSION__SCALE_FACTOR__LI_ION); } -int adc_read_physical_ex(int virt) +static short adc_read_physical_ex(int virt) { imx233_lradc_clear_channel(virt); imx233_lradc_kick_channel(virt); @@ -72,27 +37,51 @@ int adc_read_physical_ex(int virt) return imx233_lradc_read_channel(virt); } -int adc_read_physical(int src) +static short adc_read_physical(int src, bool div2) { int virt = imx233_lradc_acquire_channel(TIMEOUT_BLOCK); // divide by two for wider ranger - imx233_lradc_setup_channel(virt, true, false, 0, src); + imx233_lradc_setup_channel(virt, div2, false, 0, src); int val = adc_read_physical_ex(virt); imx233_lradc_release_channel(virt); return val; } -unsigned short adc_read_virtual(int c) +static short adc_read_virtual(int c) { switch(c) { case IMX233_ADC_BATTERY: - return adc_read_physical_ex(battery_chan); + return imx233_lradc_read_battery_voltage(); case IMX233_ADC_VDDIO: - return adc_read_physical_ex(vddio_chan); + /* VddIO pin has a builtin 2:1 divide */ + return adc_read_physical(HW_LRADC_CHANNEL_VDDIO, false); + case IMX233_ADC_VDD5V: + /* Vdd5V pin has a builtin 4:1 divide */ + return adc_read_physical(HW_LRADC_CHANNEL_5V, false) * 2; case IMX233_ADC_DIE_TEMP: - // do kelvin to celsius conversion - return imx233_lradc_sense_die_temperature(nmos_chan, pmos_chan) - 273; + { + // don't block on second channel otherwise we might deadlock ! + int nmos_chan = imx233_lradc_acquire_channel(TIMEOUT_BLOCK); + int pmos_chan = imx233_lradc_acquire_channel(TIMEOUT_NOBLOCK); + int val = 0; + if(pmos_chan >= 0) + { + val = imx233_lradc_sense_die_temperature(nmos_chan, pmos_chan) - 273; + imx233_lradc_release_channel(pmos_chan); + } + imx233_lradc_release_channel(nmos_chan); + return val; + } +#ifdef IMX233_ADC_BATT_TEMP_SENSOR + case IMX233_ADC_BATT_TEMP: + { + int virt = imx233_lradc_acquire_channel(TIMEOUT_BLOCK); + int val = imx233_lradc_sense_ext_temperature(virt, IMX233_ADC_BATT_TEMP_SENSOR); + imx233_lradc_release_channel(virt); + return val; + } +#endif default: return 0; } @@ -104,5 +93,5 @@ unsigned short adc_read(int channel) if(c < 0) return adc_read_virtual(c); else - return adc_read_physical(c); + return adc_read_physical(c, true); } diff --git a/firmware/target/arm/imx233/adc-imx233.h b/firmware/target/arm/imx233/adc-imx233.h index 6025fdcdf8..c54e79dc8d 100644 --- a/firmware/target/arm/imx233/adc-imx233.h +++ b/firmware/target/arm/imx233/adc-imx233.h @@ -29,6 +29,10 @@ #define IMX233_ADC_BATTERY -1 /* Battery voltage (mV) */ #define IMX233_ADC_DIE_TEMP -2 /* Die temperature (°C) */ #define IMX233_ADC_VDDIO -3 /* VddIO voltage (mV) */ +#define IMX233_ADC_VDD5V -4 /* Vdd5V voltage (mV) */ +#ifdef IMX233_ADC_BATT_TEMP_SENSOR +#define IMX233_ADC_BATT_TEMP -5 /* Battery temperature (°C) */ +#endif /* Channel mapping */ extern int imx233_adc_mapping[]; diff --git a/firmware/target/arm/imx233/lradc-imx233.c b/firmware/target/arm/imx233/lradc-imx233.c index 432d1c1035..4d309b0875 100644 --- a/firmware/target/arm/imx233/lradc-imx233.c +++ b/firmware/target/arm/imx233/lradc-imx233.c @@ -27,6 +27,9 @@ static struct channel_arbiter_t channel_arbiter; /* delay channels */ static struct channel_arbiter_t delay_arbiter; +/* battery is very special, dedicate a channel and a delay to it */ +static int battery_chan; +static int battery_delay_chan; void imx233_lradc_setup_channel(int channel, bool div2, bool acc, int nr_samples, int src) { @@ -111,6 +114,8 @@ void imx233_lradc_reserve_delay(int channel) int imx233_lradc_sense_die_temperature(int nmos_chan, int pmos_chan) { + imx233_lradc_setup_channel(nmos_chan, false, false, 0, HW_LRADC_CHANNEL_NMOS_THIN); + imx233_lradc_setup_channel(pmos_chan, false, false, 0, HW_LRADC_CHANNEL_PMOS_THIN); // mux sensors __REG_CLR(HW_LRADC_CTRL2) = HW_LRADC_CTRL2__TEMPSENSE_PWD; imx233_lradc_clear_channel(nmos_chan); @@ -129,6 +134,60 @@ int imx233_lradc_sense_die_temperature(int nmos_chan, int pmos_chan) return (diff * 1012) / 4000; } +/* set to 0 to disable current source */ +static void imx233_lradc_set_temp_isrc(int sensor, int value) +{ + if(sensor < 0 || sensor > 1) + panicf("imx233_lradc_set_temp_isrc: invalid sensor"); + unsigned mask = HW_LRADC_CTRL2__TEMP_ISRCx_BM(sensor); + unsigned bp = HW_LRADC_CTRL2__TEMP_ISRCx_BP(sensor); + unsigned en = HW_LRADC_CTRL2__TEMP_SENSOR_IENABLEx(sensor); + + __REG_CLR(HW_LRADC_CTRL2) = mask; + __REG_SET(HW_LRADC_CTRL2) = value << bp; + if(value != 0) + { + __REG_SET(HW_LRADC_CTRL2) = en; + udelay(100); + } + else + __REG_CLR(HW_LRADC_CTRL2) = en; +} + +int imx233_lradc_sense_ext_temperature(int chan, int sensor) +{ +#define EXT_TEMP_ACC_COUNT 5 + /* setup channel */ + imx233_lradc_setup_channel(chan, false, false, 0, sensor); + /* set current source to 300µA */ + imx233_lradc_set_temp_isrc(sensor, HW_LRADC_CTRL2__TEMP_ISRC__300uA); + /* read value and accumulate */ + int a = 0; + for(int i = 0; i < EXT_TEMP_ACC_COUNT; i++) + { + imx233_lradc_clear_channel(chan); + imx233_lradc_kick_channel(chan); + imx233_lradc_wait_channel(chan); + a += imx233_lradc_read_channel(chan); + } + /* setup channel for small accumulation */ + /* set current source to 20µA */ + imx233_lradc_set_temp_isrc(sensor, HW_LRADC_CTRL2__TEMP_ISRC__20uA); + /* read value */ + int b = 0; + for(int i = 0; i < EXT_TEMP_ACC_COUNT; i++) + { + imx233_lradc_clear_channel(chan); + imx233_lradc_kick_channel(chan); + imx233_lradc_wait_channel(chan); + b += imx233_lradc_read_channel(chan); + } + /* disable sensor current */ + imx233_lradc_set_temp_isrc(sensor, HW_LRADC_CTRL2__TEMP_ISRC__0uA); + + return (b - a) / EXT_TEMP_ACC_COUNT; +} + void imx233_lradc_setup_battery_conversion(bool automatic, unsigned long scale_factor) { __REG_CLR(HW_LRADC_CONVERSION) = HW_LRADC_CONVERSION__SCALE_FACTOR_BM; @@ -159,4 +218,20 @@ void imx233_lradc_init(void) // set frequency __REG_CLR(HW_LRADC_CTRL3) = HW_LRADC_CTRL3__CYCLE_TIME_BM; __REG_SET(HW_LRADC_CTRL3) = HW_LRADC_CTRL3__CYCLE_TIME__6MHz; + // setup battery + battery_chan = 7; + imx233_lradc_reserve_channel(battery_chan); + /* setup them for the simplest use: no accumulation, no division*/ + imx233_lradc_setup_channel(battery_chan, false, false, 0, HW_LRADC_CHANNEL_BATTERY); + /* setup delay channel for battery for automatic reading and scaling */ + battery_delay_chan = 0; + imx233_lradc_reserve_delay(battery_delay_chan); + /* setup delay to trigger battery channel and retrigger itself. + * The counter runs at 2KHz so a delay of 200 will trigger 10 + * conversions per seconds */ + imx233_lradc_setup_delay(battery_delay_chan, 1 << battery_chan, + 1 << battery_delay_chan, 0, 200); + imx233_lradc_kick_delay(battery_delay_chan); + /* enable automatic conversion, use Li-Ion type battery */ + imx233_lradc_setup_battery_conversion(true, HW_LRADC_CONVERSION__SCALE_FACTOR__LI_ION); } diff --git a/firmware/target/arm/imx233/lradc-imx233.h b/firmware/target/arm/imx233/lradc-imx233.h index d79ebe0eff..b661fa8aa4 100644 --- a/firmware/target/arm/imx233/lradc-imx233.h +++ b/firmware/target/arm/imx233/lradc-imx233.h @@ -43,8 +43,14 @@ #define HW_LRADC_CTRL2__TEMP_ISRC1_BM 0xf0 #define HW_LRADC_CTRL2__TEMP_ISRC0_BP 0 #define HW_LRADC_CTRL2__TEMP_ISRC0_BM 0xf +#define HW_LRADC_CTRL2__TEMP_ISRCx_BP(x) (4 * (x)) +#define HW_LRADC_CTRL2__TEMP_ISRCx_BM(x) (0xf << (4 * (x))) +#define HW_LRADC_CTRL2__TEMP_ISRC__0uA 0 +#define HW_LRADC_CTRL2__TEMP_ISRC__20uA 1 +#define HW_LRADC_CTRL2__TEMP_ISRC__300uA 15 #define HW_LRADC_CTRL2__TEMP_SENSOR_IENABLE0 (1 << 8) #define HW_LRADC_CTRL2__TEMP_SENSOR_IENABLE1 (1 << 9) +#define HW_LRADC_CTRL2__TEMP_SENSOR_IENABLEx(x) (1 << (8 + (x))) #define HW_LRADC_CTRL2__TEMPSENSE_PWD (1 << 15) #define HW_LRADC_CTRL2__DIVIDE_BY_TWO(x) (1 << ((x) + 24)) @@ -59,7 +65,7 @@ #define HW_LRADC_STATUS (*(volatile uint32_t *)(HW_LRADC_BASE + 0x40)) #define HW_LRADC_CHx(x) (*(volatile uint32_t *)(HW_LRADC_BASE + 0x50 + (x) * 0x10)) -#define HW_LRADC_CHx__NUM_SAMPLES_BM (0xf << 24) +#define HW_LRADC_CHx__NUM_SAMPLES_BM (0x1f << 24) #define HW_LRADC_CHx__NUM_SAMPLES_BP 24 #define HW_LRADC_CHx__ACCUMULATE 29 #define HW_LRADC_CHx__VALUE_BM 0x3ffff @@ -127,8 +133,11 @@ void imx233_lradc_reserve_delay(int dchannel); void imx233_lradc_release_delay(int dchan); /* enable sensing and return temperature in kelvin, - * channels must already be configured as nmos and pmos */ + * channels needs not to be configured */ int imx233_lradc_sense_die_temperature(int nmos_chan, int pmos_chan); +/* return *raw* external temperature, might need some transformation + * channel needs not to be configured */ +int imx233_lradc_sense_ext_temperature(int chan, int sensor); void imx233_lradc_setup_battery_conversion(bool automatic, unsigned long scale_factor); // read scaled voltage, only available after proper setup diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/adc-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/adc-fuzeplus.c index 7207e5f8a8..cf420ae84f 100644 --- a/firmware/target/arm/imx233/sansa-fuzeplus/adc-fuzeplus.c +++ b/firmware/target/arm/imx233/sansa-fuzeplus/adc-fuzeplus.c @@ -26,7 +26,8 @@ int imx233_adc_mapping[] = [ADC_BATTERY] = IMX233_ADC_BATTERY, [ADC_DIE_TEMP] = IMX233_ADC_DIE_TEMP, [ADC_VDDIO] = IMX233_ADC_VDDIO, - [ADC_5V] = HW_LRADC_CHANNEL_5V, + [ADC_5V] = IMX233_ADC_VDD5V, + [ADC_BATT_TEMP] = IMX233_ADC_BATT_TEMP, [ADC_CH2] = HW_LRADC_CHANNEL(2), }; @@ -34,7 +35,8 @@ const char *imx233_adc_channel_name[] = { "Battery(raw)", "Die temperature(°C)", - "VddIO", - "Vdd5V", + "VddIO(mV)", + "Vdd5V(mV)", + "Battery temperature(raw)", "Channel 2", }; diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/adc-target.h b/firmware/target/arm/imx233/sansa-fuzeplus/adc-target.h index e9c6f5d00e..5907eefdc2 100644 --- a/firmware/target/arm/imx233/sansa-fuzeplus/adc-target.h +++ b/firmware/target/arm/imx233/sansa-fuzeplus/adc-target.h @@ -21,12 +21,15 @@ #ifndef _ADC_TARGET_H_ #define _ADC_TARGET_H_ -#define NUM_ADC_CHANNELS 5 +#define NUM_ADC_CHANNELS 6 #define ADC_BATTERY 0 #define ADC_DIE_TEMP 1 #define ADC_VDDIO 2 #define ADC_5V 3 -#define ADC_CH2 4 +#define ADC_BATT_TEMP 4 +#define ADC_CH2 5 + +#define IMX233_ADC_BATT_TEMP_SENSOR 0 #endif diff --git a/firmware/target/arm/imx233/system-imx233.c b/firmware/target/arm/imx233/system-imx233.c index 63bd91998d..7519687568 100644 --- a/firmware/target/arm/imx233/system-imx233.c +++ b/firmware/target/arm/imx233/system-imx233.c @@ -31,6 +31,7 @@ #include "ssp-imx233.h" #include "i2c-imx233.h" #include "dcp-imx233.h" +#include "lradc-imx233.h" #include "lcd.h" #include "backlight-target.h" #include "button.h" @@ -214,6 +215,7 @@ void system_init(void) imx233_dma_init(); imx233_ssp_init(); imx233_dcp_init(); + imx233_lradc_init(); imx233_i2c_init(); #if defined(SANSA_FUZEPLUS) && !defined(BOOTLOADER) fmradio_i2c_init(); -- cgit v1.2.3