blob: 73893484668ae15f4893ae654329b990050ec3f9 [file] [log] [blame]
/* Copyright 2021 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* ADC drivers for STM32L4xx as well as STM32L5xx. */
#include "adc.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "dma.h"
#include "hooks.h"
#include "hwtimer.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
mutex_t adc_lock;
struct adc_profile_t {
/* Register values. */
uint32_t cfgr1_reg;
uint32_t cfgr2_reg;
uint32_t smpr_reg; /* Default Sampling Rate */
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
#ifndef CONFIG_ADC_SAMPLE_TIME
#define CONFIG_ADC_SAMPLE_TIME STM32_ADC_SMPR_12_5_CY
#endif
#endif
#define ADC_CALIBRATION_TIMEOUT_US 100000U
#define ADC_ENABLE_TIMEOUT_US 200000U
#define ADC_CONVERSION_TIMEOUT_US 200000U
static uint8_t adc1_initialized;
#ifdef CONFIG_ADC_PROFILE_FAST_CONTINUOUS
#error "Continuous ADC sampling not implemented for STM32L4/5"
#ifndef CONFIG_ADC_SAMPLE_TIME
#define CONFIG_ADC_SAMPLE_TIME STM32_ADC_SMPR_1_5_CY
#endif
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 = CONFIG_ADC_SAMPLE_TIME,
/* 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_init(void)
{
/*
* If clock is already enabled, and ADC module is enabled
* then this is a warm reboot and ADC is already initialized.
*/
if (STM32_RCC_AHB2ENR & STM32_RCC_AHB2ENR_ADCEN &&
(STM32_ADC1_CR & STM32_ADC1_CR_ADEN))
return;
/* Enable ADC clock */
clock_enable_module(MODULE_ADC, 1);
/* Set ADC clock to 1/4 of SYSCLK (CPU_CLOCK) */
STM32_ADC1_CCR &= ~0x003C0000;
STM32_ADC1_CCR |= 0x00080000;
STM32_RCC_AHB2ENR |= STM32_RCC_HB2_GPIOA;
STM32_RCC_AHB2ENR |= STM32_RCC_HB2_GPIOB;
/* Set ADC data resolution */
STM32_ADC1_CFGR &= ~STM32_ADC1_CFGR_CONT;
/* Set ADC conversion data alignment */
STM32_ADC1_CFGR &= ~STM32_ADC1_CFGR_ALIGN;
/* Set ADC delayed conversion mode */
STM32_ADC1_CFGR &= ~STM32_ADC1_CFGR_AUTDLY;
}
BUILD_ASSERT(CONFIG_ADC_SAMPLE_TIME > 0 && CONFIG_ADC_SAMPLE_TIME <= 8);
static void adc_configure_channel(int ain_id, enum stm32_adc_smpr sample_time)
{
/* Select Sampling time for channel to convert */
if (sample_time == STM32_ADC_SMPR_DEFAULT)
sample_time = CONFIG_ADC_SAMPLE_TIME;
if (ain_id <= 10) {
STM32_ADC1_SMPR1 &= ~(7 << ((ain_id - 1) * 3));
STM32_ADC1_SMPR1 |= ((sample_time - 1) << ((ain_id - 1) * 3));
} else {
STM32_ADC1_SMPR2 &= ~(7 << ((ain_id - 11) * 3));
STM32_ADC1_SMPR2 |= ((sample_time - 1) << ((ain_id - 11) * 3));
}
}
static void adc_select_channel(int ain_id)
{
/* Setup an "injected sequence" consisting of only this one channel. */
STM32_ADC1_JSQR = ain_id << 8;
}
static void stm32_adc1_isr_clear(uint32_t bitmask)
{
/* Write 1 to clear */
STM32_ADC1_ISR = bitmask;
}
int adc_read_channel(enum adc_channel ch)
{
const struct adc_t *adc = adc_channels + ch;
int value = 0;
uint32_t wait_loop_index;
mutex_lock(&adc_lock);
if (adc1_initialized == 0) {
adc_init();
/* Configure Channel N */
for (uint8_t i = 0; i < ADC_CH_COUNT; i++) {
const struct adc_t *adc = adc_channels + i;
adc_configure_channel(adc->channel, adc->sample_time);
}
/* Disable DMA */
STM32_ADC1_CFGR &= ~STM32_ADC1_CFGR_DMAEN;
if ((STM32_ADC1_CR & STM32_ADC1_CR_ADEN) !=
STM32_ADC1_CR_ADEN) {
/* Disable ADC deep power down (enabled by default after
* reset state)
*/
STM32_ADC1_CR &= ~STM32_ADC1_CR_DEEPPWD;
/* Enable ADC internal voltage regulator */
STM32_ADC1_CR |= STM32_ADC1_CR_ADVREGEN;
}
/* Delay for ADC internal voltage regulator stabilization. */
udelay(20);
/* Run ADC self calibration */
STM32_ADC1_CR |= STM32_ADC1_CR_ADCAL;
/* wait for the end of calibration */
wait_loop_index = ((ADC_CALIBRATION_TIMEOUT_US *
(CPU_CLOCK / (100000 * 2))) /
10);
while (STM32_ADC1_CR & STM32_ADC1_CR_ADCAL) {
if (wait_loop_index-- == 0)
break;
}
/*
* At least four ADC clock cycles must pass between end of
* calibration and enabling the ADC. 1us delay will be enough
* as long as ADC clock is at least 4MHz, that is SYSCLK
* (CPU_CLOCK) is at least 16MHz.
*/
udelay(1);
/* Enable ADC */
stm32_adc1_isr_clear(STM32_ADC1_ISR_ADRDY);
STM32_ADC1_CR |= STM32_ADC1_CR_ADEN;
wait_loop_index =
((ADC_ENABLE_TIMEOUT_US * (CPU_CLOCK / (100000 * 2))) /
10);
while (!(STM32_ADC1_ISR & STM32_ADC1_ISR_ADRDY)) {
wait_loop_index--;
if (wait_loop_index == 0)
break;
}
stm32_adc1_isr_clear(STM32_ADC1_ISR_ADRDY);
adc1_initialized = 1;
}
/* Configure Injected Channel N */
adc_select_channel(adc->channel);
/* Start injected conversion */
STM32_ADC1_CR |= BIT(3); /* JADSTART */
/* Wait for end of injected conversion */
wait_loop_index =
((ADC_CONVERSION_TIMEOUT_US * (CPU_CLOCK / (100000 * 2))) / 10);
while (!(STM32_ADC1_ISR & BIT(6))) {
if (wait_loop_index-- == 0)
break;
}
/* Clear JEOS bit */
stm32_adc1_isr_clear(BIT(6));
/* read converted value */
value = STM32_ADC1_JDR1;
mutex_unlock(&adc_lock);
return value * adc->factor_mul / adc->factor_div + adc->shift;
}
void adc_disable(void)
{
/* Disable ADC */
/* Do not Set ADDIS when ADC is disabled */
adc1_initialized = 0;
if (STM32_ADC1_CR & STM32_ADC1_CR_ADEN)
STM32_ADC1_CR |= STM32_ADC1_CR_ADDIS;
/*
* Note that the ADC is not in OFF state immediately.
* Once the ADC is effectively put into OFF state,
* STM32_ADC_CR_ADDIS bit will be cleared by hardware.
*/
}