From 5dc8498fb690ed575c3d2132a5e9ee39aa5e235a Mon Sep 17 00:00:00 2001 From: Vic Yang Date: Wed, 13 Mar 2013 08:48:03 +0800 Subject: stm32f: Add support for ADC watchdog This makes it possible to have ADC module monitor an input voltage and trigger interrupt when it goes out of accepted range. BUG=chrome-os-partner:18171 TEST=Manual BRANCH=spring Original-Change-Id: Ia0c981fadd9bcaa40afe30ca2624c2f3644b95fe Signed-off-by: Vic Yang Reviewed-on: https://gerrit.chromium.org/gerrit/45277 Reviewed-by: Vincent Palatin (cherry picked from commit 2823e7e13208a70c7c1b0fce56758f17049a1658) Change-Id: Ibc38af6348b9d79344f513ba17220aa1de01ca94 Signed-off-by: Vic Yang Reviewed-on: https://gerrit.chromium.org/gerrit/45394 Reviewed-by: Vincent Palatin Commit-Queue: Vincent Palatin Tested-by: Vincent Palatin --- chip/stm32/adc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- include/adc.h | 20 +++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/chip/stm32/adc.c b/chip/stm32/adc.c index 835f700e23..b1545dac20 100644 --- a/chip/stm32/adc.c +++ b/chip/stm32/adc.c @@ -16,6 +16,8 @@ struct mutex adc_lock; +static int watchdog_ain_id; + static const struct dma_option dma_adc_option = { DMAC_ADC, (void *)&STM32_ADC_DR, DMA_MSIZE_HALF_WORD | DMA_PSIZE_HALF_WORD @@ -81,16 +83,99 @@ static inline int adc_conversion_ended(void) return STM32_ADC_SR & (1 << 1); } +static int adc_watchdog_enabled(void) +{ + return STM32_ADC_CR1 & (1 << 23); +} + +static int adc_enable_watchdog_no_lock(void) +{ + /* Fail if watchdog already enabled */ + if (adc_watchdog_enabled()) + return EC_ERROR_UNKNOWN; + + /* Set channel */ + STM32_ADC_CR1 = (STM32_ADC_CR1 & ~0x1f) | watchdog_ain_id; + + /* AWDSGL=1, SCAN=1, AWDIE=1, AWDEN=1 */ + STM32_ADC_CR1 |= (1 << 9) | (1 << 8) | (1 << 6) | (1 << 23); + + /* Disable DMA */ + STM32_ADC_CR2 &= ~(1 << 8); + + /* CONT=1 */ + STM32_ADC_CR2 |= (1 << 1); + + /* Start conversion */ + STM32_ADC_CR2 |= (1 << 0); + + return EC_SUCCESS; +} + +int adc_enable_watchdog(int ain_id, int high, int low) +{ + int ret; + + if (!adc_powered()) + return EC_ERROR_UNKNOWN; + + mutex_lock(&adc_lock); + + watchdog_ain_id = ain_id; + + /* Set thresholds */ + STM32_ADC_HTR = high & 0xfff; + STM32_ADC_LTR = low & 0xfff; + + ret = adc_enable_watchdog_no_lock(); + mutex_unlock(&adc_lock); + return ret; +} + +static int adc_disable_watchdog_no_lock(void) +{ + /* Fail if watchdog not running */ + if (!adc_watchdog_enabled()) + return EC_ERROR_UNKNOWN; + + /* AWDEN=0 */ + STM32_ADC_CR1 &= ~(1 << 23); + + /* CONT=0 */ + STM32_ADC_CR2 &= ~(1 << 1); + + return EC_SUCCESS; +} + +int adc_disable_watchdog(void) +{ + int ret; + + if (!adc_powered()) + return EC_ERROR_UNKNOWN; + + mutex_lock(&adc_lock); + ret = adc_disable_watchdog_no_lock(); + mutex_unlock(&adc_lock); + return ret; +} + int adc_read_channel(enum adc_channel ch) { const struct adc_t *adc = adc_channels + ch; int value; + int restore_watchdog = 0; if (!adc_powered()) return EC_ERROR_UNKNOWN; mutex_lock(&adc_lock); + if (adc_watchdog_enabled()) { + restore_watchdog = 1; + adc_disable_watchdog_no_lock(); + } + adc_configure(adc->channel); /* Clear EOC bit */ @@ -104,6 +189,9 @@ int adc_read_channel(enum adc_channel ch) ; value = STM32_ADC_DR & ADC_READ_MAX; + if (restore_watchdog) + adc_enable_watchdog_no_lock(); + mutex_unlock(&adc_lock); return value * adc->factor_mul / adc->factor_div + adc->shift; } @@ -113,12 +201,19 @@ int adc_read_all_channels(int *data) int i; int16_t raw_data[ADC_CH_COUNT]; const struct adc_t *adc; + int restore_watchdog = 0; + int ret = EC_SUCCESS; if (!adc_powered()) return EC_ERROR_UNKNOWN; mutex_lock(&adc_lock); + if (adc_watchdog_enabled()) { + restore_watchdog = 1; + adc_disable_watchdog_no_lock(); + } + adc_configure_all(); dma_start_rx(&dma_adc_option, ADC_CH_COUNT, raw_data); @@ -127,8 +222,8 @@ int adc_read_all_channels(int *data) STM32_ADC_CR2 |= (1 << 0); /* ADON */ if (dma_wait(DMAC_ADC)) { - mutex_unlock(&adc_lock); - return EC_ERROR_UNKNOWN; + ret = EC_ERROR_UNKNOWN; + goto exit_all_channels; } for (i = 0; i < ADC_CH_COUNT; ++i) { @@ -137,8 +232,12 @@ int adc_read_all_channels(int *data) adc->shift; } +exit_all_channels: + if (restore_watchdog) + adc_enable_watchdog_no_lock(); + mutex_unlock(&adc_lock); - return EC_SUCCESS; + return ret; } static void adc_init(void) diff --git a/include/adc.h b/include/adc.h index 646298ca76..715852cd76 100644 --- a/include/adc.h +++ b/include/adc.h @@ -29,4 +29,24 @@ int adc_read_channel(enum adc_channel ch); */ int adc_read_all_channels(int *data); +/** + * Enable ADC watchdog. Note that interrupts might come in repeatedly very + * quickly when ADC output goes out of the accepted range. + * + * @param ain_id The AIN to be watched by the watchdog. + * @param high The high threshold that the watchdog would trigger + * an interrupt when exceeded. + * @param low The low threshold. + * + * @return EC_SUCCESS, or non-zero if any error. + */ +int adc_enable_watchdog(int ain_id, int high, int low); + +/** + * Disable ADC watchdog. + * + * @return EC_SUCCESS, or non-zero if any error. + */ +int adc_disable_watchdog(void); + #endif /* __CROS_EC_ADC_H */ -- cgit v1.2.1