text: /* 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 "adc_chip.h"
  #include "common.h"
  #include "console.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 */
  
  #define SMPR1_EXPAND(v) ((v) | ((v) << 3) | ((v) << 6) | ((v) << 9) | \
  			 ((v) << 12) | ((v) << 15) | ((v) << 18) | \
  			 ((v) << 21))
  #define SMPR2_EXPAND(v) (SMPR1_EXPAND(v) | ((v) << 24) | ((v) << 27))
  
  /* Default ADC sample time = 13.5 cycles */
  #ifndef CONFIG_ADC_SAMPLE_TIME
  #define CONFIG_ADC_SAMPLE_TIME 2
  #endif
  
  struct mutex adc_lock;
  
  static int watchdog_ain_id;
  
  static const struct dma_option dma_adc_option = {
  	STM32_DMAC_ADC, (void *)&STM32_ADC_DR,
  	STM32_DMA_CCR_MSIZE_16_BIT | STM32_DMA_CCR_PSIZE_16_BIT,
  };
  
  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 __attribute__((unused)) 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);
  }
  
  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_SQR3 = watchdog_ain_id;
  	STM32_ADC_SQR1 = 0;
  	STM32_ADC_CR1 = (STM32_ADC_CR1 & ~0x1f) | watchdog_ain_id;
  
  	/* Clear interrupt bit */
  	STM32_ADC_SR &= ~0x1;
  
  	/* 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, AWDIE=0 */
  	STM32_ADC_CR1 &= ~(1 << 23) & ~(1 << 6);
  
  	/* 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;
  	timestamp_t deadline;
  
  	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 */
  	STM32_ADC_SR &= ~(1 << 1);
  
  	/* Start conversion */
  	STM32_ADC_CR2 |= (1 << 0); /* ADON */
  
  	/* 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));
  
  	if (restore_watchdog)
  		adc_enable_watchdog_no_lock();
  
  	mutex_unlock(&adc_lock);
  	return (value == ADC_READ_ERROR) ? ADC_READ_ERROR :
  	       value * adc->factor_mul / adc->factor_div + adc->shift;
  }
  
  static void 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);
  
  	/*
  	 * ADC clock is divided with respect to AHB, so no delay needed
  	 * here. If ADC clock is the same as AHB, a dummy read on ADC
  	 * register is needed here.
  	 */
  
  	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 */
  	STM32_ADC_SMPR1 = SMPR1_EXPAND(CONFIG_ADC_SAMPLE_TIME);
  	STM32_ADC_SMPR2 = SMPR2_EXPAND(CONFIG_ADC_SAMPLE_TIME);
  }
  DECLARE_HOOK(HOOK_INIT, adc_init, HOOK_PRIO_INIT_ADC);
  