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
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);
|
|
}
|
|
|