coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/stm32/adc-stm32l.c

172 lines
3.3 KiB
C
Raw Normal View History

2024-03-04 11:14:53 +01:00
/* Copyright 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 "adc_chip.h"
#include "common.h"
#include "console.h"
#include "clock.h"
#include "dma.h"
#include "hooks.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#define ADC_SINGLE_READ_TIMEOUT 3000 /* 3 ms */
struct mutex adc_lock;
static int restore_clock;
static inline void adc_set_channel(int sample_id, int channel)
{
uint32_t mask, val;
volatile uint32_t *sqr_reg;
int reg_id;
reg_id = 5 - sample_id / 6;
mask = 0x1f << ((sample_id % 6) * 5);
val = channel << ((sample_id % 6) * 5);
sqr_reg = &STM32_ADC_SQR(reg_id);
*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 &= ~BIT(8);
/* Disable scan mode */
STM32_ADC_CR1 &= ~BIT(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 |= BIT(8);
/* Enable scan mode */
STM32_ADC_CR1 |= BIT(8);
}
static inline int adc_powered(void)
{
return STM32_ADC_SR & BIT(6); /* ADONS */
}
static void adc_enable_clock(void)
{
STM32_RCC_APB2ENR |= BIT(9);
/* ADCCLK = HSI / 2 = 8MHz*/
STM32_ADC_CCR |= BIT(16);
}
static void adc_init(void)
{
/*
* For STM32L, ADC clock source is HSI/2 = 8 MHz. HSI must be enabled
* for ADC.
*
* Note that we are not powering on ADC on EC initialization because
* STM32L ADC module requires HSI clock. Instead, ADC module is powered
* on/off in adc_prepare()/adc_release().
*/
/* Enable ADC clock. */
adc_enable_clock();
if (!adc_powered())
/* Power on ADC module */
STM32_ADC_CR2 |= BIT(0); /* ADON */
/* Set right alignment */
STM32_ADC_CR2 &= ~BIT(11);
/*
* Set sample time of all channels to 16 cycles.
* Conversion takes (12+16)/8M = 3.34 us.
*/
STM32_ADC_SMPR1 = 0x24924892;
STM32_ADC_SMPR2 = 0x24924892;
STM32_ADC_SMPR3 = 0x24924892;
}
static void adc_prepare(void)
{
if (!adc_powered()) {
clock_enable_module(MODULE_ADC, 1);
adc_init();
restore_clock = 1;
}
}
static void adc_release(void)
{
if (restore_clock) {
clock_enable_module(MODULE_ADC, 0);
restore_clock = 0;
}
/*
* Power down the ADC. The ADC consumes a non-trivial amount of power,
* so it's wasteful to leave it on.
*/
if (adc_powered())
STM32_ADC_CR2 = 0;
}
static inline int adc_conversion_ended(void)
{
return STM32_ADC_SR & BIT(1);
}
int adc_read_channel(enum adc_channel ch)
{
const struct adc_t *adc = adc_channels + ch;
int value;
timestamp_t deadline;
mutex_lock(&adc_lock);
adc_prepare();
adc_configure(adc->channel);
/* Clear EOC bit */
STM32_ADC_SR &= ~BIT(1);
/* Start conversion */
STM32_ADC_CR2 |= BIT(30); /* SWSTART */
/* Wait for EOC bit set */
deadline.val = get_time().val + ADC_SINGLE_READ_TIMEOUT;
value = ADC_READ_ERROR;
do {
if (adc_conversion_ended()) {
value = STM32_ADC_DR & ADC_READ_MAX;
break;
}
} while (!timestamp_expired(deadline, NULL));
adc_release();
mutex_unlock(&adc_lock);
return (value == ADC_READ_ERROR) ? ADC_READ_ERROR :
value * adc->factor_mul / adc->factor_div + adc->shift;
}