blob: 37642a2137ff130bc460804591ab5881d896232c [file] [log] [blame]
/* Copyright 2022 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* HyperDebug board configuration */
#include "adc.h"
#include "clock_chip.h"
#include "common.h"
#include "dfu_bootmanager_shared.h"
#include "ec_version.h"
#include "queue_policies.h"
#include "registers.h"
#include "spi.h"
#include "stm32-dma.h"
#include "timer.h"
#include "usart-stm32l5.h"
#include "usb-stream.h"
#include "usb_spi.h"
#include <stdio.h>
/* Must come after other header files and interrupt handler declarations */
#include "gpio_list.h"
void board_config_pre_init(void)
{
/* We know VDDIO2 is present, enable the GPIO circuit. */
STM32_PWR_CR2 |= STM32_PWR_CR2_IOSV;
/* Peripheral clock derived by dividing core clock by 4. */
STM32_RCC_CFGR = STM32_RCC_CFGR_PPRE1_DIV4 | STM32_RCC_CFGR_PPRE2_DIV4;
}
/******************************************************************************
* Forward UARTs as a USB serial interface.
*/
#define USB_STREAM_RX_SIZE 64
#define USB_STREAM_TX_SIZE 64
/******************************************************************************
* Forward USART2 as a simple USB serial interface.
*/
static struct usart_config const usart2;
struct usb_stream_config const usart2_usb;
static struct queue const usart2_to_usb =
QUEUE_DIRECT(1024, uint8_t, usart2.producer, usart2_usb.consumer);
static struct queue const usb_to_usart2 =
QUEUE_DIRECT(64, uint8_t, usart2_usb.producer, usart2.consumer);
static struct usart_config const usart2 =
USART_CONFIG(usart2_hw, usart_rx_interrupt, usart_tx_interrupt, 115200,
0, usart2_to_usb, usb_to_usart2);
USB_STREAM_CONFIG_USART_IFACE(usart2_usb, USB_IFACE_USART2_STREAM,
USB_STR_USART2_STREAM_NAME, USB_EP_USART2_STREAM,
USB_STREAM_RX_SIZE, USB_STREAM_TX_SIZE,
usb_to_usart2, usart2_to_usb, usart2)
/******************************************************************************
* Forward USART3 as a simple USB serial interface.
*/
static struct usart_config const usart3;
struct usb_stream_config const usart3_usb;
static struct queue const usart3_to_usb =
QUEUE_DIRECT(1024, uint8_t, usart3.producer, usart3_usb.consumer);
static struct queue const usb_to_usart3 =
QUEUE_DIRECT(64, uint8_t, usart3_usb.producer, usart3.consumer);
static struct usart_config const usart3 =
USART_CONFIG(usart3_hw, usart_rx_interrupt, usart_tx_interrupt, 115200,
0, usart3_to_usb, usb_to_usart3);
USB_STREAM_CONFIG_USART_IFACE(usart3_usb, USB_IFACE_USART3_STREAM,
USB_STR_USART3_STREAM_NAME, USB_EP_USART3_STREAM,
USB_STREAM_RX_SIZE, USB_STREAM_TX_SIZE,
usb_to_usart3, usart3_to_usb, usart3)
/******************************************************************************
* Forward USART4 as a simple USB serial interface.
*/
static struct usart_config const usart4;
struct usb_stream_config const usart4_usb;
static struct queue const usart4_to_usb =
QUEUE_DIRECT(1024, uint8_t, usart4.producer, usart4_usb.consumer);
static struct queue const usb_to_usart4 =
QUEUE_DIRECT(64, uint8_t, usart4_usb.producer, usart4.consumer);
static struct usart_config const usart4 =
USART_CONFIG(usart4_hw, usart_rx_interrupt, usart_tx_interrupt, 115200,
0, usart4_to_usb, usb_to_usart4);
USB_STREAM_CONFIG_USART_IFACE(usart4_usb, USB_IFACE_USART4_STREAM,
USB_STR_USART4_STREAM_NAME, USB_EP_USART4_STREAM,
USB_STREAM_RX_SIZE, USB_STREAM_TX_SIZE,
usb_to_usart4, usart4_to_usb, usart4)
/******************************************************************************
* Forward USART5 as a simple USB serial interface.
*/
static struct usart_config const usart5;
struct usb_stream_config const usart5_usb;
static struct queue const usart5_to_usb =
QUEUE_DIRECT(1024, uint8_t, usart5.producer, usart5_usb.consumer);
static struct queue const usb_to_usart5 =
QUEUE_DIRECT(64, uint8_t, usart5_usb.producer, usart5.consumer);
static struct usart_config const usart5 =
USART_CONFIG(usart5_hw, usart_rx_interrupt, usart_tx_interrupt, 115200,
0, usart5_to_usb, usb_to_usart5);
USB_STREAM_CONFIG_USART_IFACE(usart5_usb, USB_IFACE_USART5_STREAM,
USB_STR_USART5_STREAM_NAME, USB_EP_USART5_STREAM,
USB_STREAM_RX_SIZE, USB_STREAM_TX_SIZE,
usb_to_usart5, usart5_to_usb, usart5)
/******************************************************************************
* Define the strings used in our USB descriptors.
*/
const void *const usb_strings[] = {
[USB_STR_DESC] = usb_string_desc,
[USB_STR_VENDOR] = USB_STRING_DESC("Google LLC"),
[USB_STR_PRODUCT] = USB_STRING_DESC("HyperDebug CMSIS-DAP"),
[USB_STR_SERIALNO] = 0,
[USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
[USB_STR_CONSOLE_NAME] = USB_STRING_DESC("HyperDebug Shell"),
[USB_STR_SPI_NAME] = USB_STRING_DESC("SPI"),
[USB_STR_CMSIS_DAP_NAME] = USB_STRING_DESC("I2C CMSIS-DAP"),
[USB_STR_USART2_STREAM_NAME] = USB_STRING_DESC("UART2"),
[USB_STR_USART3_STREAM_NAME] = USB_STRING_DESC("UART3"),
[USB_STR_USART4_STREAM_NAME] = USB_STRING_DESC("UART4"),
[USB_STR_USART5_STREAM_NAME] = USB_STRING_DESC("UART5"),
[USB_STR_DFU_NAME] = USB_STRING_DESC("DFU"),
};
BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
/******************************************************************************
* Set up ADC
*/
/* ADC channels */
struct adc_t adc_channels[] = {
/*
* All available ADC signals, converted to mV (3300mV/4096). Every one
* is declared with same name as the GPIO signal on the same pin, that
* is how opentitantool identifies the signal.
*
* Technically, the Nucleo-L552ZE-Q board can run at either 1v8 or 3v3
* supply, but we use HyperDebug only on 3v3 setting. If in the future
* we want to detect actual voltage, Vrefint could be used. This would
* also serve as calibration as the supply voltage may not be 3300mV
* exactly.
*/
[ADC_VREFINT] = { "VREFINT", 1, 1, 0, STM32_AIN(0) },
[ADC_CN9_11] = { "CN9_11", 3300, 4096, 0, STM32_AIN(1) },
[ADC_CN9_9] = { "CN9_9", 3300, 4096, 0, STM32_AIN(2) },
/*[ADC_CN10_9] = { "CN10_9", 3300, 4096, 0, STM32_AIN(3) },*/
[ADC_CN9_5] = { "CN9_5", 3300, 4096, 0, STM32_AIN(4) },
[ADC_CN10_29] = { "CN10_29", 3300, 4096, 0, STM32_AIN(5) },
[ADC_CN10_11] = { "CN10_11", 3300, 4096, 0, STM32_AIN(6) },
[ADC_CN9_3] = { "CN9_3", 3300, 4096, 0, STM32_AIN(7) },
[ADC_CN9_1] = { "CN9_1", 3300, 4096, 0, STM32_AIN(8) },
[ADC_CN7_9] = { "CN7_9", 3300, 4096, 0, STM32_AIN(9) },
[ADC_CN7_10] = { "CN7_10", 3300, 4096, 0, STM32_AIN(10) },
[ADC_CN7_12] = { "CN7_12", 3300, 4096, 0, STM32_AIN(11) },
[ADC_CN7_14] = { "CN7_14", 3300, 4096, 0, STM32_AIN(12) },
[ADC_CN9_7] = { "CN9_7", 3300, 4096, 0, STM32_AIN(15) },
[ADC_CN10_7] = { "CN10_7", 3300, 4096, 0, STM32_AIN(16) },
};
BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
/******************************************************************************
* Allow changing of system and peripheral clock frequency at runtime.
*
* Changing clock frequency may disrupt speed settings of SPI ports or PWMs
* already set up, so one should preferably choose the clock speed before
* setting up anything else.
*/
/* Default divisors, resulting in maximum 110MHz system clock. */
int stm32_pllm = 4;
int stm32_plln = 55;
int stm32_pllr = 2;
/* Change system/core clock frequency and peripheral clock frequency. */
static void change_frequencies(int m, int n, int r, uint32_t rcc_cfgr_prescaler)
{
/*
* There are a few concerns: We must not use the PLL as source of
* system clock, while modifying its parameters. Also, the peripheral
* clock must never drop below 10MHz, or the USB controller might not
* be able to keep up with the bus.
*/
/* Peripheral clock equal to system core clock (no division). */
STM32_RCC_CFGR = (STM32_RCC_CFGR & ~STM32_RCC_CFGR_PPRE1_MSK &
~STM32_RCC_CFGR_PPRE2_MSK) |
STM32_RCC_CFGR_PPRE1_DIV1 | STM32_RCC_CFGR_PPRE2_DIV1;
/* Temporarily use 16MHz clock source, rather than PLL. */
clock_set_osc(OSC_HSI, OSC_INIT);
/*
* Now we are free to modify PLL parameters.
*/
/* HSI (16MHz) / M must stay within 4MHz - 16MHz. */
stm32_pllm = m;
/* Above frequency * N must stay within 64MHz - 344MHz. */
stm32_plln = n;
/* Above frequency / R must not exceed 110MHz. */
stm32_pllr = r;
/*
* Switch to PLL clock source, using newly updated parameters, waiting
* for the new source to stabilize.
*/
clock_set_osc(OSC_PLL, OSC_HSI);
/* Now apply the desired divisor to peripheral clock. */
hook_notify(HOOK_PRE_FREQ_CHANGE);
STM32_RCC_CFGR = (STM32_RCC_CFGR & ~STM32_RCC_CFGR_PPRE1_MSK &
~STM32_RCC_CFGR_PPRE2_MSK) |
rcc_cfgr_prescaler;
hook_notify(HOOK_FREQ_CHANGE);
}
static int command_clock_set(int argc, const char **argv)
{
if (argc < 3)
return EC_ERROR_PARAM_COUNT;
char *e;
int req_freq = strtoi(argv[1], &e, 0);
int plln, pllr;
if (*e)
return EC_ERROR_PARAM1;
/*
* We restrict ourselves to a PLL input frequency of 4MHz, which is
* then multiplied by a value N in the range 16 though 86 and divided
* by R: 2, 4, or 8. This allows producing any frequency between
* 10MHz and 110MHz with no more than +/- 1.5% deviation.
*/
if (req_freq > 110000000 || req_freq < 10000000) {
ccprintf("Error: Clock frequency out of range\n");
return EC_ERROR_PARAM1;
}
if (req_freq >= 32000000 && req_freq % 2000000 == 0) {
plln = req_freq / 2000000;
pllr = 2;
} else if (req_freq >= 16000000 && req_freq % 1000000 == 0) {
plln = req_freq / 1000000;
pllr = 4;
} else if (req_freq >= 8000000 && req_freq % 500000 == 0) {
plln = req_freq / 500000;
pllr = 8;
} else {
ccprintf("Error: Clock frequency not supported\n");
return EC_ERROR_PARAM1;
}
if (plln > 86) {
ccprintf("Error: Clock frequency not supported\n");
}
int peripheral_clock_div = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
if (req_freq / peripheral_clock_div < 10000000) {
/*
* The STM32L5 USB peripheral requires an APB1 clock frequency
* of at least 10MHz for correct operation.
*/
ccprintf(
"Error: Peripheral frequency must be at least 10000000,"
" reduce the divisor\n");
return EC_ERROR_PARAM2;
}
uint32_t rcc_cfgr;
switch (peripheral_clock_div) {
case 1:
rcc_cfgr = STM32_RCC_CFGR_PPRE1_DIV1 |
STM32_RCC_CFGR_PPRE2_DIV1;
break;
case 2:
rcc_cfgr = STM32_RCC_CFGR_PPRE1_DIV2 |
STM32_RCC_CFGR_PPRE2_DIV2;
break;
case 4:
rcc_cfgr = STM32_RCC_CFGR_PPRE1_DIV4 |
STM32_RCC_CFGR_PPRE2_DIV4;
break;
case 8:
rcc_cfgr = STM32_RCC_CFGR_PPRE1_DIV8 |
STM32_RCC_CFGR_PPRE2_DIV8;
break;
case 16:
rcc_cfgr = STM32_RCC_CFGR_PPRE1_DIV16 |
STM32_RCC_CFGR_PPRE2_DIV16;
break;
default:
ccprintf("Error: Divisor must be power of two, at most 16\n");
return EC_ERROR_PARAM2;
}
change_frequencies(4, plln, pllr, rcc_cfgr);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND_FLAGS(
clock_set, command_clock_set, "clock_set [Hz] [divisor]",
"Valid range: Hz: 10,000,000 to 110,000,000 (with restrictions)\n"
" divisor: 1, 2, 4, 8",
CMD_FLAG_RESTRICTED);
/******************************************************************************
* Initialize board. (More initialization done by hooks in other files.)
*/
static void board_init(void)
{
/* USB to serial queues */
queue_init(&usart2_to_usb);
queue_init(&usb_to_usart2);
queue_init(&usart3_to_usb);
queue_init(&usb_to_usart3);
queue_init(&usart4_to_usb);
queue_init(&usb_to_usart4);
queue_init(&usart5_to_usb);
queue_init(&usb_to_usart5);
/* UART init */
usart_init(&usart2);
usart_init(&usart3);
usart_init(&usart4);
usart_init(&usart5);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_PRE_DEFAULT);
static void usart_reinit(struct usb_stream_config const *usart_usb,
struct usart_config const *usart)
{
usb_usart_clear(usart_usb, usart, CLEAR_BOTH_FIFOS);
usart_set_parity(usart, 0);
usart_set_baud(usart, 115200);
usart_set_break(usart, false);
}
static void usart_reinit_all(void)
{
usart_reinit(&usart2_usb, &usart2);
usart_reinit(&usart3_usb, &usart3);
usart_reinit(&usart4_usb, &usart4);
usart_reinit(&usart5_usb, &usart5);
}
DECLARE_HOOK(HOOK_REINIT, usart_reinit_all, HOOK_PRIO_DEFAULT);
static int command_reinit(int argc, const char **argv)
{
/* Switch back to power-on clock configuration. */
change_frequencies(4, 55, 2,
STM32_RCC_CFGR_PPRE1_DIV4 |
STM32_RCC_CFGR_PPRE2_DIV4);
/* Let every module know to re-initialize to power-on state. */
hook_notify(HOOK_REINIT);
/*
* Since we apparently have some communication with OpenTitanTool,
* rewind "repeating reset" counter, which would otherwise trigger DFU
* mode after a certain number of resets.
*/
dfu_bootmanager_clear();
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND_FLAGS(
reinit, command_reinit, "",
"Stop any ongoing operation, revert to power-on state.",
CMD_FLAG_RESTRICTED);
const char *board_read_serial(void)
{
const uint32_t *stm32_unique_id =
(const uint32_t *)STM32_UNIQUE_ID_BASE;
static char serial[13];
// Compute 12 hex digits from three factory programmed 32-bit "Unique
// ID" words in a manner that has been observed to be consistent with
// how the STM DFU ROM bootloader presents its serial number. This
// means that the serial number of any particular HyperDebug board will
// remain the same as it enters and leaves DFU mode for software
// upgrade.
int rc = snprintf(serial, sizeof(serial), "%08X%04X",
stm32_unique_id[0] + stm32_unique_id[2],
stm32_unique_id[1] >> 16);
if (12 != rc)
return NULL;
return serial;
}