// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. // SPDX-License-Identifier: GPL-3.0-or-later // // SPDX-FileCopyrightText: 2018 Coinkite, Inc. // SPDX-License-Identifier: GPL-3.0-only // /* * (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard * and is covered by GPLv3 license found in COPYING. * * sflash.c -- talk to the serial flash * */ #include #include "spiflash.h" // Connections: // - SPI4 port // - all port E // // SF_CS => PE11 // SF_SCLK => PE12 // SF_MISO => PE13 // SF_MOSI => PE14 #define SF_CS_PIN GPIO_PIN_11 // port E #define SF_SPI_SCK GPIO_PIN_12 // port E #define SF_SPI_MISO GPIO_PIN_13 // port E #define SF_SPI_MOSI GPIO_PIN_14 // port E #define CMD_WRSR 0x01 #define CMD_WRITE 0x02 #define CMD_READ 0x03 #define CMD_FAST_READ 0x0b #define CMD_RDSR 0x05 #define CMD_WREN 0x06 #define CMD_SEC_ERASE 0x20 #define CMD_RDCR 0x35 #define CMD_RD_DEVID 0x9f #define CMD_CHIP_ERASE 0xc7 // active-low chip-select line #define CS_LOW() HAL_GPIO_WritePin(GPIOE, SF_CS_PIN, 0) #define CS_HIGH() HAL_GPIO_WritePin(GPIOE, SF_CS_PIN, 1) static SPI_HandleTypeDef sf_spi_port; static HAL_StatusTypeDef wait_wip_done() { // read RDSR (status register) and busy-wait until // the write operation is done while (1) { uint8_t pkt = CMD_RDSR, stat = 0; CS_LOW(); HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, &pkt, 1, HAL_MAX_DELAY); if (rv == HAL_OK) { rv = HAL_SPI_Receive(&sf_spi_port, &stat, 1, HAL_MAX_DELAY); } CS_HIGH(); if (rv != HAL_OK) return rv; if (stat & 0x01) continue; return HAL_OK; } } static HAL_StatusTypeDef write_enable(void) { uint8_t pkt = CMD_WREN; CS_LOW(); HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, &pkt, 1, HAL_MAX_DELAY); CS_HIGH(); return rv; } HAL_StatusTypeDef spi_read(uint32_t addr, int len, uint8_t *buf) { // send via SPI(1) uint8_t pkt[5] = { CMD_FAST_READ, (addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff, 0x0 }; // for fast-read case CS_LOW(); HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, pkt, sizeof(pkt), HAL_MAX_DELAY); if (rv == HAL_OK) { rv = HAL_SPI_Receive(&sf_spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY); } CS_HIGH(); return rv; } HAL_StatusTypeDef spi_write(uint32_t addr, int len, const uint8_t *buf) { // enable writing HAL_StatusTypeDef rv = write_enable(); if (rv) return rv; // do a "PAGE Program" aka. write uint8_t pkt[4] = { CMD_WRITE, (addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; CS_LOW(); rv = HAL_SPI_Transmit(&sf_spi_port, pkt, sizeof(pkt), HAL_MAX_DELAY); if (rv == HAL_OK) { rv = HAL_SPI_Transmit(&sf_spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY); } CS_HIGH(); if (rv == HAL_OK) { rv = wait_wip_done(); } return rv; } HAL_StatusTypeDef spi_setup(void) { // enable some internal clocks __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_SPI4_CLK_ENABLE(); // simple pins GPIO_InitTypeDef setup = { .Pin = SF_CS_PIN, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_MEDIUM, .Alternate = 0, }; HAL_GPIO_Init(GPIOE, &setup); // starting value: high HAL_GPIO_WritePin(GPIOE, SF_CS_PIN, 1); // SPI pins, on various ports setup.Pin = SF_SPI_SCK; setup.Mode = GPIO_MODE_AF_PP; setup.Alternate = GPIO_AF5_SPI4; HAL_GPIO_Init(GPIOE, &setup); setup.Pin = SF_SPI_MOSI | SF_SPI_MISO; HAL_GPIO_Init(GPIOE, &setup); memset(&sf_spi_port, 0, sizeof(sf_spi_port)); sf_spi_port.Instance = SPI4; // see SPI_InitTypeDef sf_spi_port.Init.Mode = SPI_MODE_MASTER; sf_spi_port.Init.Direction = SPI_DIRECTION_2LINES; sf_spi_port.Init.DataSize = SPI_DATASIZE_8BIT; sf_spi_port.Init.CLKPolarity = SPI_POLARITY_LOW; sf_spi_port.Init.CLKPhase = SPI_PHASE_1EDGE; sf_spi_port.Init.NSS = SPI_NSS_SOFT; sf_spi_port.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // conservative sf_spi_port.Init.FirstBit = SPI_FIRSTBIT_MSB; sf_spi_port.Init.TIMode = SPI_TIMODE_DISABLED; sf_spi_port.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; return HAL_SPI_Init(&sf_spi_port); }