blob: a1f0b01ed9cd61d768fe274bebabec7de6434d6a [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 SPI logic and console commands */
#include "board_util.h"
#include "builtin/assert.h"
#include "clock.h"
#include "clock_chip.h"
#include "common.h"
#include "console.h"
#include "dma.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "spi.h"
#include "stm32-dma.h"
#include "timer.h"
#include "usb_spi.h"
#include "util.h"
const uint8_t OCTOSPI_PORT = (uint8_t)-1;
/*
* This flag indicates that the SPI request is for a TPM, that is, as the
* fourth and final command byte is written to the SPI bus, the controller
* must pay attention to the simultaneous output from the SPI device. If the
* byte has the value 0x01, it means that the transaction can immediately
* proceed (read or write), of the value is 0x00, then the controller must
* continue polling, until a value 0x01 is received, before proceeding.
*
* Care must be taken that this bit does not overlap with any of the
* "standard" bits declared in chip/stm32/usb_spi.h
*/
#define FLASH_FLAG_TPM_POS 27
#define FLASH_FLAG_TPM (0x1U << FLASH_FLAG_TPM_POS)
/*
* This flag requests that HyperDebug should wait for a "ready pulse" on a
* particular pin, before proceeding with the TPM transaction. (For reads,
* the wait is between header and data, for writes, the wait is after the data
* transfer.)
*
* Care must be taken that this bit does not overlap with any of the
* "standard" bits declared in chip/stm32/usb_spi.h
*/
#define FLASH_FLAG_TPM_WAIT_FOR_READY_POS 26
#define FLASH_FLAG_TPM_WAIT_FOR_READY \
(0x1U << FLASH_FLAG_TPM_WAIT_FOR_READY_POS)
/*
* List of SPI devices that can be controlled via USB.
*
* SPI1 and SPI2 use PCLK (27.5 MHz) as base frequency.
* QSPI uses either SYSCLK (110 MHz) or MSI (variable) as base frequency.
*
* Divisors below result in default SPI clock of approx. 430 kHz for all
*/
struct spi_device_t spi_devices[] = {
{ .name = "SPI2",
.port = 1,
.div = 5,
.gpio_cs = GPIO_CN9_25,
.usb_flags = USB_SPI_ENABLED | USB_SPI_CUSTOM_SPI_DEVICE |
USB_SPI_CUSTOM_SPI_DEVICE_FULL_DUPLEX_SUPPORTED },
{ .name = "QSPI",
.port = OCTOSPI_PORT,
.div = 255,
.gpio_cs = GPIO_CN10_6,
.usb_flags = USB_SPI_ENABLED | USB_SPI_CUSTOM_SPI_DEVICE |
USB_SPI_FLASH_DUAL_SUPPORT | USB_SPI_FLASH_QUAD_SUPPORT |
USB_SPI_FLASH_DTR_SUPPORT },
{ .name = "SPI1",
.port = 0,
.div = 5,
.gpio_cs = GPIO_CN7_4,
.usb_flags = USB_SPI_ENABLED | USB_SPI_CUSTOM_SPI_DEVICE |
USB_SPI_CUSTOM_SPI_DEVICE_FULL_DUPLEX_SUPPORTED },
};
const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
static int spi_device_default_gpio_cs[ARRAY_SIZE(spi_devices)];
static int spi_device_default_div[ARRAY_SIZE(spi_devices)];
static int spi_device_ready_pin[ARRAY_SIZE(spi_devices)];
static const size_t NUM_MSI_FREQUENCIES = 12;
/*
* List of possible base frequencies for the OCTOSPI controller, the first
* NUM_MSI_FREQUENCIES entries correspond to the options of the MSI oscillator.
* The last one is SYSCLK, and will be dynamically populated.
*/
static uint32_t base_frequencies[13] = {
100000, 200000, 400000, 800000, 1000000, 2000000, 4000000,
8000000, 16000000, 24000000, 32000000, 48000000, 0xFFFFFFFF,
};
uint32_t octospi_clock(void)
{
switch (STM32_RCC_CCIPR2 & STM32_RCC_CCIPR2_OSPISEL_MSK) {
case STM32_RCC_CCIPR2_OSPISEL_SYSCLK:
return clock_get_freq();
case STM32_RCC_CCIPR2_OSPISEL_MSI: {
size_t msi_freq = (STM32_RCC_CR & STM32_RCC_CR_MSIRANGE_MSK) >>
STM32_RCC_CR_MSIRANGE_POS;
if (msi_freq < NUM_MSI_FREQUENCIES)
return base_frequencies[msi_freq];
return 0;
}
default:
return 0;
}
}
uint32_t spi_clock(void)
{
return clock_get_apb_freq();
}
/*
* Find spi device by name or by number. Returns an index into spi_devices[],
* or on error a negative value.
*/
static int find_spi_by_name(const char *name)
{
int i;
char *e;
i = strtoi(name, &e, 0);
if (!*e && i < spi_devices_used)
return i;
for (i = 0; i < spi_devices_used; i++) {
if (!strcasecmp(name, spi_devices[i].name))
return i;
}
/* SPI device not found */
return -1;
}
static void print_spi_info(int index)
{
uint32_t bits_per_second;
if (spi_devices[index].port == OCTOSPI_PORT) {
// OCTOSPI as 8 bit prescaler, dividing clock by 1..256.
bits_per_second =
octospi_clock() / (spi_devices[index].div + 1);
} else {
// Other SPIs have prescaler by power of two 2, 4, 8, ..., 256.
bits_per_second = spi_clock() / (2 << spi_devices[index].div);
}
ccprintf(" %d %s %d bps\n", index, spi_devices[index].name,
bits_per_second);
/* Flush console to avoid truncating output */
cflush();
}
/*
* Get information about one or all SPI ports.
*/
static int command_spi_info(int argc, const char **argv)
{
int i;
/* If a SPI target is specified, print only that one */
if (argc == 3) {
int index = find_spi_by_name(argv[2]);
if (index < 0) {
ccprintf("SPI device not found\n");
return EC_ERROR_PARAM2;
}
print_spi_info(index);
return EC_SUCCESS;
}
/* Otherwise print them all */
for (i = 0; i < spi_devices_used; i++) {
print_spi_info(i);
}
return EC_SUCCESS;
}
static int command_spi_set_speed(int argc, const char **argv)
{
int index;
uint32_t desired_speed;
char *e;
if (argc < 5)
return EC_ERROR_PARAM_COUNT;
index = find_spi_by_name(argv[3]);
if (index < 0)
return EC_ERROR_PARAM3;
desired_speed = strtoi(argv[4], &e, 0);
if (*e)
return EC_ERROR_PARAM4;
if (spi_devices[index].port == OCTOSPI_PORT) {
/* Turn off MSI oscillator (in order to allow modification). */
STM32_RCC_CR &= ~STM32_RCC_CR_MSION;
/*
* Find prescaler value by division, rounding up in order to get
* slightly slower speed than requested, if it cannot be matched
* exactly.
*
* The OCTOSPI peripheral can derive clock from either SYSCLK
* (110 MHz) or the variable MSI, attempt calculation with all
* possible frequencies, and see which one gets closest to the
* requested frequency, without exceeding it.
*/
uint8_t best_divisor;
size_t best_base_frequency_index;
/* Populate current SYSCLK. */
base_frequencies[NUM_MSI_FREQUENCIES] = clock_get_freq();
find_best_divisor(desired_speed, base_frequencies,
NUM_MSI_FREQUENCIES + 1, &best_divisor,
&best_base_frequency_index);
if (best_base_frequency_index < NUM_MSI_FREQUENCIES) {
/*
* Either the requested SPI clock frequency is too slow
* for SYSCLK source, or the MSI source would be able to
* get closer to the requested frequency. Select MSI as
* OCTOSPI clock source
*/
/* Select MSI frequency */
STM32_RCC_CR =
(STM32_RCC_CR & ~STM32_RCC_CR_MSIRANGE_MSK) |
(best_base_frequency_index
<< STM32_RCC_CR_MSIRANGE_POS) |
STM32_RCC_CR_MSIRGSEL;
/* Enable MSI and wait for MSI to be ready */
wait_for_ready(&STM32_RCC_CR, STM32_RCC_CR_MSION,
STM32_RCC_CR_MSIRDY);
/* Choose MSI as clock source for OCTOSPI */
STM32_RCC_CCIPR2 = (STM32_RCC_CCIPR2 &
~STM32_RCC_CCIPR2_OSPISEL_MSK) |
STM32_RCC_CCIPR2_OSPISEL_MSI;
} else {
/*
* The SYSCLK source is able to get closer to the
* requested SPI clock frequency, select SYSCLK as
* OCTOSPI clock source
*/
STM32_RCC_CCIPR2 = (STM32_RCC_CCIPR2 &
~STM32_RCC_CCIPR2_OSPISEL_MSK) |
STM32_RCC_CCIPR2_OSPISEL_SYSCLK;
}
STM32_OCTOSPI_DCR2 = spi_devices[index].div = best_divisor;
} else {
int divisor = 7;
/*
* Find the smallest divisor that result in a speed not faster
* than what was requested.
*/
while (divisor > 0) {
if (spi_clock() / (2 << (divisor - 1)) >
desired_speed) {
/* One step further would make the clock too
* fast, stop here. */
break;
}
divisor--;
}
/*
* Re-initialize spi controller to apply the new clock divisor.
*/
spi_enable(&spi_devices[index], 0);
spi_devices[index].div = divisor;
spi_enable(&spi_devices[index], 1);
}
return EC_SUCCESS;
}
static int command_spi_set_cs(int argc, const char **argv)
{
int index;
int desired_gpio_cs;
if (argc < 5)
return EC_ERROR_PARAM_COUNT;
index = find_spi_by_name(argv[3]);
if (index < 0)
return EC_ERROR_PARAM3;
if (!strcasecmp(argv[4], "default")) {
desired_gpio_cs = spi_device_default_gpio_cs[index];
} else {
desired_gpio_cs = gpio_find_by_name(argv[4]);
if (desired_gpio_cs == GPIO_COUNT)
return EC_ERROR_PARAM4;
}
spi_devices[index].gpio_cs = desired_gpio_cs;
return EC_SUCCESS;
}
static int command_spi_set_ready_pin(int argc, const char **argv)
{
int index;
int desired_gpio;
if (argc < 5)
return EC_ERROR_PARAM_COUNT;
index = find_spi_by_name(argv[3]);
if (index < 0)
return EC_ERROR_PARAM3;
desired_gpio = gpio_find_by_name(argv[4]);
if (desired_gpio == GPIO_COUNT)
return EC_ERROR_PARAM4;
spi_device_ready_pin[index] = desired_gpio;
return EC_SUCCESS;
}
static int command_spi_set(int argc, const char **argv)
{
if (argc < 3)
return EC_ERROR_PARAM_COUNT;
if (!strcasecmp(argv[2], "speed"))
return command_spi_set_speed(argc, argv);
if (!strcasecmp(argv[2], "cs"))
return command_spi_set_cs(argc, argv);
if (!strcasecmp(argv[2], "ready"))
return command_spi_set_ready_pin(argc, argv);
return EC_ERROR_PARAM2;
}
static int command_spi(int argc, const char **argv)
{
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
if (!strcasecmp(argv[1], "info"))
return command_spi_info(argc, argv);
if (!strcasecmp(argv[1], "set"))
return command_spi_set(argc, argv);
return EC_ERROR_PARAM1;
}
DECLARE_CONSOLE_COMMAND_FLAGS(spi, command_spi,
"info [PORT]"
"\nset speed PORT BPS"
"\nset cs PORT PIN"
"\nset ready PORT PIN",
"SPI bus manipulation", CMD_FLAG_RESTRICTED);
/******************************************************************************
* OCTOSPI driver.
*/
/*
* Wait for a certain set of status bits to all be asserted.
*/
static int octospi_wait_for(uint32_t flags, timestamp_t deadline)
{
while ((STM32_OCTOSPI_SR & flags) != flags) {
timestamp_t now = get_time();
if (timestamp_expired(deadline, &now))
return EC_ERROR_TIMEOUT;
}
return EC_SUCCESS;
}
/*
* Board-specific SPI driver entry point, called by usb_spi.c.
*/
void usb_spi_board_enable(void)
{
/* All initialization already done in board_init(). */
}
void usb_spi_board_disable(void)
{
}
static const struct dma_option dma_octospi_option = {
.channel = STM32_DMAC_CH13,
.periph = (void *)&STM32_OCTOSPI_DR,
.flags = STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT,
};
static bool previous_cs;
static timestamp_t deadline;
/*
* Implementation of EC SPI driver for OCTOSPI controller in STM32L5 chips (and
* probably others). For now, HyperDebug is the only board that uses a chip
* with such an OctoSPI controller, so until we have seen this code being
* generally useful, it will live in board/hyperdebug.
*/
static int qspi_transaction_async(const struct spi_device_t *spi_device,
uint32_t flash_flags, const uint8_t *txdata,
int txlen, uint8_t *rxdata, int rxlen)
{
uint32_t opcode = 0, address = 0;
const uint32_t mode = flash_flags & FLASH_FLAG_MODE_MSK;
const uint8_t width = (flash_flags & FLASH_FLAG_WIDTH_MSK) >>
FLASH_FLAG_WIDTH_POS;
uint8_t opcode_len = (flash_flags & FLASH_FLAG_OPCODE_LEN_MSK) >>
FLASH_FLAG_OPCODE_LEN_POS;
uint8_t addr_len = (flash_flags & FLASH_FLAG_ADDR_LEN_MSK) >>
FLASH_FLAG_ADDR_LEN_POS;
const uint8_t dummy_cycles =
(flash_flags & FLASH_FLAG_DUMMY_CYCLES_MSK) >>
FLASH_FLAG_DUMMY_CYCLES_POS;
uint32_t data_len;
uint32_t control_value = 0;
/*
* Bring OCTOSPI block out of reset.
*/
deadline.val = get_time().val + OCTOSPI_INIT_TIMEOUT_US;
STM32_RCC_AHB3RSTR |= STM32_RCC_AHB3RSTR_QSPIRST;
STM32_RCC_AHB3RSTR &= ~STM32_RCC_AHB3RSTR_QSPIRST;
while (STM32_OCTOSPI_SR & STM32_OCTOSPI_SR_BUSY) {
timestamp_t now = get_time();
if (timestamp_expired(deadline, &now))
return EC_ERROR_TIMEOUT;
}
/*
* Declare that a "Standard" SPI flash device, maximum size is connected
* to OCTOSPI. This allows the controller to send arbitrary 32-bit
* addresses, which is needed as we use the instruction and address
* bytes as arbitrary data to send via SPI.
*/
STM32_OCTOSPI_DCR1 = STM32_OCTOSPI_DCR1_MTYP_STANDARD |
STM32_OCTOSPI_DCR1_DEVSIZE_MSK;
/* Clock prescaler (max value 255) */
STM32_OCTOSPI_DCR2 = spi_devices[1].div;
if (!flash_flags) {
/*
* This is a request does not use the extended format with SPI
* flash flags, but is for doing plain write-then-read of single
* lane (COPI/CIPO) SPI data.
*/
if (rxlen == SPI_READBACK_ALL) {
cprints(CC_SPI,
"Full duplex not supported by OctoSPI hardware");
return EC_ERROR_UNIMPLEMENTED;
} else if (!rxlen && !txlen) {
/* No operation requested, done. */
return EC_SUCCESS;
} else if (!rxlen) {
/*
* Transmit-only transaction. This is implemented by
* not using any of the up to 12 bytes of instructions,
* but as all "data".
*/
flash_flags |= FLASH_FLAG_READ_WRITE_WRITE;
} else if (!txlen) {
/*
* Receive-only transaction. Not supported by STM32L552,
* as described in ST document ES0448:
* "STM32L552xx/562xx device errata" in the section
* 2.4.12: "Data not sampled correctly on reads without
* DQS and with less than two cycles before the data
* phase".
*/
cprints(CC_SPI,
"Receive-only transaction not supported by OctoSPI hardware");
return EC_ERROR_UNIMPLEMENTED;
} else if (txlen <= 12) {
/*
* Sending of up to 12 bytes, followed by reading a
* possibly large number of bytes. This is implemented
* by a "read" transaction using the instruction and
* address feature of OctoSPI.
*/
if (txlen <= 4) {
opcode_len = txlen;
} else {
opcode_len = 4;
addr_len = txlen - 4;
}
} else {
/*
* Sending many bytes, followed by reading. This would
* have to be implemented as two separate OctoSPI
* transactions.
*/
cprints(CC_SPI,
"General write-then-read not supported by OctoSPI hardware");
return EC_ERROR_UNIMPLEMENTED;
}
}
previous_cs = gpio_get_level(spi_device->gpio_cs);
/* Drive chip select low */
gpio_set_level(spi_device->gpio_cs, 0);
/* Deadline on the entire SPI transaction. */
deadline.val = get_time().val + OCTOSPI_TRANSACTION_TIMEOUT_US;
if ((flash_flags & FLASH_FLAG_READ_WRITE_MSK) ==
FLASH_FLAG_READ_WRITE_WRITE) {
data_len = txlen - opcode_len - addr_len;
/* Enable OCTOSPI, indirect write mode. */
STM32_OCTOSPI_CR = STM32_OCTOSPI_CR_FMODE_IND_WRITE |
STM32_OCTOSPI_CR_DMAEN | STM32_OCTOSPI_CR_EN;
} else {
data_len = rxlen;
/* Enable OCTOSPI, indirect read mode. */
STM32_OCTOSPI_CR = STM32_OCTOSPI_CR_FMODE_IND_READ |
STM32_OCTOSPI_CR_DMAEN | STM32_OCTOSPI_CR_EN;
}
/* Clear completion flag from last transaction. */
STM32_OCTOSPI_FCR = STM32_OCTOSPI_FCR_CTCF;
/* Data length. */
STM32_OCTOSPI_DLR = data_len - 1;
/*
* Set up the number of bytes and data width of each of the opcode,
* address, and "alternate" stages of the initial command bytes.
*/
if (opcode_len == 0) {
control_value |= STM32_OCTOSPI_CCR_IMODE_NONE;
} else {
control_value |= (opcode_len - 1)
<< STM32_OCTOSPI_CCR_ISIZE_POS;
if (mode < FLASH_FLAG_MODE_NNN) {
// Opcode phase is single-lane
control_value |= 1 << STM32_OCTOSPI_CCR_IMODE_POS;
} else {
// Opcode phase is multi-lane
control_value |= (width + 1)
<< STM32_OCTOSPI_CCR_IMODE_POS;
if (flash_flags & FLASH_FLAG_DTR)
control_value |= STM32_OCTOSPI_CCR_IDTR;
}
for (int i = 0; i < opcode_len; i++) {
opcode <<= 8;
opcode |= *txdata++;
txlen--;
}
}
if (addr_len == 0) {
control_value |= STM32_OCTOSPI_CCR_ADMODE_NONE;
control_value |= STM32_OCTOSPI_CCR_ABMODE_NONE;
} else if (addr_len <= 4) {
control_value |= (addr_len - 1) << STM32_OCTOSPI_CCR_ADSIZE_POS;
if (mode < FLASH_FLAG_MODE_1NN) {
// Address phase is single-lane
control_value |= 1 << STM32_OCTOSPI_CCR_ADMODE_POS;
} else {
// Address phase is multi-lane
control_value |= (width + 1)
<< STM32_OCTOSPI_CCR_ADMODE_POS;
if (flash_flags & FLASH_FLAG_DTR)
control_value |= STM32_OCTOSPI_CCR_ADDTR;
}
for (int i = 0; i < addr_len; i++) {
address <<= 8;
address |= *txdata++;
txlen--;
}
control_value |= STM32_OCTOSPI_CCR_ABMODE_NONE;
} else {
uint32_t alternate = 0;
control_value |= 3 << STM32_OCTOSPI_CCR_ADSIZE_POS;
control_value |= (addr_len - 5) << STM32_OCTOSPI_CCR_ABSIZE_POS;
if (mode < FLASH_FLAG_MODE_1NN) {
// Address phase is single-lane
control_value |= 1 << STM32_OCTOSPI_CCR_ADMODE_POS;
control_value |= 1 << STM32_OCTOSPI_CCR_ABMODE_POS;
} else {
// Address phase is multi-lane
control_value |= (width + 1)
<< STM32_OCTOSPI_CCR_ADMODE_POS;
control_value |= (width + 1)
<< STM32_OCTOSPI_CCR_ABMODE_POS;
if (flash_flags & FLASH_FLAG_DTR)
control_value |= STM32_OCTOSPI_CCR_ADDTR |
STM32_OCTOSPI_CCR_ABDTR;
}
for (int i = 0; i < 4; i++) {
address <<= 8;
address |= *txdata++;
txlen--;
}
for (int i = 0; i < addr_len - 4; i++) {
alternate <<= 8;
alternate |= *txdata++;
txlen--;
}
STM32_OCTOSPI_ABR = alternate;
}
/* Set up how many bytes to read/write after the initial command. */
if (data_len == 0) {
control_value |= STM32_OCTOSPI_CCR_DMODE_NONE;
} else {
if (mode < FLASH_FLAG_MODE_11N) {
// Data phase is single-lane
control_value |= 1 << STM32_OCTOSPI_CCR_DMODE_POS;
} else {
// Data phase is multi-lane
control_value |= (width + 1)
<< STM32_OCTOSPI_CCR_DMODE_POS;
if (flash_flags & FLASH_FLAG_DTR)
control_value |= STM32_OCTOSPI_CCR_DDTR;
}
}
/* Dummy cycles. */
STM32_OCTOSPI_TCR = dummy_cycles << STM32_OCTOSPI_TCR_DCYC_POS;
STM32_OCTOSPI_CCR = control_value;
/* Set instruction and address registers, triggering the start of the
* write+read transaction. */
STM32_OCTOSPI_IR = opcode;
STM32_OCTOSPI_AR = address;
if ((flash_flags & FLASH_FLAG_READ_WRITE_MSK) ==
FLASH_FLAG_READ_WRITE_WRITE) {
if (txlen > 0) {
dma_chan_t *txdma = dma_get_channel(STM32_DMAC_CH13);
dma_prepare_tx(&dma_octospi_option, txlen, txdata);
dma_go(txdma);
}
} else {
if (rxlen > 0) {
dma_start_rx(&dma_octospi_option, rxlen, rxdata);
}
}
return EC_SUCCESS;
}
static int qspi_transaction_is_complete(const struct spi_device_t *spi_device)
{
/* Query the "transaction complete flag" of the status register. */
return STM32_OCTOSPI_SR & STM32_OCTOSPI_SR_TCF;
}
static int qspi_transaction_flush(const struct spi_device_t *spi_device)
{
/*
* Wait until DMA transfer is complete (no-op if DMA not started because
* of zero-length transfer).
*/
int rv = dma_wait(STM32_DMAC_CH13);
dma_disable(STM32_DMAC_CH13);
if (rv != EC_SUCCESS)
return rv;
/*
* Ensure that all bits of the last byte has been shifted onto the SPI
* bus.
*/
rv = octospi_wait_for(STM32_OCTOSPI_SR_TCF, deadline);
/* Return chip select to previous level. */
gpio_set_level(spi_device->gpio_cs, previous_cs);
/*
* Put OCTOSPI block into reset, to ensure that no state carries over to
* next transaction.
*/
STM32_RCC_AHB3RSTR |= STM32_RCC_AHB3RSTR_QSPIRST;
return rv;
}
/*
* Board-specific SPI driver entry point, called by usb_spi.c. On this board,
* every spi device is declared as requiring board specific driver, in order to
* add enhanced TPM functionality.
*/
int usb_spi_board_transaction_async(const struct spi_device_t *spi_device,
uint32_t flash_flags, const uint8_t *txdata,
int txlen, uint8_t *rxdata, int rxlen)
{
if (flash_flags & (FLASH_FLAG_TPM_WAIT_FOR_READY | FLASH_FLAG_TPM)) {
/* Polling only supported in synchronous function. */
return USB_SPI_UNSUPPORTED_FLASH_MODE;
}
if (spi_device->port == OCTOSPI_PORT)
return qspi_transaction_async(spi_device, flash_flags, txdata,
txlen, rxdata, rxlen);
if (flash_flags & FLASH_FLAGS_REQUIRING_SUPPORT) {
/*
* The standard spi_transaction() does not support
* any multi-lane modes.
*/
return USB_SPI_UNSUPPORTED_FLASH_MODE;
}
return spi_transaction_async(spi_device, txdata, txlen, rxdata, rxlen);
}
/*
* Board-specific SPI driver entry point, called by usb_spi.c. On this board,
* every spi device is declared as requiring board specific driver, in order to
* add enhanced TPM functionality.
*/
int usb_spi_board_transaction_is_complete(const struct spi_device_t *spi_device)
{
if (spi_device->port == OCTOSPI_PORT)
return qspi_transaction_is_complete(spi_device);
return true;
}
/*
* Board-specific SPI driver entry point, called by usb_spi.c. On this board,
* every spi device is declared as requiring board specific driver, in order to
* add enhanced TPM functionality.
*/
int usb_spi_board_transaction_flush(const struct spi_device_t *spi_device)
{
if (spi_device->port == OCTOSPI_PORT)
return qspi_transaction_flush(spi_device);
return spi_transaction_flush(spi_device);
}
/*
* Synchronously perform one TPM transaction, consisting of four byte header
* followed by a number of data bytes. Respect TPM protocol by polling when
* data phase can proceed, as well as optionally wait for edge on Google
* proprietary "ready signal".
*/
static int usb_spi_tpm_transaction(const struct spi_device_t *spi_device,
uint32_t flash_flags, const uint8_t *txdata,
int txlen, uint8_t *rxdata, int rxlen)
{
size_t spi_index = spi_device - spi_devices;
assert(spi_index < ARRAY_SIZE(spi_devices));
int gsc_ready_pin = spi_device_ready_pin[spi_index];
/* TPM protocol has 4-byte command/address. */
if (txlen < 4)
return USB_SPI_UNSUPPORTED_FLASH_MODE;
if (flash_flags & FLASH_FLAG_TPM_WAIT_FOR_READY &&
gsc_ready_pin == GPIO_COUNT) {
/*
* Waiting for ready pulse was requested, but ready pin not
* declared.
*/
return USB_SPI_UNSUPPORTED_FLASH_MODE;
}
timestamp_t deadline;
deadline.val = get_time().val + 100000;
/* Assert chip select */
int chip_select_level_before = gpio_get_level(spi_device->gpio_cs);
gpio_set_level(spi_device->gpio_cs, 0);
/* Enable polling from fast timer interrupt. */
if (flash_flags & FLASH_FLAG_TPM_WAIT_FOR_READY)
start_monitoring_for_falling_edge(gsc_ready_pin);
uint8_t resp[4];
/* Send 4-byte TPM header, also receiving ready status. */
int rv = spi_transaction(spi_device, txdata, 4, resp, -1);
/* Poll for the TPM standard ready status. */
while (rv == EC_SUCCESS && resp[3] != 0x01) {
timestamp_t now = get_time();
if (timestamp_expired(deadline, &now)) {
rv = EC_ERROR_TIMEOUT;
break;
}
rv = spi_transaction(spi_device, NULL, 0, resp + 3, 1);
}
/* Data phase of the TPM transaction. */
if (rv == EC_SUCCESS)
rv = spi_transaction(spi_device, txdata + 4, txlen - 4, rxdata,
rxlen);
/* Release chip select even when returning an error. */
gpio_set_level(spi_device->gpio_cs, chip_select_level_before);
/* Optionally wait for Google ready signal. */
if (flash_flags & FLASH_FLAG_TPM_WAIT_FOR_READY) {
if (rv == EC_SUCCESS)
rv = wait_for_falling_edge(deadline);
stop_monitoring_for_falling_edge();
}
return rv;
}
/*
* Board-specific SPI driver entry point, called by usb_spi.c. On this board,
* every spi device is declared as requiring board specific driver, in order to
* add enhanced TPM functionality.
*/
int usb_spi_board_transaction(const struct spi_device_t *spi_device,
uint32_t flash_flags, const uint8_t *txdata,
int txlen, uint8_t *rxdata, int rxlen)
{
if (flash_flags & FLASH_FLAG_TPM) {
/* Tailored logic for TPM transactions. */
return usb_spi_tpm_transaction(spi_device, flash_flags, txdata,
txlen, rxdata, rxlen);
}
int rv = usb_spi_board_transaction_async(spi_device, flash_flags,
txdata, txlen, rxdata, rxlen);
if (rv == EC_SUCCESS) {
rv = usb_spi_board_transaction_flush(spi_device);
}
return rv;
}
/* Reconfigure SPI ports to power-on default values. */
static void spi_reinit(void)
{
for (unsigned int i = 0; i < spi_devices_used; i++) {
spi_device_ready_pin[i] = GPIO_COUNT;
if (spi_devices[i].port == OCTOSPI_PORT) {
/* Quad SPI controller */
spi_devices[i].gpio_cs = spi_device_default_gpio_cs[i];
spi_devices[i].div = spi_device_default_div[i];
/* Select SYSCLK clock source */
STM32_RCC_CCIPR2 = (STM32_RCC_CCIPR2 &
~STM32_RCC_CCIPR2_OSPISEL_MSK) |
STM32_RCC_CCIPR2_OSPISEL_SYSCLK;
} else {
/* "Ordinary" SPI controller */
spi_enable(&spi_devices[i], 0);
spi_devices[i].gpio_cs = spi_device_default_gpio_cs[i];
spi_devices[i].div = spi_device_default_div[i];
spi_enable(&spi_devices[i], 1);
}
}
}
DECLARE_HOOK(HOOK_REINIT, spi_reinit, HOOK_PRIO_DEFAULT);
/* Initialize board for SPI. */
static void spi_init(void)
{
for (unsigned int i = 0; i < spi_devices_used; i++)
spi_device_ready_pin[i] = GPIO_COUNT;
/* Record initial values for use by `spi_reinit()` above. */
for (unsigned int i = 0; i < spi_devices_used; i++) {
spi_device_default_gpio_cs[i] = spi_devices[i].gpio_cs;
spi_device_default_div[i] = spi_devices[i].div;
}
/* Structured endpoints */
usb_spi_enable(1);
/* Configure SPI GPIOs */
gpio_config_module(MODULE_SPI, 1);
/*
* Unlike most SPI, I2C and UARTs, which are configured in their
* alternate mode by default, SPI1 pins are in GPIO input mode on
* HyperDebug power-on, for compatibility with previous firmwares. In
* the future we may decide to leave even more functions off by default,
* in order for HyperDebug to actively drive as little at possible on
* boot. It is relatively straightforward to declare pins as "Alternate
* mode" in opentitantool json configuration file, to have them enabled
* by "transport init".
*
* The code below sets up the alternate function "number" for the
* relevant pins, such that when alternate mode is enabled on the pins,
* the result is the particular alternate function that HyperDebug
* firmware has chosen for the pin.
*/
STM32_GPIO_AFRL(STM32_GPIOA_BASE) |= 0x55000000; /* SPI1: PA6/PA7
HIDO/HODI */
STM32_GPIO_AFRL(STM32_GPIOB_BASE) |= 0x00005000; /* SPI1: PB3 SCK */
/*
* Enable SPI1.
*/
/* Enable clocks to SPI1 module */
STM32_RCC_APB2ENR |= STM32_RCC_APB2ENR_SPI1EN;
/* Reset SPI1 */
STM32_RCC_APB2RSTR |= STM32_RCC_APB2RSTR_SPI1RST;
STM32_RCC_APB2RSTR &= ~STM32_RCC_APB2RSTR_SPI1RST;
spi_enable(&spi_devices[2], 1);
/*
* Enable SPI2.
*/
/* Enable clocks to SPI2 module */
STM32_RCC_APB1ENR1 |= STM32_RCC_APB1ENR1_SPI2EN;
/* Reset SPI2 */
STM32_RCC_APB1RSTR1 |= STM32_RCC_APB1RSTR1_SPI2RST;
STM32_RCC_APB1RSTR1 &= ~STM32_RCC_APB1RSTR1_SPI2RST;
spi_enable(&spi_devices[0], 1);
/*
* Enable OCTOSPI clock, but keep the block under reset. Will be
* brought out of reset only when needed.
*/
STM32_RCC_AHB3RSTR |= STM32_RCC_AHB3RSTR_QSPIRST;
STM32_RCC_AHB3ENR |= STM32_RCC_AHB3ENR_QSPIEN;
/* Turn off MSI, not used initially. */
STM32_RCC_CR &= ~STM32_RCC_CR_MSION;
/* Select DMA channel */
dma_select_channel(STM32_DMAC_CH13, DMAMUX_REQ_OCTOSPI1);
}
DECLARE_HOOK(HOOK_INIT, spi_init, HOOK_PRIO_DEFAULT + 1);