diff options
-rw-r--r-- | board/bds/board.c | 19 | ||||
-rw-r--r-- | board/bds/board.h | 10 | ||||
-rw-r--r-- | board/link/board.c | 18 | ||||
-rw-r--r-- | board/link/board.h | 10 | ||||
-rw-r--r-- | chip/lm4/adc.c | 135 | ||||
-rw-r--r-- | chip/lm4/lm4_adc.h | 43 | ||||
-rw-r--r-- | chip/lm4/temp_sensor.c | 6 | ||||
-rw-r--r-- | include/adc.h | 31 |
8 files changed, 180 insertions, 92 deletions
diff --git a/board/bds/board.c b/board/bds/board.c index 71bc810444..631cf29079 100644 --- a/board/bds/board.c +++ b/board/bds/board.c @@ -9,6 +9,25 @@ #include "power_button.h" #include "registers.h" #include "util.h" +#include "lm4_adc.h" +#include "adc.h" + +/* ADC channels. Must be in the exactly same order as in enum adc_channel. */ +const struct adc_t adc_channels[ADC_CH_COUNT] = +{ + /* EC internal temperature is calculated by + * 273 + (295 - 450 * ADC_VALUE / ADC_READ_MAX) / 2 + * = -225 * ADC_VALUE / ADC_READ_MAX + 420.5 + */ + {"ECTemp", LM4_ADC_SEQ0, -225, ADC_READ_MAX, 420, + LM4_NO_AIN, 0x0e /* TS0 | IE0 | END0 */}, + + /* Charger current is mapped from 0~4000mA to 0~1.6V. + * And ADC maps 0~3.3V to ADC_READ_MAX. + */ + {"ChargerCurrent", LM4_ADC_SEQ1, 33 * 4000, ADC_READ_MAX * 16, 0, + LM4_AIN(ADC_IN0), 0x06 /* IE0 | END0 */}, +}; /* GPIO signal list. Must match order from enum gpio_signal. */ diff --git a/board/bds/board.h b/board/bds/board.h index 9b28cd6e46..fbd4f153ca 100644 --- a/board/bds/board.h +++ b/board/bds/board.h @@ -46,6 +46,16 @@ /* TODO: really just need a lookup table for channels to inputs */ #define ADC_IN0 0 /* Turn POT on badger board */ +enum adc_channel +{ + /* EC internal die temperature in degrees K. */ + ADC_CH_EC_TEMP = 0, + /* Treat BDS pot input as charger current. */ + ADC_CH_CHARGER_CURRENT, + + ADC_CH_COUNT +}; + /* I2C ports */ #define I2C_PORT_BATTERY 5 // port 0 / PB2:3 on Link, open on badger #define I2C_PORT_CHARGER 5 // port 1 / PA6:7 on Link, user LED on badger diff --git a/board/link/board.c b/board/link/board.c index 20a2437b00..67d975686e 100644 --- a/board/link/board.c +++ b/board/link/board.c @@ -11,6 +11,8 @@ #include "registers.h" #include "util.h" #include "x86_power.h" +#include "lm4_adc.h" +#include "adc.h" #ifndef CONFIG_TASK_X86POWER #define x86_power_interrupt NULL @@ -99,6 +101,22 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"USB2_ILIM_SEL", LM4_GPIO_E, (1<<0), GPIO_OUT_LOW, NULL}, }; +/* ADC channels. Must be in the exactly same order as in enum adc_channel. */ +const struct adc_t adc_channels[ADC_CH_COUNT] = +{ + /* EC internal temperature is calculated by + * 273 + (295 - 450 * ADC_VALUE / ADC_READ_MAX) / 2 + * = -225 * ADC_VALUE / ADC_READ_MAX + 420.5 + */ + {"ECTemp", LM4_ADC_SEQ0, -225, ADC_READ_MAX, 420, + LM4_NO_AIN, 0x0e /* TS0 | IE0 | END0 */}, + + /* Charger current is mapped from 0~4000mA to 0~1.6V. + * And ADC maps 0~3.3V to ADC_READ_MAX. + */ + {"ChargerCurrent", LM4_ADC_SEQ1, 33 * 4000, ADC_READ_MAX * 16, 0, + LM4_AIN(ADC_IN0), 0x06 /* IE0 | END0 */}, +}; void configure_board(void) { diff --git a/board/link/board.h b/board/link/board.h index 923ae716ad..0387bc45fc 100644 --- a/board/link/board.h +++ b/board/link/board.h @@ -46,6 +46,16 @@ /* TODO: assign real ADC inputs */ #define ADC_IN0 11 /* Charger current */ +enum adc_channel +{ + /* EC internal die temperature in degrees K. */ + ADC_CH_EC_TEMP = 0, + /* Charger current in mA. */ + ADC_CH_CHARGER_CURRENT, + + ADC_CH_COUNT +}; + /* I2C ports */ #define I2C_PORT_BATTERY 0 #define I2C_PORT_CHARGER 1 diff --git a/chip/lm4/adc.c b/chip/lm4/adc.c index 531411fd21..740c9a623d 100644 --- a/chip/lm4/adc.c +++ b/chip/lm4/adc.c @@ -3,9 +3,9 @@ * found in the LICENSE file. */ -/* ADC module for Chrome EC */ +/* LM4-specific ADC module for Chrome EC */ -#include "board.h" +#include "lm4_adc.h" #include "console.h" #include "adc.h" #include "timer.h" @@ -13,6 +13,7 @@ #include "uart.h" #include "util.h" +extern const struct adc_t adc_channels[ADC_CH_COUNT]; static void configure_gpio(void) { @@ -22,16 +23,13 @@ static void configure_gpio(void) LM4_SYSTEM_RCGCGPIO |= 0x0010; scratch = LM4_SYSTEM_RCGCGPIO; - /* Use analog function for PE3 (AIN0) */ + /* Use analog function for PE3 (AIN0) */ LM4_GPIO_DEN(LM4_GPIO_E) &= ~0x08; LM4_GPIO_AMSEL(LM4_GPIO_E) |= 0x08; } - -int adc_read(enum adc_channel ch) +int lm4_adc_flush_and_read(enum lm4_adc_sequencer seq) { - volatile uint32_t scratch __attribute__((unused)); - /* TODO: right now we have only a single channel so this is * simple. When we have multiple channels, should we... * @@ -44,84 +42,95 @@ int adc_read(enum adc_channel ch) * callers; doesn't matter if just used for debugging. * * 3) Both? */ - if (ch != ADC_CH_POT) - return ADC_READ_ERROR; - - /* Empty the FIFO of any previous results */ - while (!(LM4_ADC_SSFSTAT(0) & 0x100)) - scratch = LM4_ADC_SSFIFO(0); - - /* Clear the interrupt status */ - LM4_ADC_ADCISC |= 0x01; - - /* Initiate sample sequence */ - LM4_ADC_ADCPSSI |= 0x01; - - /* Wait for interrupt */ - /* TODO: use a real interrupt */ - while (!(LM4_ADC_ADCRIS & 0x01)); - - /* Read the FIFO */ - return LM4_ADC_SSFIFO(0); -} - - -int adc_read_ec_temperature(void) -{ volatile uint32_t scratch __attribute__((unused)); - int a; /* Empty the FIFO of any previous results */ - while (!(LM4_ADC_SSFSTAT(3) & 0x100)) - scratch = LM4_ADC_SSFIFO(3); + while (!(LM4_ADC_SSFSTAT(seq) & 0x100)) + scratch = LM4_ADC_SSFIFO(seq); /* Clear the interrupt status */ - LM4_ADC_ADCISC |= 0x08; + LM4_ADC_ADCISC |= 0x01 << seq; /* Initiate sample sequence */ - LM4_ADC_ADCPSSI |= 0x08; + LM4_ADC_ADCPSSI |= 0x01 << seq; /* Wait for interrupt */ /* TODO: use a real interrupt */ /* TODO: timeout */ - while (!(LM4_ADC_ADCRIS & 0x08)); + while (!(LM4_ADC_ADCRIS & (0x01 << seq))); /* Read the FIFO and convert to temperature */ - a = LM4_ADC_SSFIFO(3); - return 273 + (295 - (225 * 2 * a) / ADC_READ_MAX) / 2; + return LM4_ADC_SSFIFO(seq); } +int lm4_adc_configure(enum lm4_adc_sequencer seq, + int ain_id, + int ssctl) +{ + volatile uint32_t scratch __attribute__((unused)); + /* TODO: set up clock using ADCCC register? */ + /* Configure sample sequencer */ + LM4_ADC_ADCACTSS &= ~(0x01 << seq); + /* Trigger sequencer by processor request */ + LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & ~(0xf << (seq * 4))) | 0x00; + /* Sample internal temp sensor */ + if (ain_id != LM4_AIN_NONE) { + LM4_ADC_SSMUX(seq) = ain_id & 0xf; + LM4_ADC_SSEMUX(seq) = ain_id >> 4; + } + else { + LM4_ADC_SSMUX(seq) = 0x00; + LM4_ADC_SSEMUX(seq) = 0x00; + } + LM4_ADC_SSCTL(seq) = ssctl; + /* Enable sample sequencer */ + LM4_ADC_ADCACTSS |= 0x01 << seq; -/*****************************************************************************/ -/* Console commands */ + return EC_SUCCESS; +} -static int command_adc(int argc, char **argv) +int adc_read_channel(enum adc_channel ch) { - uart_printf("ADC channel %d = 0x%03x\n", ADC_IN0, - adc_read(ADC_CH_POT)); - return EC_SUCCESS; + const struct adc_t *adc = adc_channels + ch; + int rv = lm4_adc_flush_and_read(adc->sequencer); + return rv * adc->factor_mul / adc->factor_div + adc->shift; } -DECLARE_CONSOLE_COMMAND(adc, command_adc); +/*****************************************************************************/ +/* Console commands */ static int command_ectemp(int argc, char **argv) { - int t = adc_read_ec_temperature(); + int t = adc_read_channel(ADC_CH_EC_TEMP); uart_printf("EC temperature is %d K = %d C\n", t, t-273); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(ectemp, command_ectemp); +static int command_adc(int argc, char **argv) +{ + int i; + for (i = 0; i < ADC_CH_COUNT; ++i) + uart_printf("ADC channel \"%s\" = %d\n", + adc_channels[i].name, + adc_read_channel(i)); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(adc, command_adc); + +/*****************************************************************************/ /* Initialization */ int adc_init(void) { - volatile uint32_t scratch __attribute__((unused)); + int i; + const struct adc_t *adc; /* Enable ADC0 module and delay a few clocks */ LM4_SYSTEM_RCGCADC |= 0x01; - scratch = LM4_SYSTEM_RCGCADC; + udelay(1); /* Configure GPIOs */ configure_gpio(); @@ -130,29 +139,11 @@ int adc_init(void) * VDDA and GNDA. */ LM4_ADC_ADCCTL = 0x01; - /* TODO: set up clock using ADCCC register? */ - - /* Configure sample sequencer 0 */ - LM4_ADC_ADCACTSS &= ~0x01; - /* Trigger SS0 by processor request */ - LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & 0xfffffff0) | 0x00; - /* Sample only our one channel */ - LM4_ADC_SSMUX(0) = ADC_IN0 & 0x0f; - LM4_ADC_SSEMUX(0) = (ADC_IN0 >> 4) & 0x0f; - LM4_ADC_SSCTL(0) = 0x06; /* IE0 | END0 */ - /* Enable sample sequencer 0 */ - LM4_ADC_ADCACTSS |= 0x01; - - /* Configure sample sequencer 3 */ - LM4_ADC_ADCACTSS &= ~0x08; - /* Trigger SS3 by processor request */ - LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & 0xffffff0f) | 0x00; - /* Sample internal temp sensor */ - LM4_ADC_SSMUX(3) = 0x00; - LM4_ADC_SSEMUX(3) = 0x00; - LM4_ADC_SSCTL(3) = 0x0e; /* TS0 | IE0 | END0 */ - /* Enable sample sequencer 3 */ - LM4_ADC_ADCACTSS |= 0x08; + /* Initialize ADC sequencer */ + for (i = 0; i < ADC_CH_COUNT; ++i) { + adc = adc_channels + i; + lm4_adc_configure(adc->sequencer, adc->channel, adc->flag); + } return EC_SUCCESS; } diff --git a/chip/lm4/lm4_adc.h b/chip/lm4/lm4_adc.h new file mode 100644 index 0000000000..dad05be703 --- /dev/null +++ b/chip/lm4/lm4_adc.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* LM4-specific ADC module for Chrome EC */ + +#ifndef __CROS_EC_LM4_ADC_H +#define __CROS_EC_LM4_ADC_H + +enum lm4_adc_sequencer +{ + LM4_ADC_SEQ0 = 0, + LM4_ADC_SEQ1, + LM4_ADC_SEQ2, + LM4_ADC_SEQ3, + + LM4_ADC_SEQ_COUNT +}; + +/* Minimum and maximum values returned by lm4_adc_flush_and_read(). */ +#define ADC_READ_MIN 0 +#define ADC_READ_MAX 4095 + +/* Value returned if the read failed. */ +#define ADC_READ_ERROR -1 + +/* Just plain id mapping for code readability */ +#define LM4_AIN(x) (x) +#define LM4_AIN_NONE (-1) + +/* Dummy value for "channel" in adc_t if we don't have an external channel. */ +#define LM4_NO_AIN 0 + +/* Flush an ADC sequencer and initiate a read. Return raw ADC value. */ +int lm4_adc_flush_and_read(enum lm4_adc_sequencer); + +/* Configure an ADC sequencer to be dedicated for an ADC input "ain_id". + * Value in "ssctl" field is passed to sampler sequencer control register. + */ +int lm4_adc_configure(enum lm4_adc_sequencer, int ain_id, int ssctl); + +#endif /* __CROS_EC_LM4_ADC_H */ diff --git a/chip/lm4/temp_sensor.c b/chip/lm4/temp_sensor.c index 7755bcf140..a9095bf380 100644 --- a/chip/lm4/temp_sensor.c +++ b/chip/lm4/temp_sensor.c @@ -53,11 +53,11 @@ int temp_sensor_read(enum temp_sensor_id id) return -1; t = (int)(int16_t)traw / 128; return t + 273; - case TEMP_SENSOR_EC_INTERNAL: - return adc_read_ec_temperature(); + return adc_read_channel(ADC_CH_EC_TEMP); + default: + return -1; } - /* If we're still here, we don't handle that sensor */ return -1; } diff --git a/include/adc.h b/include/adc.h index 1f01f2c08f..a59c58c463 100644 --- a/include/adc.h +++ b/include/adc.h @@ -9,27 +9,24 @@ #define __CROS_EC_ADC_H #include "common.h" - -/* Value returned by adc_read_*() methods if the read failed. */ -#define ADC_READ_ERROR -1 - -/* Minimum and maximum values returned by adc_read(). */ -#define ADC_READ_MIN 0 -#define ADC_READ_MAX 4095 - -/* ADC channels */ -/* TODO: channel mapping is board-specific */ -enum adc_channel { - ADC_CH_POT = 0, +#include "board.h" + +/* Data structure to define ADC channels. */ +struct adc_t +{ + const char* name; + int sequencer; + int factor_mul; + int factor_div; + int shift; + int channel; + int flag; }; /* Initializes the module. */ int adc_init(void); -/* Reads an ADC channel. Returns the ADC value, or ADC_READ_ERROR if error. */ -int adc_read(enum adc_channel ch); - -/* Returns the internal EC temperature in K, or ADC_READ_ERROR if error. */ -int adc_read_ec_temperature(void); +/* Read ADC channel. */ +int adc_read_channel(enum adc_channel ch); #endif /* __CROS_EC_ADC_H */ |