summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVic Yang <victoryang@chromium.org>2012-09-24 17:07:19 +0800
committerGerrit <chrome-bot@google.com>2012-09-26 08:58:59 -0700
commit1b14b2c922808ac1f7084f301d54173ca0074bf7 (patch)
treee2caa3a413513daf3dbb4bca4bfe65992755ab97
parentcfcd1412eae4f9f14076d357106709372ca6dc0b (diff)
downloadchrome-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.c12
-rw-r--r--board/spring/board.h10
-rw-r--r--chip/stm32/adc.c198
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/registers.h24
-rw-r--r--chip/stm32/stm32_adc.h23
-rw-r--r--include/adc.h3
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 */