mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
Merge "Refactor ADC code and add Link charger current ADC support"
This commit is contained in:
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
139
chip/lm4/adc.c
139
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;
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
int adc_read_channel(enum adc_channel ch)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Console commands */
|
||||
|
||||
static int command_adc(int argc, char **argv)
|
||||
{
|
||||
uart_printf("ADC channel %d = 0x%03x\n", ADC_IN0,
|
||||
adc_read(ADC_CH_POT));
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(adc, command_adc);
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
43
chip/lm4/lm4_adc.h
Normal file
43
chip/lm4/lm4_adc.h
Normal file
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -9,27 +9,24 @@
|
||||
#define __CROS_EC_ADC_H
|
||||
|
||||
#include "common.h"
|
||||
#include "board.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,
|
||||
/* 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 */
|
||||
|
||||
Reference in New Issue
Block a user