/* Copyright (c) 2014 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.
 */
/* Hardware initialization and common functions */

#include "adc.h"
#include "adc_chip.h"
#include "common.h"
#include "cpu.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"

static void system_init(void)
{
	/* Enable access to RCC CSR register and RTC backup registers */
	STM32_PWR_CR |= 1 << 8;

	/* switch on LSI */
	STM32_RCC_CSR |= 1 << 0;
	/* Wait for LSI to be ready */
	while (!(STM32_RCC_CSR & (1 << 1)))
		;
	/* re-configure RTC if needed */
	if ((STM32_RCC_BDCR & 0x00018300) != 0x00008200) {
		/* the RTC settings are bad, we need to reset it */
		STM32_RCC_BDCR |= 0x00010000;
		/* Enable RTC and use LSI as clock source */
		STM32_RCC_BDCR = (STM32_RCC_BDCR & ~0x00018300) | 0x00008200;
	}
}

static void power_init(void)
{
	/* enable SYSCFG, COMP, ADC, SPI1, USART1 */
	STM32_RCC_APB2ENR = 0x00005201;
	/* enable TIM2, TIM3, TIM14, PWR */
	STM32_RCC_APB1ENR = 0x10000103;
	/* enable DMA, SRAM, CRC, GPA, GPB, GPF */
	STM32_RCC_AHBENR  = 0x460045;
}

/* GPIO setting helpers */
#define OUT(n) (1 << ((n) * 2))
#define AF(n) (2 << ((n) * 2))
#define ANALOG(n) (3 << ((n) * 2))
#define HIGH(n) (1 << (n))
#define ODR(n) (1 << (n))
#define HISPEED(n) (3 << ((n) * 2))
#define AFx(n, x) (x << (((n) % 8) * 4))

static void pins_init(void)
{
	/* Pin usage:
	 * PA0  (OUT - GPIO)       : Wakeup on Vnc / Threshold
	 * PA1  (ANALOG - ADC_IN1) : CC sense
	 * PA2  (ANALOG - ADC_IN2) : Current sense
	 * PA3  (ANALOG - ADC_IN3) : Voltage sense
	 * PA4  (OUT - OD GPIO)    : PD TX enable
	 * PA5  (AF0 - SPI1_SCK)   : TX clock in
	 * PA6  (AF0 - SPI1_MISO)  : PD TX
	 * PA7  (AF5 - TIM3_CH2)   : PD RX
	 * PA9  (AF1 - UART1_TX)   : [DEBUG] UART TX
	 * PA10 (AF1 - UART1_RX)   : [DEBUG] UART RX
	 * PA13 (OUT - GPIO)       : voltage select[0]
	 * PA14 (OUT - GPIO)       : voltage select[1]
	 * PB1  (AF0 - TIM14_CH1)  : TX clock out
	 * PF0  (OUT - GPIO)       : LM5050 FET driver off
	 * PF1  (OUT - GPIO)       : discharge FET
	 */

	/*
	 * Clear power control/status register to disable wakeup
	 * pin A0, so that we can change it to an output.
	 */
	STM32_PWR_CSR = 0;
	STM32_PWR_CR |= 0xc;

	STM32_GPIO_ODR(GPIO_A) = HIGH(0) | HIGH(4);
	STM32_GPIO_AFRL(GPIO_A) = AFx(7, 1);
	STM32_GPIO_AFRH(GPIO_A) = AFx(9, 1) | AFx(10, 1);
	STM32_GPIO_OTYPER(GPIO_A) = ODR(4);
	STM32_GPIO_OSPEEDR(GPIO_A) = HISPEED(5) | HISPEED(6) | HISPEED(7);
	STM32_GPIO_MODER(GPIO_A) = OUT(0) | ANALOG(1) | ANALOG(2) | ANALOG(3)
				 | OUT(4) | AF(5) /*| AF(6)*/ | AF(7) | AF(9)
				 | AF(10) | OUT(13) | OUT(14);
	/* set PF0 / PF1 as output */
	STM32_GPIO_ODR(GPIO_F) = 0;
	STM32_GPIO_MODER(GPIO_F) = OUT(0) | OUT(1);
	STM32_GPIO_OTYPER(GPIO_F) = 0;

	/* Set PB1 as AF0 (TIM14_CH1) */
	STM32_GPIO_OSPEEDR(GPIO_B) = HISPEED(1);
	STM32_GPIO_MODER(GPIO_B) = AF(1);
}

static void adc_init(void)
{
	/* Only do the calibration if the ADC is off  */
	if (!(STM32_ADC_CR & 1)) {
		/* ADC calibration */
		STM32_ADC_CR = STM32_ADC_CR_ADCAL; /* set ADCAL = 1, ADC off */
		/* wait for the end of calibration */
		while (STM32_ADC_CR & STM32_ADC_CR_ADCAL)
			;
	}
	/* Single conversion, right aligned, 12-bit */
	STM32_ADC_CFGR1 = 1 << 12; /* (1 << 15) => AUTOOFF */;
	/* clock is ADCCLK (ADEN must be off when writing this reg) */
	STM32_ADC_CFGR2 = 0;
	/* Sampling time : 71.5 ADC clock cycles, about 5us */
	STM32_ADC_SMPR = 6;

	/*
	 * ADC enable (note: takes 4 ADC clocks between end of calibration
	 * and setting ADEN).
	 */
	STM32_ADC_CR = STM32_ADC_CR_ADEN;
	while (!(STM32_ADC_ISR & STM32_ADC_ISR_ADRDY))
		STM32_ADC_CR = STM32_ADC_CR_ADEN;
	/* Disable interrupts */
	STM32_ADC_IER = 0;
	/* Analog watchdog IRQ */
	task_enable_irq(STM32_IRQ_ADC_COMP);
}

static void uart_init(void)
{
	/* set baudrate */
	STM32_USART_BRR(UARTN_BASE) =
		DIV_ROUND_NEAREST(CPU_CLOCK, CONFIG_UART_BAUD_RATE);
	/* UART enabled, 8 Data bits, oversampling x16, no parity */
	STM32_USART_CR1(UARTN_BASE) =
		STM32_USART_CR1_UE | STM32_USART_CR1_TE | STM32_USART_CR1_RE;
	/* 1 stop bit, no fancy stuff */
	STM32_USART_CR2(UARTN_BASE) = 0x0000;
	/* DMA disabled, special modes disabled, error interrupt disabled */
	STM32_USART_CR3(UARTN_BASE) = 0x0000;
}

static void timers_init(void)
{
	/* TIM2 is a 32-bit free running counter with 1Mhz frequency */
	STM32_TIM_CR2(2) = 0x0000;
	STM32_TIM32_ARR(2) = 0xFFFFFFFF;
	STM32_TIM_PSC(2) = CPU_CLOCK / 1000000 - 1;
	STM32_TIM_EGR(2) = 0x0001; /* Reload the pre-scaler */
	STM32_TIM_CR1(2) = 1;
	STM32_TIM32_CNT(2) = 0x00000000;
	STM32_TIM_SR(2) = 0; /* Clear pending interrupts */
	STM32_TIM_DIER(2) = 1; /* Overflow interrupt */
	task_enable_irq(STM32_IRQ_TIM2);
}

static void irq_init(void)
{
	/* clear all pending interrupts */
	CPU_NVIC_UNPEND(0) = 0xffffffff;
	/* enable global interrupts */
	asm("cpsie i");
}

extern void runtime_init(void);
void hardware_init(void)
{
	uint32_t raw_cause = STM32_RCC_CSR;
	uint32_t pwr_status = STM32_PWR_CSR;

	power_init();

	/* Clear the hardware reset cause by setting the RMVF bit */
	STM32_RCC_CSR |= 1 << 24;
	/* Clear SBF in PWR_CSR */
	STM32_PWR_CR |= 1 << 3;

	/*
	 * WORKAROUND: as we cannot de-activate the watchdog during
	 * long hibernation, we are woken-up once by the watchdog and
	 * go back to hibernate if we detect that condition, without
	 * watchdog initialized this time.
	 * The RTC deadline (if any) is already set.
	 */
	if ((pwr_status & 0x2) && (raw_cause & 0x60000000))
		__enter_hibernate(0, 0);

	system_init();
	runtime_init(); /* sets clock */
	pins_init();
	uart_init();
	timers_init();
	watchdog_init();
	adc_init();
	irq_init();
}

static int watchdog_ain_id, watchdog_ain_high, watchdog_ain_low;

static int adc_enable_last_watchdog(void)
{
	return adc_enable_watchdog(watchdog_ain_id, watchdog_ain_high,
			    watchdog_ain_low);
}

static inline int adc_watchdog_enabled(void)
{
	return STM32_ADC_CFGR1 & (1 << 23);
}

int adc_read_channel(enum adc_channel ch)
{
	int value;
	int watchdog_enabled = adc_watchdog_enabled();

	if (watchdog_enabled)
		adc_disable_watchdog();

	/* Select channel to convert */
	STM32_ADC_CHSELR = 1 << ch;
	/* Clear flags */
	STM32_ADC_ISR = 0x8e;
	/* Start conversion */
	STM32_ADC_CR |= 1 << 2; /* ADSTART */
	/* Wait for end of conversion */
	while (!(STM32_ADC_ISR & (1 << 2)))
		;
	/* read converted value */
	value = STM32_ADC_DR;

	if (watchdog_enabled)
		adc_enable_last_watchdog();

	return value;
}

int adc_enable_watchdog(int ch, int high, int low)
{
	/* store last watchdog setup */
	watchdog_ain_id = ch;
	watchdog_ain_high = high;
	watchdog_ain_low = low;

	/* Set thresholds */
	STM32_ADC_TR = ((high & 0xfff) << 16) | (low & 0xfff);
	/* Select channel to convert */
	STM32_ADC_CHSELR = 1 << ch;
	/* Clear flags */
	STM32_ADC_ISR = 0x8e;
	/* Set Watchdog enable bit on a single channel / continuous mode */
	STM32_ADC_CFGR1 = (ch << 26) | (1 << 23) | (1 << 22)
			|  (1 << 13) | (1 << 12);
	/* Enable watchdog interrupt */
	STM32_ADC_IER = 1 << 7;
	/* Start continuous conversion */
	STM32_ADC_CR |= 1 << 2; /* ADSTART */

	return EC_SUCCESS;
}

int adc_disable_watchdog(void)
{
	/* Stop on-going conversion */
	STM32_ADC_CR |= 1 << 4; /* ADSTP */
	/* Wait for conversion to stop */
	while (STM32_ADC_CR & (1 << 4))
		;
	/* CONT=0 -> continuous mode off / Clear Watchdog enable */
	STM32_ADC_CFGR1 = 1 << 12;
	/* Disable interrupt */
	STM32_ADC_IER = 0;
	/* Clear flags */
	STM32_ADC_ISR = 0x8e;

	return EC_SUCCESS;
}

/* ---- flash handling ---- */

/*
 * Approximate number of CPU cycles per iteration of the loop when polling
 * the flash status
 */
#define CYCLE_PER_FLASH_LOOP 10

/* Flash page programming timeout.  This is 2x the datasheet max. */
#define FLASH_TIMEOUT_US 16000
#define FLASH_TIMEOUT_LOOP \
	(FLASH_TIMEOUT_US * (CPU_CLOCK / SECOND) / CYCLE_PER_FLASH_LOOP)

/* Flash unlocking keys */
#define KEY1    0x45670123
#define KEY2    0xCDEF89AB

/* Lock bits for FLASH_CR register */
#define PG       (1<<0)
#define PER      (1<<1)
#define OPTPG    (1<<4)
#define OPTER    (1<<5)
#define STRT     (1<<6)
#define CR_LOCK  (1<<7)
#define OPTWRE   (1<<9)

int flash_physical_write(int offset, int size, const char *data)
{
	uint16_t *address = (uint16_t *)(CONFIG_PROGRAM_MEMORY_BASE + offset);
	int res = EC_SUCCESS;
	int i;

	if ((uint32_t)address > CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_SIZE)
		return EC_ERROR_INVAL;

	/* unlock CR if needed */
	if (STM32_FLASH_CR & CR_LOCK) {
		STM32_FLASH_KEYR = KEY1;
		STM32_FLASH_KEYR = KEY2;
	}

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;
	/* set the ProGram bit */
	STM32_FLASH_CR |= PG;

	for (; size > 0; size -= sizeof(uint16_t)) {
		/* wait to be ready  */
		for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
		     i++)
			;
		/* write the half word */
		*address++ = data[0] + (data[1] << 8);
		data += 2;
		/* Wait for writes to complete */
		for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
		     i++)
			;
		if (i == FLASH_TIMEOUT_LOOP) {
			res = EC_ERROR_TIMEOUT;
			goto exit_wr;
		}
		/* Check for error conditions - erase failed, voltage error,
		 * protection error */
		if (STM32_FLASH_SR & 0x14) {
			res = EC_ERROR_UNKNOWN;
			goto exit_wr;
		}
	}

exit_wr:
	STM32_FLASH_CR &= ~PG;
	STM32_FLASH_CR = CR_LOCK;

	return res;
}

int flash_physical_erase(int offset, int size)
{
	int res = EC_SUCCESS;

	/* unlock CR if needed */
	if (STM32_FLASH_CR & CR_LOCK) {
		STM32_FLASH_KEYR = KEY1;
		STM32_FLASH_KEYR = KEY2;
	}

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;
	/* set PER bit */
	STM32_FLASH_CR |= PER;

	for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
	     offset += CONFIG_FLASH_ERASE_SIZE) {
		int i;
		/* select page to erase */
		STM32_FLASH_AR = CONFIG_PROGRAM_MEMORY_BASE + offset;
		/* set STRT bit : start erase */
		STM32_FLASH_CR |= STRT;


		/* Wait for erase to complete */
		for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
		     i++)
			;
		if (i == FLASH_TIMEOUT_LOOP) {
			res = EC_ERROR_TIMEOUT;
			goto exit_er;
		}

		/*
		 * Check for error conditions - erase failed, voltage error,
		 * protection error
		 */
		if (STM32_FLASH_SR & 0x14) {
			res = EC_ERROR_UNKNOWN;
			goto exit_er;
		}
	}

exit_er:
	STM32_FLASH_CR &= ~PER;
	STM32_FLASH_CR = CR_LOCK;

	return res;
}

static void unlock_erase_optb(void)
{
	int i;

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;

	/* wait to be ready  */
	for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++)
		;

	/* Unlock the option bytes access */
	if (STM32_FLASH_CR & CR_LOCK) {
		STM32_FLASH_KEYR = KEY1;
		STM32_FLASH_KEYR = KEY2;
	}
	if (!(STM32_FLASH_CR & OPTWRE)) {
		STM32_FLASH_OPTKEYR = KEY1;
		STM32_FLASH_OPTKEYR = KEY2;
	}
	/* Must be set in 2 separate lines. */
	STM32_FLASH_CR |= OPTER;
	STM32_FLASH_CR |= STRT;

	/* wait to be ready  */
	for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++)
		;
	/* reset erasing bits */
	STM32_FLASH_CR = OPTWRE;
}


static void write_optb(int byte, uint8_t value)
{
	volatile int16_t *hword = (uint16_t *)(STM32_OPTB_BASE + byte);
	int i;

	/* Clear previous error status */
	STM32_FLASH_SR = 0x34;

	/* set OPTPG bit */
	STM32_FLASH_CR |= OPTPG;

	*hword = ((~value) << STM32_OPTB_COMPL_SHIFT) | value;

	/* reset OPTPG bit */
	STM32_FLASH_CR = OPTWRE;

	/* wait to be ready  */
	for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP); i++)
		;
}

void flash_physical_permanent_protect(void)
{
	unlock_erase_optb();
	/* protect the 16KB RO partition against write/erase in WRP0 */
	write_optb(8, 0xF0);
	/* Set RDP to level 1 to prevent disabling the protection */
	write_optb(0, 0x11);
	/* Reset by using OBL_LAUNCH to take changes into account */
	asm volatile("cpsid i");
	STM32_FLASH_CR |= STM32_FLASH_CR_OBL_LAUNCH;
	/* Spin and wait for reboot; should never return */
	while (1)
		;
}

int flash_physical_is_permanently_protected(void)
{
	/* if RDP is still at level 0, the flash protection is not in place */
	return (STM32_FLASH_OBR & STM32_FLASH_OBR_RDP_MASK) &&
		/* the low 16KB (RO partition) are write-protected */
		!(STM32_FLASH_WRPR & 0xF);
}
