You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

179 lines
4.5 KiB

// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. <hello@foundationdevices.com>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
// SPDX-License-Identifier: GPL-3.0-only
//
/*
* (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
* and is covered by GPLv3 license found in COPYING.
*
* sflash.c -- talk to the serial flash
*
*/
#include <string.h>
#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);
}