summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVic Yang <victoryang@chromium.org>2013-03-13 08:48:03 +0800
committerChromeBot <chrome-bot@google.com>2013-03-13 12:11:59 -0700
commit2823e7e13208a70c7c1b0fce56758f17049a1658 (patch)
treeddc0d577600026026956a86abf6de4428c6a58a4
parenteefc0ac8c5e3b167d902997e28324e5f57e0a932 (diff)
downloadchrome-ec-2823e7e13208a70c7c1b0fce56758f17049a1658.tar.gz
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 Change-Id: Ia0c981fadd9bcaa40afe30ca2624c2f3644b95fe Signed-off-by: Vic Yang <victoryang@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/45277 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--chip/stm32/adc.c105
-rw-r--r--include/adc.h20
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 */