diff options
author | Vic Yang <victoryang@chromium.org> | 2012-09-24 17:07:19 +0800 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-09-26 08:58:59 -0700 |
commit | 1b14b2c922808ac1f7084f301d54173ca0074bf7 (patch) | |
tree | e2caa3a413513daf3dbb4bca4bfe65992755ab97 | |
parent | cfcd1412eae4f9f14076d357106709372ca6dc0b (diff) | |
download | chrome-ec-1b14b2c922808ac1f7084f301d54173ca0074bf7.tar.gz |
stm32f: ADC driver
This adds basic ADC support for multiple channel conversion.
BUG=chrome-os-partner:14316
BRANCH=none
TEST=1. Boot on snow.
2. Use keyboard signal as input. Check read value changes as input
signal changes.
Change-Id: I3c15c37446fa9273d098f6d581573c11ced45b5e
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/33883
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | board/spring/board.c | 12 | ||||
-rw-r--r-- | board/spring/board.h | 10 | ||||
-rw-r--r-- | chip/stm32/adc.c | 198 | ||||
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/registers.h | 24 | ||||
-rw-r--r-- | chip/stm32/stm32_adc.h | 23 | ||||
-rw-r--r-- | include/adc.h | 3 |
7 files changed, 269 insertions, 2 deletions
diff --git a/board/spring/board.c b/board/spring/board.c index 10d7eb2f1c..9b52f48552 100644 --- a/board/spring/board.c +++ b/board/spring/board.c @@ -4,6 +4,7 @@ */ /* Spring board-specific configuration */ +#include "adc.h" #include "board.h" #include "chipset.h" #include "common.h" @@ -14,6 +15,7 @@ #include "i2c.h" #include "pmu_tpschrome.h" #include "registers.h" +#include "stm32_adc.h" #include "timer.h" #include "util.h" @@ -95,6 +97,16 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"ILIM_500", GPIO_B, (1<<4), GPIO_OUT_LOW, NULL}, }; +/* ADC channels */ +const struct adc_t adc_channels[ADC_CH_COUNT] = { + /* Micro USB ID pin. Raw ADC value. */ + [ADC_CH_USB_ID] = {"USB_ID", 0, 1, 1, 0, STM32_AIN(5), 0}, + /* Micro USB D+ sense pin. Raw ADC value. */ + [ADC_CH_USB_DP_SNS] = {"USB_DP_SNS", 0, 1, 1, 0, STM32_AIN(2), 0}, + /* Micro USB D- sense pin. Raw ADC value. */ + [ADC_CH_USB_DN_SNS] = {"USB_DN_SNS", 0, 1, 1, 0, STM32_AIN(4), 0}, +}; + void configure_board(void) { uint32_t val; diff --git a/board/spring/board.h b/board/spring/board.h index 8acd4fc187..b8ec6ab2ea 100644 --- a/board/spring/board.h +++ b/board/spring/board.h @@ -55,6 +55,16 @@ /* Battery */ #define CONFIG_BATTERY_BQ20Z453 +/* ADC signal */ +#define CONFIG_ADC +enum adc_channel { + ADC_CH_USB_ID = 0, + ADC_CH_USB_DP_SNS, + ADC_CH_USB_DN_SNS, + + ADC_CH_COUNT +}; + /* GPIO signal list */ enum gpio_signal { /* Inputs with interrupt handlers are first for efficiency */ diff --git a/chip/stm32/adc.c b/chip/stm32/adc.c new file mode 100644 index 0000000000..12247477b9 --- /dev/null +++ b/chip/stm32/adc.c @@ -0,0 +1,198 @@ +/* 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. + */ + +#include "adc.h" +#include "board.h" +#include "console.h" +#include "dma.h" +#include "hooks.h" +#include "registers.h" +#include "stm32_adc.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +struct mutex adc_lock; + +static const struct dma_option dma_adc_option = { + DMAC_ADC, (void *)&STM32_ADC_DR, + DMA_MSIZE_HALF_WORD | DMA_PSIZE_HALF_WORD +}; + +static inline void adc_set_channel(int sample_id, int channel) +{ + uint32_t mask, val; + volatile uint32_t *sqr_reg; + + if (sample_id < 6) { + mask = 0x1f << (sample_id * 5); + val = channel << (sample_id * 5); + sqr_reg = &STM32_ADC_SQR3; + } else if (sample_id < 12) { + mask = 0x1f << ((sample_id - 6) * 5); + val = channel << ((sample_id - 6) * 5); + sqr_reg = &STM32_ADC_SQR2; + } else { + mask = 0x1f << ((sample_id - 12) * 5); + val = channel << ((sample_id - 12) * 5); + sqr_reg = &STM32_ADC_SQR1; + } + + *sqr_reg = (*sqr_reg & ~mask) | val; +} + +static void adc_configure(int ain_id) +{ + /* Set ADC channel */ + adc_set_channel(0, ain_id); + + /* Disable DMA */ + STM32_ADC_CR2 &= ~(1 << 8); + + /* Disable scan mode */ + STM32_ADC_CR1 &= ~(1 << 8); +} + +static void adc_configure_all(void) +{ + int i; + + /* Set ADC channels */ + STM32_ADC_SQR1 = (ADC_CH_COUNT - 1) << 20; + for (i = 0; i < ADC_CH_COUNT; ++i) + adc_set_channel(i, adc_channels[i].channel); + + /* Enable DMA */ + STM32_ADC_CR2 |= (1 << 8); + + /* Enable scan mode */ + STM32_ADC_CR1 |= (1 << 8); +} + +static inline int adc_powered(void) +{ + return STM32_ADC_CR2 & (1 << 0); +} + +static inline int adc_conversion_ended(void) +{ + return STM32_ADC_SR & (1 << 1); +} + +int adc_read_channel(enum adc_channel ch) +{ + const struct adc_t *adc = adc_channels + ch; + int value; + + if (!adc_powered()) + return EC_ERROR_UNKNOWN; + + mutex_lock(&adc_lock); + + adc_configure(adc->channel); + + /* Clear EOC bit */ + STM32_ADC_SR &= ~(1 << 1); + + /* Start conversion */ + STM32_ADC_CR2 |= (1 << 0); /* ADON */ + + /* Wait for EOC bit set */ + while (!adc_conversion_ended()) + ; + value = STM32_ADC_DR & ADC_READ_MAX; + + mutex_unlock(&adc_lock); + return value * adc->factor_mul / adc->factor_div + adc->shift; +} + +int adc_read_all_channels(int *data) +{ + int i; + int16_t raw_data[ADC_CH_COUNT]; + const struct adc_t *adc; + + if (!adc_powered()) + return EC_ERROR_UNKNOWN; + + mutex_lock(&adc_lock); + + adc_configure_all(); + + dma_start_rx(&dma_adc_option, ADC_CH_COUNT, raw_data); + + /* Start conversion */ + STM32_ADC_CR2 |= (1 << 0); /* ADON */ + + if (dma_wait(DMAC_ADC)) { + mutex_unlock(&adc_lock); + return EC_ERROR_UNKNOWN; + } + + for (i = 0; i < ADC_CH_COUNT; ++i) { + adc = adc_channels + i; + data[i] = raw_data[i] * adc->factor_mul / adc->factor_div + + adc->shift; + } + + mutex_unlock(&adc_lock); + return EC_SUCCESS; +} + +static int adc_init(void) +{ + /* + * Enable ADC clock. + * APB2 clock is 16MHz. ADC clock prescaler is /2. + * So the ADC clock is 8MHz. + */ + STM32_RCC_APB2ENR |= (1 << 9); + + if (!adc_powered()) { + /* Power on ADC module */ + STM32_ADC_CR2 |= (1 << 0); /* ADON */ + + /* Reset calibration */ + STM32_ADC_CR2 |= (1 << 3); /* RSTCAL */ + while (STM32_ADC_CR2 & (1 << 3)) + ; + + /* A/D Calibrate */ + STM32_ADC_CR2 |= (1 << 2); /* CAL */ + while (STM32_ADC_CR2 & (1 << 2)) + ; + } + + /* Set right alignment */ + STM32_ADC_CR2 &= ~(1 << 11); + + /* + * Set sample time of all channels to 1.5 cycles. + * Conversion takes 1.75 us. + */ + STM32_ADC_SMPR1 = 0; + STM32_ADC_SMPR2 = 0; + + return EC_SUCCESS; +} +DECLARE_HOOK(HOOK_INIT, adc_init, HOOK_PRIO_DEFAULT); + +static int command_adc(int argc, char **argv) +{ + int i; + int data[ADC_CH_COUNT]; + + if (adc_read_all_channels(data)) + return EC_ERROR_UNKNOWN; + for (i = 0; i < ADC_CH_COUNT; ++i) + ccprintf("ADC channel \"%s\" = %d\n", + adc_channels[i].name, data[i]); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(adc, command_adc, + NULL, + "Print ADC channels", + NULL); diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index 33a7ffb943..ebce4cf35a 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -16,3 +16,4 @@ chip-$(CONFIG_I2C)+=i2c.o chip-$(CONFIG_TASK_WATCHDOG)+=watchdog.o chip-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o chip-$(CONFIG_FLASH)+=flash-$(CHIP_VARIANT).o +chip-$(CONFIG_ADC)+=adc.o diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index 4831916a9e..f08881d17c 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -486,11 +486,31 @@ struct spi_ctlr { #define STM32_EXTI_SWIER REG32(STM32_EXTI_BASE + 0x10) #define STM32_EXTI_PR REG32(STM32_EXTI_BASE + 0x14) -/* --- MISC --- */ +/* --- ADC --- */ -#define STM32_RI_BASE 0x40007C04 #define STM32_ADC1_BASE 0x40012400 #define STM32_ADC_BASE 0x40012700 /* STM32L15X only */ + +#if defined(CHIP_VARIANT_stm32f100) +#define STM32_ADC_SR REG32(STM32_ADC1_BASE + 0x00) +#define STM32_ADC_CR1 REG32(STM32_ADC1_BASE + 0x04) +#define STM32_ADC_CR2 REG32(STM32_ADC1_BASE + 0x08) +#define STM32_ADC_SMPR1 REG32(STM32_ADC1_BASE + 0x0C) +#define STM32_ADC_SMPR2 REG32(STM32_ADC1_BASE + 0x10) +#define STM32_ADC_JOFR(n) REG32(STM32_ADC1_BASE + 0x14 + ((n)&3) * 4) +#define STM32_ADC_HTR REG32(STM32_ADC1_BASE + 0x24) +#define STM32_ADC_LTR REG32(STM32_ADC1_BASE + 0x28) +#define STM32_ADC_SQR1 REG32(STM32_ADC1_BASE + 0x2C) +#define STM32_ADC_SQR2 REG32(STM32_ADC1_BASE + 0x30) +#define STM32_ADC_SQR3 REG32(STM32_ADC1_BASE + 0x34) +#define STM32_ADC_JSQR REG32(STM32_ADC1_BASE + 0x38) +#define STM32_ADC_JDR(n) REG32(STM32_ADC1_BASE + 0x3C + ((n)&3) * 4) +#define STM32_ADC_DR REG32(STM32_ADC1_BASE + 0x4C) +#endif + +/* --- MISC --- */ + +#define STM32_RI_BASE 0x40007C04 #define STM32_COMP_BASE 0x40007C00 #define STM32_CEC_BASE 0x40007800 /* STM32F100 only */ #define STM32_DAC_BASE 0x40007400 diff --git a/chip/stm32/stm32_adc.h b/chip/stm32/stm32_adc.h new file mode 100644 index 0000000000..01e67c603b --- /dev/null +++ b/chip/stm32/stm32_adc.h @@ -0,0 +1,23 @@ +/* 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. + */ + +/* STM32-specific ADC module for Chrome EC */ + +#ifndef __CROS_EC_STM32_ADC_H +#define __CROS_EC_STM32_ADC_H + +extern const struct adc_t adc_channels[ADC_CH_COUNT]; + +/* Minimum and maximum values returned by adc_read_channel(). */ +#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 STM32_AIN(x) (x) + +#endif /* __CROS_EC_STM32_ADC_H */ diff --git a/include/adc.h b/include/adc.h index 661baca292..fe1296cc3b 100644 --- a/include/adc.h +++ b/include/adc.h @@ -26,4 +26,7 @@ struct adc_t /* Read ADC channel. */ int adc_read_channel(enum adc_channel ch); +/* Read all ADC channels. Return non-zero on error. */ +int adc_read_all_channels(int *data); + #endif /* __CROS_EC_ADC_H */ |