| /* 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 "sha1.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| #include "watchdog.h" |
| |
| static void clock_init(void) |
| { |
| /* put 1 Wait-State for flash access to ensure proper reads at 48Mhz */ |
| STM32_FLASH_ACR = 0x1001; /* 1 WS / Prefetch enabled */ |
| |
| /* Ensure that HSI8 is ON */ |
| if (!(STM32_RCC_CR & (1 << 1))) { |
| /* Enable HSI */ |
| STM32_RCC_CR |= 1 << 0; |
| /* Wait for HSI to be ready */ |
| while (!(STM32_RCC_CR & (1 << 1))) |
| ; |
| } |
| /* PLLSRC = HSI, PLLMUL = x12 (x HSI/2) = 48Mhz */ |
| STM32_RCC_CFGR = 0x00288000; |
| /* Enable PLL */ |
| STM32_RCC_CR |= 1 << 24; |
| /* Wait for PLL to be ready */ |
| while (!(STM32_RCC_CR & (1 << 25))) |
| ; |
| |
| /* switch SYSCLK to PLL */ |
| STM32_RCC_CFGR = 0x00288002; |
| /* wait until the PLL is the clock source */ |
| while ((STM32_RCC_CFGR & 0xc) != 0x8) |
| ; |
| } |
| |
| 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 |
| */ |
| 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 = 1 << 31; /* set ADCAL = 1, ADC off */ |
| /* wait for the end of calibration */ |
| while (STM32_ADC_CR & (1 << 31)) |
| ; |
| } |
| /* ADC enabled */ |
| STM32_ADC_CR = 1 << 0; |
| /* Single conversion, right aligned, 12-bit */ |
| STM32_ADC_CFGR1 = 1 << 12; /* (1 << 15) => AUTOOFF */; |
| /* clock is ADCCLK */ |
| STM32_ADC_CFGR2 = 0; |
| /* Sampling time : 71.5 ADC clock cycles, about 5us */ |
| STM32_ADC_SMPR = 6; |
| /* 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_TIM32_CNT(2) = 0; |
| STM32_TIM_PSC(2) = CPU_CLOCK / 1000000 - 1; |
| STM32_TIM_EGR(2) = 0x0001; /* Reload the pre-scaler */ |
| STM32_TIM_CR1(2) = 1; |
| STM32_TIM_DIER(2) = 0; |
| 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"); |
| } |
| |
| void hardware_init(void) |
| { |
| power_init(); |
| clock_init(); |
| 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 STRT (1<<6) |
| #define CR_LOCK (1<<7) |
| |
| int flash_write_rw(int offset, int size, const char *data) |
| { |
| uint16_t *address = (uint16_t *) |
| (CONFIG_FLASH_BASE + CONFIG_FW_RW_OFF + offset); |
| int res = EC_SUCCESS; |
| int i; |
| |
| if ((uint32_t)address > CONFIG_FLASH_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_erase_rw(void) |
| { |
| int res = EC_SUCCESS; |
| int offset = CONFIG_FW_RW_OFF; |
| int size = CONFIG_FW_RW_SIZE; |
| |
| /* 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_FLASH_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 struct sha1_ctx ctx; |
| uint8_t *flash_hash_rw(void) |
| { |
| sha1_init(&ctx); |
| sha1_update(&ctx, (void *)CONFIG_FLASH_BASE + CONFIG_FW_RW_OFF, |
| CONFIG_FW_RW_SIZE - 32); |
| return sha1_final(&ctx); |
| } |