summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Nematbakhsh <shawnn@chromium.org>2015-12-15 16:42:55 -0800
committerchrome-bot <chrome-bot@chromium.org>2015-12-21 14:58:56 -0800
commit8038e5f146905181ea93140184f1a350f078bdd0 (patch)
treec1f9e89a2c29c0bfe62c157bbf70af563b341bc0
parent34f226422733c8d98bcfee946e1994997fa7c1e8 (diff)
downloadchrome-ec-8038e5f146905181ea93140184f1a350f078bdd0.tar.gz
stm32: adc: Add support for DMA continuous mode
Add support for continuously writing ADC samples to a circular buffer. CONFIG_ADC_PROFILE_FAST_CONTINUOUS should be defined and an appropriate sized buffer must be passed to adc_read_all_channels(). BUG=chromium:569994 TEST=Manual on snoball. Verify 'adc' continues to function (single mode). With pending commit, verify that continuous conversion interrupt is called at appropriate frequency and values look consistent. BRANCH=None Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> Change-Id: I025825d72a698f8f1f4f95a89477df791bd5e67e Reviewed-on: https://chromium-review.googlesource.com/318505 Commit-Ready: Shawn N <shawnn@chromium.org> Tested-by: Shawn N <shawnn@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org>
-rw-r--r--chip/stm32/adc-stm32f0.c117
-rw-r--r--chip/stm32/registers.h43
-rw-r--r--common/adc.c3
-rw-r--r--include/adc.h4
-rw-r--r--include/config.h14
5 files changed, 150 insertions, 31 deletions
diff --git a/chip/stm32/adc-stm32f0.c b/chip/stm32/adc-stm32f0.c
index 17cc8b7e2d..ca111d055e 100644
--- a/chip/stm32/adc-stm32f0.c
+++ b/chip/stm32/adc-stm32f0.c
@@ -18,18 +18,64 @@
struct mutex adc_lock;
-static const struct dma_option dma_adc_option = {
+struct adc_profile_t {
+ /* Register values. */
+ uint32_t cfgr1_reg;
+ uint32_t cfgr2_reg;
+ uint32_t smpr_reg;
+ uint32_t ier_reg;
+ /* DMA config. */
+ const struct dma_option *dma_option;
+ /* Size of DMA buffer, in units of ADC_CH_COUNT. */
+ int dma_buffer_size;
+};
+
+#ifdef CONFIG_ADC_PROFILE_SINGLE
+static const struct dma_option dma_single = {
STM32_DMAC_ADC, (void *)&STM32_ADC_DR,
STM32_DMA_CCR_MSIZE_32_BIT | STM32_DMA_CCR_PSIZE_32_BIT,
};
+static const struct adc_profile_t profile = {
+ /* Sample all channels once using DMA */
+ .cfgr1_reg = STM32_ADC_CFGR1_OVRMOD,
+ .cfgr2_reg = 0,
+ .smpr_reg = STM32_ADC_SMPR_13_5_CY,
+ .ier_reg = 0,
+ .dma_option = &dma_single,
+ .dma_buffer_size = 1,
+};
+#endif
+
+#ifdef CONFIG_ADC_PROFILE_FAST_CONTINUOUS
+static const struct dma_option dma_continuous = {
+ STM32_DMAC_ADC, (void *)&STM32_ADC_DR,
+ STM32_DMA_CCR_MSIZE_32_BIT | STM32_DMA_CCR_PSIZE_32_BIT |
+ STM32_DMA_CCR_CIRC,
+};
+
+static const struct adc_profile_t profile = {
+ /* Sample all channels continuously using DMA */
+ .cfgr1_reg = STM32_ADC_CFGR1_OVRMOD |
+ STM32_ADC_CFGR1_CONT |
+ STM32_ADC_CFGR1_DMACFG,
+ .cfgr2_reg = 0,
+ .smpr_reg = STM32_ADC_SMPR_1_5_CY,
+ /* Fire interrupt at end of sequence. */
+ .ier_reg = STM32_ADC_IER_EOSEQIE,
+ .dma_option = &dma_continuous,
+ /* Double-buffer our samples. */
+ .dma_buffer_size = 2,
+};
+#endif
+
static void adc_configure(int ain_id)
{
/* Select channel to convert */
STM32_ADC_CHSELR = 1 << ain_id;
/* Disable DMA */
- STM32_ADC_CFGR1 &= ~0x1;
+ STM32_ADC_CFGR1 &= ~STM32_ADC_CFGR1_DMAEN;
}
#ifdef CONFIG_ADC_WATCHDOG
@@ -42,7 +88,7 @@ static void adc_continuous_read(int ain_id)
adc_configure(ain_id);
/* CONT=1 -> continuous mode on */
- STM32_ADC_CFGR1 |= 1 << 13;
+ STM32_ADC_CFGR1 |= STM32_ADC_CFGR1_CONT;
/* Start continuous conversion */
STM32_ADC_CR |= 1 << 2; /* ADSTART */
@@ -58,7 +104,7 @@ static void adc_continuous_stop(void)
;
/* CONT=0 -> continuous mode off */
- STM32_ADC_CFGR1 &= ~(1 << 13);
+ STM32_ADC_CFGR1 &= ~STM32_ADC_CFGR1_CONT;
}
static void adc_interval_read(int ain_id, int interval_ms)
@@ -66,10 +112,12 @@ static void adc_interval_read(int ain_id, int interval_ms)
adc_configure(ain_id);
/* EXTEN=01 -> hardware trigger detection on rising edge */
- STM32_ADC_CFGR1 = (STM32_ADC_CFGR1 & ~0xc00) | (1 << 10);
+ STM32_ADC_CFGR1 = (STM32_ADC_CFGR1 & ~STM32_ADC_CFGR1_EXTEN_MASK)
+ | STM32_ADC_CFGR1_EXTEN_RISE;
/* EXTSEL=TRG3 -> Trigger on TIM3_TRGO */
- STM32_ADC_CFGR1 = (STM32_ADC_CFGR1 & ~0x1c0) | (3 << 6);
+ STM32_ADC_CFGR1 = (STM32_ADC_CFGR1 & ~STM32_ADC_CFGR1_TRG_MASK) |
+ STM32_ADC_CFGR1_TRG3;
__hw_timer_enable_clock(TIM_ADC, 1);
@@ -96,7 +144,7 @@ static void adc_interval_read(int ain_id, int interval_ms)
static void adc_interval_stop(void)
{
/* EXTEN=00 -> hardware trigger detection disabled */
- STM32_ADC_CFGR1 &= ~0xc00;
+ STM32_ADC_CFGR1 &= ~STM32_ADC_CFGR1_EXTEN_MASK;
/* Set ADSTP to clear ADSTART */
STM32_ADC_CR |= 1 << 4; /* ADSTP */
@@ -111,22 +159,22 @@ static void adc_interval_stop(void)
static int adc_watchdog_enabled(void)
{
- return STM32_ADC_CFGR1 & (1 << 23);
+ return STM32_ADC_CFGR1 & STM32_ADC_CFGR1_AWDEN;
}
static int adc_enable_watchdog_no_lock(void)
{
/* Select channel */
- STM32_ADC_CFGR1 = (STM32_ADC_CFGR1 & ~0x7c000000) |
+ STM32_ADC_CFGR1 = (STM32_ADC_CFGR1 & ~STM32_ADC_CFGR1_AWDCH_MASK) |
(watchdog_ain_id << 26);
adc_configure(watchdog_ain_id);
/* Clear AWD interupt flag */
STM32_ADC_ISR = 0x80;
/* Set Watchdog enable bit on a single channel */
- STM32_ADC_CFGR1 |= (1 << 23) | (1 << 22);
+ STM32_ADC_CFGR1 |= STM32_ADC_CFGR1_AWDEN | STM32_ADC_CFGR1_AWDSGL;
/* Enable interrupt */
- STM32_ADC_IER |= 1 << 7;
+ STM32_ADC_IER |= STM32_ADC_IER_AWDIE;
if (watchdog_delay_ms)
adc_interval_read(watchdog_ain_id, watchdog_delay_ms);
@@ -160,7 +208,7 @@ static int adc_disable_watchdog_no_lock(void)
adc_continuous_stop();
/* Clear Watchdog enable bit */
- STM32_ADC_CFGR1 &= ~(1 << 23);
+ STM32_ADC_CFGR1 &= ~STM32_ADC_CFGR1_AWDEN;
return EC_SUCCESS;
}
@@ -240,14 +288,15 @@ int adc_read_all_channels(int *data)
{
int i;
uint32_t channels = 0;
- uint32_t raw_data[ADC_CH_COUNT];
const struct adc_t *adc;
int restore_watchdog = 0;
int ret = EC_SUCCESS;
+ int blocking_read = !profile.ier_reg;
mutex_lock(&adc_lock);
if (adc_watchdog_enabled()) {
+ ASSERT(blocking_read);
restore_watchdog = 1;
adc_disable_watchdog_no_lock();
}
@@ -258,31 +307,41 @@ int adc_read_all_channels(int *data)
STM32_ADC_CHSELR = channels;
/* Enable DMA */
- STM32_ADC_CFGR1 |= 0x1;
+ STM32_ADC_CFGR1 |= STM32_ADC_CFGR1_DMAEN;
dma_clear_isr(STM32_DMAC_ADC);
- dma_start_rx(&dma_adc_option, ADC_CH_COUNT, raw_data);
+ dma_start_rx(profile.dma_option,
+ profile.dma_buffer_size * ADC_CH_COUNT,
+ data);
/* Clear flags */
STM32_ADC_ISR = 0xe;
+ /* Enable requested interrupt(s) */
+ STM32_ADC_IER |= profile.ier_reg;
+ if (!blocking_read)
+ task_enable_irq(STM32_IRQ_ADC_COMP);
STM32_ADC_CR |= 1 << 2; /* ADSTART */
- if (dma_wait(STM32_DMAC_ADC)) {
- ret = EC_ERROR_UNKNOWN;
- goto fail; /* goto fail; goto fail; */
- }
-
- for (i = 0; i < ADC_CH_COUNT; ++i) {
- adc = adc_channels + i;
- data[i] = (raw_data[i] & 0xffff) *
- adc->factor_mul / adc->factor_div + adc->shift;
+ if (blocking_read) {
+ if (dma_wait(STM32_DMAC_ADC)) {
+ ret = EC_ERROR_UNKNOWN;
+ goto fail;
+ }
+
+ for (i = 0; i < ADC_CH_COUNT; ++i) {
+ adc = adc_channels + i;
+ data[i] = (data[i] & 0xffff) *
+ adc->factor_mul / adc->factor_div +
+ adc->shift;
+ }
}
fail:
if (restore_watchdog)
adc_enable_watchdog_no_lock();
- mutex_unlock(&adc_lock);
+ if (blocking_read)
+ mutex_unlock(&adc_lock);
return ret;
}
@@ -306,11 +365,11 @@ static void adc_init(void)
;
/* Single conversion, right aligned, 12-bit */
- STM32_ADC_CFGR1 = 1 << 12; /* (1 << 15) => AUTOOFF */;
+ STM32_ADC_CFGR1 = profile.cfgr1_reg;
/* clock is ADCCLK (ADEN must be off when writing this reg) */
- STM32_ADC_CFGR2 = 0;
- /* Sampling time : 13.5 ADC clock cycles. */
- STM32_ADC_SMPR = 2;
+ STM32_ADC_CFGR2 = profile.cfgr2_reg;
+ /* Sampling time */
+ STM32_ADC_SMPR = profile.smpr_reg;
/*
* ADC enable (note: takes 4 ADC clocks between end of calibration
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index 9352584d28..57fb2a9ec0 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -848,12 +848,55 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t;
#define STM32_ADC_ISR REG32(STM32_ADC1_BASE + 0x00)
#define STM32_ADC_ISR_ADRDY (1 << 0)
#define STM32_ADC_IER REG32(STM32_ADC1_BASE + 0x04)
+#define STM32_ADC_IER_AWDIE (1 << 7)
+#define STM32_ADC_IER_OVRIE (1 << 4)
+#define STM32_ADC_IER_EOSEQIE (1 << 3)
+#define STM32_ADC_IER_EOCIE (1 << 2)
+#define STM32_ADC_IER_EOSMPIE (1 << 1)
+#define STM32_ADC_IER_ADRDYIE (1 << 0)
+
#define STM32_ADC_CR REG32(STM32_ADC1_BASE + 0x08)
#define STM32_ADC_CR_ADEN (1 << 0)
#define STM32_ADC_CR_ADCAL (1 << 31)
#define STM32_ADC_CFGR1 REG32(STM32_ADC1_BASE + 0x0C)
+/* Analog watchdog channel selection */
+#define STM32_ADC_CFGR1_AWDCH_MASK (0x1f << 26)
+#define STM32_ADC_CFGR1_AWDEN (1 << 23)
+#define STM32_ADC_CFGR1_AWDSGL (1 << 22)
+/* Selects single vs continuous */
+#define STM32_ADC_CFGR1_CONT (1 << 13)
+/* Selects ADC_DR overwrite vs preserve */
+#define STM32_ADC_CFGR1_OVRMOD (1 << 12)
+/* External trigger polarity selection */
+#define STM32_ADC_CFGR1_EXTEN_DIS (0 << 10)
+#define STM32_ADC_CFGR1_EXTEN_RISE (1 << 10)
+#define STM32_ADC_CFGR1_EXTEN_FALL (2 << 10)
+#define STM32_ADC_CFGR1_EXTEN_BOTH (3 << 10)
+#define STM32_ADC_CFGR1_EXTEN_MASK (3 << 10)
+/* External trigger selection */
+#define STM32_ADC_CFGR1_TRG0 (0 << 6)
+#define STM32_ADC_CFGR1_TRG1 (1 << 6)
+#define STM32_ADC_CFGR1_TRG2 (2 << 6)
+#define STM32_ADC_CFGR1_TRG3 (3 << 6)
+#define STM32_ADC_CFGR1_TRG4 (4 << 6)
+#define STM32_ADC_CFGR1_TRG5 (5 << 6)
+#define STM32_ADC_CFGR1_TRG6 (6 << 6)
+#define STM32_ADC_CFGR1_TRG7 (7 << 6)
+#define STM32_ADC_CFGR1_TRG_MASK (7 << 6)
+/* Selects circular vs one-shot */
+#define STM32_ADC_CFGR1_DMACFG (1 << 1)
+#define STM32_ADC_CFGR1_DMAEN (1 << 0)
#define STM32_ADC_CFGR2 REG32(STM32_ADC1_BASE + 0x10)
+/* Sampling time selection - 1.5 ADC cycles min, 239.5 cycles max */
#define STM32_ADC_SMPR REG32(STM32_ADC1_BASE + 0x14)
+#define STM32_ADC_SMPR_1_5_CY 0x0
+#define STM32_ADC_SMPR_7_5_CY 0x1
+#define STM32_ADC_SMPR_13_5_CY 0x2
+#define STM32_ADC_SMPR_28_5_CY 0x3
+#define STM32_ADC_SMPR_41_5_CY 0x4
+#define STM32_ADC_SMPR_55_5_CY 0x5
+#define STM32_ADC_SMPR_71_5_CY 0x6
+#define STM32_ADC_SMPR_239_5_CY 0x7
#define STM32_ADC_TR REG32(STM32_ADC1_BASE + 0x20)
#define STM32_ADC_CHSELR REG32(STM32_ADC1_BASE + 0x28)
#define STM32_ADC_DR REG32(STM32_ADC1_BASE + 0x40)
diff --git a/common/adc.c b/common/adc.c
index 696b017788..10e9399bdb 100644
--- a/common/adc.c
+++ b/common/adc.c
@@ -11,6 +11,8 @@
#include "console.h"
#include "util.h"
+/* 'adc' console command is not supported in continuous mode */
+#ifndef CONFIG_ADC_PROFILE_FAST_CONTINUOUS
static enum adc_channel find_adc_channel_by_name(const char *name)
{
const struct adc_t *ch = adc_channels;
@@ -55,3 +57,4 @@ DECLARE_CONSOLE_COMMAND(adc, command_adc,
"[name]",
"Print ADC channel(s)",
NULL);
+#endif
diff --git a/include/adc.h b/include/adc.h
index f7fbb0d3ac..a42fc376ee 100644
--- a/include/adc.h
+++ b/include/adc.h
@@ -33,7 +33,9 @@ int adc_read_channel(enum adc_channel ch);
* Read all ADC channels.
*
* @param data Destination array for channel data; must be
- * ADC_CH_COUNT elements long.
+ * ADC_CH_COUNT elements long for single-read profile,
+ * or sized according to dma_buffer_size in
+ * continuous profiles.
*
* @return EC_SUCCESS, or non-zero on error.
*/
diff --git a/include/config.h b/include/config.h
index 1ca46f1d98..24daae1b34 100644
--- a/include/config.h
+++ b/include/config.h
@@ -84,13 +84,25 @@
/* Compile chip support for analog-to-digital convertor */
#undef CONFIG_ADC
-/* ADC sample time selection. The value is chip-dependent. */
+/*
+ * ADC sample time selection. The value is chip-dependent.
+ * TODO: Replace this with CONFIG_ADC_PROFILE entries.
+ */
#undef CONFIG_ADC_SAMPLE_TIME
/* Include the ADC analog watchdog feature in the ADC code */
#define CONFIG_ADC_WATCHDOG
/*
+ * Chip-dependent ADC configuration - select one.
+ * SINGLE - Sample all inputs once when requested.
+ * FAST_CONTINUOUS - Sample all inputs continuously using DMA, with minimal
+ * sample time.
+ */
+#define CONFIG_ADC_PROFILE_SINGLE
+#undef CONFIG_ADC_PROFILE_FAST_CONTINUOUS
+
+/*
* Some ALS modules may be connected to the EC. We need the command, and
* specific drivers for each module.
*/