From 9ef82030e6a005df990f8f2924cf54076cd2e8da Mon Sep 17 00:00:00 2001 From: Vic Yang Date: Wed, 2 Jul 2014 14:55:07 -0700 Subject: [PATCH] Refactor STM32 SPI flash driver This CL factors out the SPI flash driver to be a STM32-specific SPI master driver and a common SPI flash driver. BUG=None TEST=Verify on Fruitpie BRANCH=None Change-Id: I9cca918299bc57a6532c85c4452e73f04550a424 Signed-off-by: Vic Yang Reviewed-on: https://chromium-review.googlesource.com/206582 Reviewed-by: Dmitry Torokhov Reviewed-by: Vincent Palatin Reviewed-by: Daming Chen Tested-by: Daming Chen --- board/fruitpie/board.h | 3 +- chip/stm32/build.mk | 2 +- chip/stm32/registers.h | 2 + chip/stm32/spi_master.c | 211 +++++++++++++++ common/build.mk | 1 + {chip/stm32 => common}/spi_flash.c | 402 ++++++++--------------------- include/config.h | 4 +- include/spi_flash.h | 88 ++++--- 8 files changed, 383 insertions(+), 330 deletions(-) create mode 100644 chip/stm32/spi_master.c rename {chip/stm32 => common}/spi_flash.c (70%) diff --git a/board/fruitpie/board.h b/board/fruitpie/board.h index 67bd82375d..e542cfedc2 100644 --- a/board/fruitpie/board.h +++ b/board/fruitpie/board.h @@ -27,7 +27,8 @@ #define CONFIG_USB_SWITCH_TSU6721 #define CONFIG_SPI_FLASH #define CONFIG_SPI_FLASH_SIZE 8388608 -#define CONFIG_SPI_FLASH_REGISTER STM32_SPI2_REGS +#define CONFIG_SPI_MASTER_PORT 2 +#define CONFIG_SPI_CS_GPIO GPIO_PD_TX_EN #undef CONFIG_WATCHDOG_HELP #undef CONFIG_LID_SWITCH #undef CONFIG_TASK_PROFILING diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index b0bdd6bf66..1700fb190d 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -26,7 +26,7 @@ TIMER_TYPE=$(if $(CONFIG_STM_HWTIMER32),32,) chip-y=dma.o system.o chip-y+=jtag-$(CHIP_FAMILY).o clock-$(CHIP_FAMILY).o chip-$(CONFIG_SPI)+=spi.o -chip-$(CONFIG_SPI_FLASH)+=spi_flash.o +chip-$(CONFIG_SPI_MASTER_PORT)+=spi_master.o chip-$(CONFIG_SW_CRC)+=crc.o chip-$(CONFIG_COMMON_GPIO)+=gpio-$(CHIP_FAMILY).o chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index f47452de84..21a44caa78 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -1113,6 +1113,8 @@ enum dma_channel { STM32_DMAC_I2C1_RX = STM32_DMAC_CH7, STM32_DMAC_PMSE_ROW = STM32_DMAC_CH6, STM32_DMAC_PMSE_COL = STM32_DMAC_CH7, + STM32_DMAC_SPI2_RX = STM32_DMAC_CH6, + STM32_DMAC_SPI2_TX = STM32_DMAC_CH7, /* Only DMA1 (with 7 channels) is present on STM32F100 and STM32L151x */ STM32_DMAC_COUNT = 7, diff --git a/chip/stm32/spi_master.c b/chip/stm32/spi_master.c new file mode 100644 index 0000000000..0103362461 --- /dev/null +++ b/chip/stm32/spi_master.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * SPI master driver. + */ + +#include "common.h" +#include "dma.h" +#include "gpio.h" +#include "shared_mem.h" +#include "spi.h" +#include "timer.h" +#include "util.h" + +#define SPI_REG_ADDR CONCAT3(STM32_SPI, CONFIG_SPI_MASTER_PORT, _BASE) +#define SPI_REG ((stm32_spi_regs_t *)SPI_REG_ADDR) +#define DMAC_SPI_TX CONCAT3(STM32_DMAC_SPI, CONFIG_SPI_MASTER_PORT, _TX) +#define DMAC_SPI_RX CONCAT3(STM32_DMAC_SPI, CONFIG_SPI_MASTER_PORT, _RX) + +#define SPI_TRANSACTION_TIMEOUT_USEC (800 * MSEC) + +/* Default DMA channel options */ +static const struct dma_option dma_tx_option = { + DMAC_SPI_TX, (void *)&SPI_REG->dr, + STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT +}; + +static const struct dma_option dma_rx_option = { + DMAC_SPI_RX, (void *)&SPI_REG->dr, + STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT +}; + +static uint8_t spi_enabled; + +/** + * Initialize SPI module, registers, and clocks + */ +static int spi_master_initialize(void) +{ + stm32_spi_regs_t *spi = SPI_REG; + + /* + * Set SPI master, baud rate, and software slave control. + * Set SPI clock rate to DIV2R = 24 MHz + * */ + spi->cr1 = STM32_SPI_CR1_MSTR | STM32_SPI_CR1_SSM | STM32_SPI_CR1_SSI; + + /* + * Configure 8-bit datasize, set FRXTH, enable DMA, + * and enable NSS output + */ + spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN | + STM32_SPI_CR2_FRXTH | STM32_SPI_CR2_DATASIZE(8); + + /* Enable SPI */ + spi->cr1 |= STM32_SPI_CR1_SPE; + + /* Drive SS high */ + gpio_set_level(CONFIG_SPI_CS_GPIO, 1); + + /* Set flag */ + spi_enabled = 1; + + return EC_SUCCESS; +} + +/** + * Shutdown SPI module + */ +static int spi_master_shutdown(void) +{ + int rv = EC_SUCCESS; + stm32_spi_regs_t *spi = SPI_REG; + char dummy __attribute__((unused)); + + /* Set flag */ + spi_enabled = 0; + + /* Disable DMA streams */ + dma_disable(dma_tx_option.channel); + dma_disable(dma_rx_option.channel); + + /* Disable SPI */ + spi->cr1 &= ~STM32_SPI_CR1_SPE; + + /* Read until FRLVL[1:0] is empty */ + while (spi->sr & STM32_SPI_SR_FTLVL) + dummy = spi->dr; + + /* Disable DMA buffers */ + spi->cr2 &= ~(STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN); + + return rv; +} + +int spi_enable(int enable) +{ + if (enable == spi_enabled) + return EC_SUCCESS; + if (enable) + return spi_master_initialize(); + else + return spi_master_shutdown(); +} + +static int spi_dma_start(const uint8_t *txdata, uint8_t *rxdata, int len) +{ + stm32_dma_chan_t *txdma; + + /* Set up RX DMA */ + dma_start_rx(&dma_rx_option, len, rxdata); + + /* Set up TX DMA */ + txdma = dma_get_channel(dma_tx_option.channel); + dma_prepare_tx(&dma_tx_option, len, txdata); + dma_go(txdma); + + return EC_SUCCESS; +} + +static int spi_dma_wait(void) +{ + timestamp_t timeout; + stm32_spi_regs_t *spi = SPI_REG; + + /* Wait for DMA transmission to complete */ + dma_wait(dma_tx_option.channel); + + timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC; + /* Wait for FIFO empty and BSY bit clear to indicate completion */ + while ((spi->sr & STM32_SPI_SR_FTLVL) || (spi->sr & STM32_SPI_SR_BSY)) + if (get_time().val > timeout.val) + return EC_ERROR_TIMEOUT; + + /* Disable TX DMA */ + dma_disable(dma_tx_option.channel); + + /* Wait for DMA reception to complete */ + dma_wait(dma_rx_option.channel); + + timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC; + /* Wait for FRLVL[1:0] to indicate FIFO empty */ + while (spi->sr & STM32_SPI_SR_FRLVL) + if (get_time().val > timeout.val) + return EC_ERROR_TIMEOUT; + + /* Disable RX DMA */ + dma_disable(dma_rx_option.channel); + + return EC_SUCCESS; +} + +int spi_transaction_async(const uint8_t *txdata, int txlen, + uint8_t *rxdata, int rxlen) +{ + int rv = EC_SUCCESS; + stm32_spi_regs_t *spi = SPI_REG; + char *buf; + + rv = shared_mem_acquire(MAX(txlen, rxlen), &buf); + if (rv != EC_SUCCESS) + goto err_free; + + /* Drive SS low */ + gpio_set_level(CONFIG_SPI_CS_GPIO, 0); + + /* Clear out the FIFO. */ + while (spi->sr & STM32_SPI_SR_FRLVL) + (void) (uint8_t) spi->dr; + + rv = spi_dma_start(txdata, buf, txlen); + if (rv != EC_SUCCESS) + goto err_free; + + rv = spi_dma_wait(); + if (rv != EC_SUCCESS) + goto err_free; + + if (rxlen) { + rv = spi_dma_start(buf, rxdata, rxlen); + if (rv != EC_SUCCESS) + goto err_free; + } + +err_free: + shared_mem_release(buf); + return rv; +} + +int spi_transaction_flush(void) +{ + int rv = spi_dma_wait(); + + /* Drive SS high */ + gpio_set_level(CONFIG_SPI_CS_GPIO, 1); + + return rv; +} + +int spi_transaction(const uint8_t *txdata, int txlen, + uint8_t *rxdata, int rxlen) +{ + int rv; + + rv = spi_transaction_async(txdata, txlen, rxdata, rxlen); + rv |= spi_transaction_flush(); + + return rv; +} diff --git a/common/build.mk b/common/build.mk index 09bbfe2564..6b2dd5dbfb 100644 --- a/common/build.mk +++ b/common/build.mk @@ -60,6 +60,7 @@ common-$(CONFIG_PWM)+=pwm.o common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o common-$(CONFIG_SHA1)+=sha1.o common-$(CONFIG_SOFTWARE_CLZ)+=clz.o +common-$(CONFIG_SPI_FLASH)+=spi_flash.o common-$(CONFIG_SWITCH)+=switch.o common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o diff --git a/chip/stm32/spi_flash.c b/common/spi_flash.c similarity index 70% rename from chip/stm32/spi_flash.c rename to common/spi_flash.c index 966ff49dbc..20e4ce37cb 100644 --- a/chip/stm32/spi_flash.c +++ b/common/spi_flash.c @@ -3,35 +3,18 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * - * SPI flash driver for Chrome EC, particularly fruitpie board with Winbond - * W25Q64FVZPIG flash memory. - * - * This uses DMA to handle transmission and reception. + * SPI flash driver for Chrome EC, particularly Winbond W25Q64FV. */ -#include "config.h" +#include "common.h" #include "console.h" -#include "dma.h" -#include "gpio.h" -#include "hooks.h" -#include "registers.h" #include "shared_mem.h" +#include "spi.h" #include "spi_flash.h" #include "timer.h" #include "util.h" #include "watchdog.h" -/* Default DMA channel options */ -static const struct dma_option dma_tx_option = { - STM32_DMAC_CH7, (void *)&CONFIG_SPI_FLASH_REGISTER->dr, - STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT -}; - -static const struct dma_option dma_rx_option = { - STM32_DMAC_CH6, (void *)&CONFIG_SPI_FLASH_REGISTER->dr, - STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT -}; - /* * Time to sleep when chip is busy */ @@ -42,112 +25,37 @@ static const struct dma_option dma_rx_option = { */ #define SPI_FLASH_TIMEOUT_USEC (800*MSEC) -/* - * Maximum message size (in bytes) for the W25Q64FV SPI flash - * Instruction (1) + Address (3) + Data (256) = 260 - * Limited by chip maximum input length for write instruction - */ -#define SPI_FLASH_MAX_MESSAGE_SIZE 260 - /* * Registers for the W25Q64FV SPI flash */ -#define SPI_FLASH_SR2_SUS (1 << 7) -#define SPI_FLASH_SR2_CMP (1 << 6) -#define SPI_FLASH_SR2_LB3 (1 << 5) -#define SPI_FLASH_SR2_LB2 (1 << 4) -#define SPI_FLASH_SR2_LB1 (1 << 3) -#define SPI_FLASH_SR2_QE (1 << 1) +#define SPI_FLASH_SR2_SUS (1 << 7) +#define SPI_FLASH_SR2_CMP (1 << 6) +#define SPI_FLASH_SR2_LB3 (1 << 5) +#define SPI_FLASH_SR2_LB2 (1 << 4) +#define SPI_FLASH_SR2_LB1 (1 << 3) +#define SPI_FLASH_SR2_QE (1 << 1) #define SPI_FLASH_SR2_SRP1 (1 << 0) #define SPI_FLASH_SR1_SRP0 (1 << 7) -#define SPI_FLASH_SR1_SEC (1 << 6) -#define SPI_FLASH_SR1_TB (1 << 5) -#define SPI_FLASH_SR1_BP2 (1 << 4) -#define SPI_FLASH_SR1_BP1 (1 << 3) -#define SPI_FLASH_SR1_BP0 (1 << 2) -#define SPI_FLASH_SR1_WEL (1 << 1) +#define SPI_FLASH_SR1_SEC (1 << 6) +#define SPI_FLASH_SR1_TB (1 << 5) +#define SPI_FLASH_SR1_BP2 (1 << 4) +#define SPI_FLASH_SR1_BP1 (1 << 3) +#define SPI_FLASH_SR1_BP0 (1 << 2) +#define SPI_FLASH_SR1_WEL (1 << 1) #define SPI_FLASH_SR1_BUSY (1 << 0) /* Internal buffer used by SPI flash driver */ static uint8_t buf[SPI_FLASH_MAX_MESSAGE_SIZE]; -static uint8_t spi_enabled; - -/** - * Sends and receives a message. Limited to SPI_FLASH_MAX_MESSAGE_SIZE. - * @param snd_len Number of message bytes to send - * @param rcv_len Number of bytes to receive - * @return EC_SUCCESS, or non-zero if any error. - */ -static int communicate(int snd_len, int rcv_len) -{ - int rv = EC_SUCCESS; - timestamp_t timeout; - stm32_dma_chan_t *txdma; - stm32_spi_regs_t *spi = CONFIG_SPI_FLASH_REGISTER; - - /* Enable SPI if it is disabled */ - if (!spi_enabled) - spi_flash_initialize(); - - /* Buffer overflow */ - if (snd_len + rcv_len > SPI_FLASH_MAX_MESSAGE_SIZE) - return EC_ERROR_OVERFLOW; - - /* Wipe send buffer from snd_len to snd_len + rcv_len */ - memset(buf + snd_len, 0, rcv_len); - - /* Drive SS low */ - gpio_set_level(GPIO_PD_TX_EN, 0); - - /* Clear out the FIFO. */ - while (spi->sr & STM32_SPI_SR_FRLVL) - (void) (uint8_t) spi->dr; - - /* Set up RX DMA */ - dma_start_rx(&dma_rx_option, snd_len + rcv_len, buf); - - /* Set up TX DMA */ - txdma = dma_get_channel(dma_tx_option.channel); - dma_prepare_tx(&dma_tx_option, snd_len + rcv_len, buf); - dma_go(txdma); - - /* Wait for DMA transmission to complete */ - dma_wait(dma_tx_option.channel); - - timeout.val = get_time().val + SPI_FLASH_TIMEOUT_USEC; - /* Wait for FIFO empty and BSY bit clear to indicate completion */ - while ((spi->sr & STM32_SPI_SR_FTLVL) || (spi->sr & STM32_SPI_SR_BSY)) - if (get_time().val > timeout.val) - return EC_ERROR_TIMEOUT; - - /* Disable TX DMA */ - dma_disable(dma_tx_option.channel); - - /* Wait for DMA reception to complete */ - dma_wait(dma_rx_option.channel); - - timeout.val = get_time().val + SPI_FLASH_TIMEOUT_USEC; - /* Wait for FRLVL[1:0] to indicate FIFO empty */ - while (spi->sr & STM32_SPI_SR_FRLVL) - if (get_time().val > timeout.val) - return EC_ERROR_TIMEOUT; - - /* Disable RX DMA */ - dma_disable(dma_rx_option.channel); - - /* Drive SS high */ - gpio_set_level(GPIO_PD_TX_EN, 1); - - return rv; -} /** * Computes block write protection range from registers * Returns start == len == 0 for no protection + * * @param sr1 Status register 1 * @param sr2 Status register 2 * @param start Output pointer for protection start offset * @param len Output pointer for protection length + * * @return EC_SUCCESS, or non-zero if any error. */ static int reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start, @@ -204,10 +112,12 @@ static int reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start, /** * Computes block write protection registers from range + * * @param start Desired protection start offset * @param len Desired protection length * @param sr1 Output pointer for status register 1 * @param sr2 Output pointer for status register 2 + * * @return EC_SUCCESS, or non-zero if any error. */ static int protect_to_reg(unsigned int start, unsigned int len, @@ -271,18 +181,10 @@ static int protect_to_reg(unsigned int start, unsigned int len, return EC_SUCCESS; } -/** - * Determines whether SPI is initialized - * @return 1 if initialized, 0 otherwise. - */ -int spi_flash_ready(void) -{ - return spi_enabled; -} - /** * Waits for chip to finish current operation. Must be called after * erase/write operations to ensure successive commands are executed. + * * @return EC_SUCCESS or error on timeout */ int spi_flash_wait(void) @@ -301,74 +203,13 @@ int spi_flash_wait(void) return EC_SUCCESS; } -/** - * Initialize SPI module, registers, and clocks - */ -void spi_flash_initialize(void) -{ - stm32_spi_regs_t *spi = CONFIG_SPI_FLASH_REGISTER; - - /* Set SPI master, baud rate, and software slave control */ - /* Set SPI clock rate to DIV2R = 24 MHz */ - spi->cr1 = STM32_SPI_CR1_MSTR | STM32_SPI_CR1_SSM | STM32_SPI_CR1_SSI; - - /* - * Configure 8-bit datasize, set FRXTH, enable DMA, - * and enable NSS output - */ - spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN | - STM32_SPI_CR2_FRXTH | STM32_SPI_CR2_DATASIZE(8); - - /* Enable SPI */ - spi->cr1 |= STM32_SPI_CR1_SPE; - - /* Drive SS high */ - gpio_set_level(GPIO_PD_TX_EN, 1); - - /* Set flag */ - spi_enabled = 1; -} - -/** - * Shutdown SPI - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_shutdown(void) -{ - int rv = EC_SUCCESS; - stm32_spi_regs_t *spi = CONFIG_SPI_FLASH_REGISTER; - - /* Set flag */ - spi_enabled = 0; - - /* Disable DMA streams */ - dma_disable(dma_tx_option.channel); - dma_disable(dma_rx_option.channel); - - - - /* Disable SPI */ - spi->cr1 &= ~STM32_SPI_CR1_SPE; - - /* Read until FRLVL[1:0] is empty */ - while (spi->sr & STM32_SPI_SR_FTLVL) - buf[0] = spi->dr; - - /* Disable DMA buffers */ - spi->cr2 &= ~(STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN); - - return rv; -} - /** * Set the write enable latch */ static int spi_flash_write_enable(void) { - /* Compose instruction */ - buf[0] = SPI_FLASH_WRITE_ENABLE; - - return communicate(1, 0); + uint8_t cmd = SPI_FLASH_WRITE_ENABLE; + return spi_transaction(&cmd, 1, NULL, 0); } /** @@ -377,12 +218,13 @@ static int spi_flash_write_enable(void) */ uint8_t spi_flash_get_status1(void) { - /* Get SR 1 */ - buf[0] = SPI_FLASH_READ_SR1; - if (communicate(1, 1)) + uint8_t cmd = SPI_FLASH_READ_SR1; + uint8_t resp; + + if (spi_transaction(&cmd, 1, &resp, 1) != EC_SUCCESS) return -1; - return buf[1]; + return resp; } /** @@ -391,23 +233,27 @@ uint8_t spi_flash_get_status1(void) */ uint8_t spi_flash_get_status2(void) { - /* Get SR 2 */ - buf[0] = SPI_FLASH_READ_SR2; - if (communicate(1, 1)) + uint8_t cmd = SPI_FLASH_READ_SR2; + uint8_t resp; + + if (spi_transaction(&cmd, 1, &resp, 1) != EC_SUCCESS) return -1; - return buf[1]; + return resp; } /** * Sets the SPI flash status registers (non-volatile bits only) * Pass reg2 == -1 to only set reg1. + * * @param reg1 Status register 1 * @param reg2 Status register 2 (optional) + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_set_status(int reg1, int reg2) { + uint8_t cmd[3] = {SPI_FLASH_WRITE_SR, reg1, reg2}; int rv = EC_SUCCESS; /* Register has protection */ @@ -420,15 +266,10 @@ int spi_flash_set_status(int reg1, int reg2) if (rv) return rv; - /* Compose instruction */ - buf[0] = SPI_FLASH_WRITE_SR; - buf[1] = reg1; - buf[2] = reg2; - if (reg2 == -1) - rv = communicate(2, 0); + rv = spi_transaction(cmd, 2, NULL, 0); else - rv = communicate(3, 0); + rv = spi_transaction(cmd, 3, NULL, 0); if (rv) return rv; @@ -437,40 +278,37 @@ int spi_flash_set_status(int reg1, int reg2) /** * Returns the content of SPI flash + * * @param buf Buffer to write flash contents * @param offset Flash offset to start reading from * @param bytes Number of bytes to read. Limited by receive buffer to 256. + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_read(uint8_t *buf_usr, unsigned int offset, unsigned int bytes) { - int rv = EC_SUCCESS; + uint8_t cmd[4] = {SPI_FLASH_READ, + (offset >> 16) & 0xFF, + (offset >> 8) & 0xFF, + offset & 0xFF}; if (offset + bytes > CONFIG_SPI_FLASH_SIZE) return EC_ERROR_INVAL; - /* Compose instruction */ - buf[0] = SPI_FLASH_READ; - buf[1] = (offset >> 16) & 0xFF; - buf[2] = (offset >> 8) & 0xFF; - buf[3] = offset & 0xFF; - - rv = communicate(4, bytes); - if (rv) - return rv; - - memcpy(buf_usr, buf + 4, bytes); - return rv; + return spi_transaction(cmd, 4, buf_usr, bytes); } /** * Erase a block of SPI flash. + * * @param offset Flash offset to start erasing * @param block Block size in kb (4 or 32) + * * @return EC_SUCCESS, or non-zero if any error. */ static int spi_flash_erase_block(unsigned int offset, unsigned int block) { + uint8_t cmd[4]; int rv = EC_SUCCESS; /* Invalid block size */ @@ -492,12 +330,12 @@ static int spi_flash_erase_block(unsigned int offset, unsigned int block) return rv; /* Compose instruction */ - buf[0] = (block == 4) ? SPI_FLASH_ERASE_4KB : SPI_FLASH_ERASE_32KB; - buf[1] = (offset >> 16) & 0xFF; - buf[2] = (offset >> 8) & 0xFF; - buf[3] = offset & 0xFF; + cmd[0] = (block == 4) ? SPI_FLASH_ERASE_4KB : SPI_FLASH_ERASE_32KB; + cmd[1] = (offset >> 16) & 0xFF; + cmd[2] = (offset >> 8) & 0xFF; + cmd[3] = offset & 0xFF; - rv = communicate(4, 0); + rv = spi_transaction(cmd, 4, NULL, 0); if (rv) return rv; @@ -506,8 +344,10 @@ static int spi_flash_erase_block(unsigned int offset, unsigned int block) /** * Erase SPI flash. + * * @param offset Flash offset to start erasing * @param bytes Number of bytes to erase + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_erase(unsigned int offset, unsigned int bytes) @@ -550,19 +390,21 @@ int spi_flash_erase(unsigned int offset, unsigned int bytes) /** * Write to SPI flash. Assumes already erased. * Limited to SPI_FLASH_MAX_WRITE_SIZE by chip. + * * @param offset Flash offset to write * @param bytes Number of bytes to write * @param data Data to write to flash + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_write(unsigned int offset, unsigned int bytes, const uint8_t const *data) { - int rv = EC_SUCCESS; + int rv; /* Invalid input */ if (!data || offset + bytes > CONFIG_SPI_FLASH_SIZE || - bytes > SPI_FLASH_MAX_WRITE_SIZE) + bytes > SPI_FLASH_MAX_WRITE_SIZE) return EC_ERROR_INVAL; /* Enable writing to SPI flash */ @@ -570,83 +412,72 @@ int spi_flash_write(unsigned int offset, unsigned int bytes, if (rv) return rv; + /* Copy data to send buffer; buffers may overlap */ + memmove(buf + 4, data, bytes); + /* Compose instruction */ buf[0] = SPI_FLASH_PAGE_PRGRM; buf[1] = (offset >> 16) & 0xFF; buf[2] = (offset >> 8) & 0xFF; buf[3] = offset & 0xFF; - /* Copy data to send buffer; buffers may overlap */ - memcpy(buf + 4, data, bytes); - - return communicate(4 + bytes, 0); + return spi_transaction(buf, 4 + bytes, NULL, 0); } /** * Returns the SPI flash manufacturer ID and device ID [8:0] + * * @return flash manufacturer + device ID or -1 on error */ uint16_t spi_flash_get_id(void) { - uint16_t res; + uint8_t cmd[4] = {SPI_FLASH_MFR_DEV_ID, 0, 0, 0}; + uint8_t resp[2]; - /* Compose instruction */ - buf[0] = SPI_FLASH_MFR_DEV_ID; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - - if (communicate(4, 2)) + if (spi_transaction(cmd, 4, resp, 2) != EC_SUCCESS) return -1; - res = (buf[4] << 8) | buf[5]; - return res; + return (resp[4] << 8) | resp[5]; } /** * Returns the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity) + * * @return flash JEDEC ID or -1 on error */ uint32_t spi_flash_get_jedec_id(void) { - uint32_t res; + uint8_t cmd = SPI_FLASH_JEDEC_ID; + uint32_t resp; - /* Compose instruction */ - buf[0] = SPI_FLASH_JEDEC_ID; - - if (communicate(1, 4)) + if (spi_transaction(&cmd, 1, (uint8_t *)&resp, 4) != EC_SUCCESS) return -1; - memcpy((uint8_t *) &res, buf + 1, 4); - return res; + return resp; } /** * Returns the SPI flash unique ID (serial) + * * @return flash unique ID or -1 on error */ uint64_t spi_flash_get_unique_id(void) { - uint64_t res; + uint8_t cmd[5] = {SPI_FLASH_UNIQUE_ID, 0, 0, 0, 0}; + uint64_t resp; - /* Compose instruction */ - buf[0] = SPI_FLASH_UNIQUE_ID; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - buf[4] = 0; - - if (communicate(5, 8)) + if (spi_transaction(cmd, 5, (uint8_t *)&resp, 8) != EC_SUCCESS) return -1; - memcpy((uint8_t *) &res, buf + 5, 8); - return res; + return resp; } /** * Check for SPI flash status register write protection * Cannot sample WP pin, will consider hardware WP to be no protection + * * @param wp Status register write protection mode + * * @return EC_SUCCESS for no protection, or non-zero if error. */ int spi_flash_check_wp(void) @@ -662,10 +493,12 @@ int spi_flash_check_wp(void) /** * Set SPI flash status register write protection + * * @param wp Status register write protection mode + * * @return EC_SUCCESS for no protection, or non-zero if error. */ -int spi_flash_set_wp(enum wp w) +int spi_flash_set_wp(enum spi_flash_wp w) { int sr1 = spi_flash_get_status1(); int sr2 = spi_flash_get_status2(); @@ -696,8 +529,10 @@ int spi_flash_set_wp(enum wp w) /** * Check for SPI flash block write protection + * * @param offset Flash block offset to check * @param bytes Flash block length to check + * * @return EC_SUCCESS for no protection, or non-zero if error. */ int spi_flash_check_protect(unsigned int offset, unsigned int bytes) @@ -727,8 +562,10 @@ int spi_flash_check_protect(unsigned int offset, unsigned int bytes) /** * Set SPI flash block write protection * If offset == bytes == 0, remove protection. + * * @param offset Flash block offset to protect * @param bytes Flash block length to protect + * * @return EC_SUCCESS, or non-zero if error. */ int spi_flash_set_protect(unsigned int offset, unsigned int bytes) @@ -753,7 +590,9 @@ static int command_spi_flashinfo(int argc, char **argv) { uint32_t jedec; uint64_t unique; - int rv = EC_SUCCESS; + int rv; + + spi_enable(1); /* Wait for previous operation to complete */ rv = spi_flash_wait(); @@ -791,6 +630,8 @@ static int command_spi_flasherase(int argc, char **argv) if (rv) return rv; + spi_enable(1); + /* Chip has protection */ if (spi_flash_check_protect(offset, bytes)) return EC_ERROR_ACCESS_DENIED; @@ -805,10 +646,8 @@ static int command_spi_flasherase(int argc, char **argv) if (rv) return rv; - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - - return rv; + /* Wait for the operation to complete */ + return spi_flash_wait(); } DECLARE_CONSOLE_COMMAND(spi_flasherase, command_spi_flasherase, "offset [bytes]", @@ -817,7 +656,6 @@ DECLARE_CONSOLE_COMMAND(spi_flasherase, command_spi_flasherase, static int command_spi_flashwrite(int argc, char **argv) { - char *data; int offset = -1; int bytes = SPI_FLASH_MAX_WRITE_SIZE; int write_len; @@ -828,18 +666,15 @@ static int command_spi_flashwrite(int argc, char **argv) if (rv) return rv; + spi_enable(1); + /* Chip has protection */ if (spi_flash_check_protect(offset, bytes)) return EC_ERROR_ACCESS_DENIED; - /* Acquire the shared memory buffer */ - rv = shared_mem_acquire(SPI_FLASH_MAX_WRITE_SIZE, &data); - if (rv) - goto err_free; - /* Fill the data buffer with a pattern */ for (i = 0; i < SPI_FLASH_MAX_WRITE_SIZE; i++) - data[i] = i; + buf[i] = i; ccprintf("Writing %d bytes to 0x%x...\n", bytes, offset); while (bytes > 0) { @@ -852,12 +687,12 @@ static int command_spi_flashwrite(int argc, char **argv) /* Wait for previous operation to complete */ rv = spi_flash_wait(); if (rv) - goto err_free; + return rv; /* Perform write */ - rv = spi_flash_write(offset, write_len, data); + rv = spi_flash_write(offset, write_len, buf); if (rv) - goto err_free; + return rv; offset += write_len; bytes -= write_len; @@ -865,17 +700,7 @@ static int command_spi_flashwrite(int argc, char **argv) ASSERT(bytes == 0); -err_free: - /* Free the buffer */ - shared_mem_release(data); - - /* Don't clobber return value */ - if (rv) - spi_flash_wait(); - else - rv = spi_flash_wait(); - - return rv; + return spi_flash_wait(); } DECLARE_CONSOLE_COMMAND(spi_flashwrite, command_spi_flashwrite, "offset [bytes]", @@ -888,12 +713,14 @@ static int command_spi_flashread(int argc, char **argv) int offset = -1; int bytes = -1; int read_len; - int rv = EC_SUCCESS; + int rv; rv = parse_offset_size(argc, argv, 1, &offset, &bytes); if (rv) return rv; + spi_enable(1); + /* Can't read past size of memory */ if (offset + bytes > CONFIG_SPI_FLASH_SIZE) return EC_ERROR_INVAL; @@ -941,11 +768,10 @@ DECLARE_CONSOLE_COMMAND(spi_flashread, command_spi_flashread, static int command_spi_flashread_sr(int argc, char **argv) { - uint8_t sr1 = spi_flash_get_status1(); - uint8_t sr2 = spi_flash_get_status2(); + spi_enable(1); - ccprintf("Status Register 1: 0x%02x\nStatus Register 2: 0x%02x\n", - sr1, sr2); + ccprintf("Status Register 1: 0x%02x\n", spi_flash_get_status1()); + ccprintf("Status Register 2: 0x%02x\n", spi_flash_get_status2()); return EC_SUCCESS; } @@ -963,6 +789,8 @@ static int command_spi_flashwrite_sr(int argc, char **argv) if (rv) return rv; + spi_enable(1); + /* Wait for previous operation to complete */ rv = spi_flash_wait(); if (rv) @@ -974,10 +802,8 @@ static int command_spi_flashwrite_sr(int argc, char **argv) if (rv) return rv; - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - - return rv; + /* Wait for the operation to complete */ + return spi_flash_wait(); } DECLARE_CONSOLE_COMMAND(spi_flash_wsr, command_spi_flashwrite_sr, "value1 value2", @@ -993,6 +819,8 @@ static int command_spi_flashprotect(int argc, char **argv) if (rv) return rv; + spi_enable(1); + /* Wait for previous operation to complete */ rv = spi_flash_wait(); if (rv) @@ -1003,10 +831,8 @@ static int command_spi_flashprotect(int argc, char **argv) if (rv) return rv; - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - - return rv; + /* Wait for the operation to complete */ + return spi_flash_wait(); } DECLARE_CONSOLE_COMMAND(spi_flash_prot, command_spi_flashprotect, "offset len", diff --git a/include/config.h b/include/config.h index 8bb62e0a0b..1d546f7fff 100644 --- a/include/config.h +++ b/include/config.h @@ -765,8 +765,8 @@ /* Size (bytes) of SPI flash memory */ #undef CONFIG_SPI_FLASH_SIZE -/* SPI module register used for flash interface */ -#undef CONFIG_SPI_FLASH_REGISTER +/* SPI module port used for master interface */ +#undef CONFIG_SPI_MASTER_PORT /* Default stack size to use for tasks, in bytes */ #undef CONFIG_STACK_SIZE diff --git a/include/spi_flash.h b/include/spi_flash.h index 4c45a90713..50065b101f 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -14,35 +14,41 @@ /* SPI flash instructions */ #define SPI_FLASH_WRITE_ENABLE 0x06 #define SPI_FLASH_WRITE_DISABLE 0x04 -#define SPI_FLASH_READ_SR1 0x05 -#define SPI_FLASH_READ_SR2 0x35 -#define SPI_FLASH_WRITE_SR 0x01 -#define SPI_FLASH_ERASE_4KB 0x20 +#define SPI_FLASH_READ_SR1 0x05 +#define SPI_FLASH_READ_SR2 0x35 +#define SPI_FLASH_WRITE_SR 0x01 +#define SPI_FLASH_ERASE_4KB 0x20 #define SPI_FLASH_ERASE_32KB 0x52 #define SPI_FLASH_ERASE_64KB 0xD8 #define SPI_FLASH_ERASE_CHIP 0xC7 -#define SPI_FLASH_READ 0x03 +#define SPI_FLASH_READ 0x03 #define SPI_FLASH_PAGE_PRGRM 0x02 #define SPI_FLASH_REL_PWRDWN 0xAB #define SPI_FLASH_MFR_DEV_ID 0x90 -#define SPI_FLASH_JEDEC_ID 0x9F -#define SPI_FLASH_UNIQUE_ID 0x4B -#define SPI_FLASH_SFDP 0x44 +#define SPI_FLASH_JEDEC_ID 0x9F +#define SPI_FLASH_UNIQUE_ID 0x4B +#define SPI_FLASH_SFDP 0x44 #define SPI_FLASH_ERASE_SEC_REG 0x44 #define SPI_FLASH_PRGRM_SEC_REG 0x42 #define SPI_FLASH_READ_SEC_REG 0x48 #define SPI_FLASH_ENABLE_RESET 0x66 -#define SPI_FLASH_RESET 0x99 +#define SPI_FLASH_RESET 0x99 /* Maximum single write size (in bytes) for the W25Q64FV SPI flash */ -#define SPI_FLASH_MAX_WRITE_SIZE 256 +#define SPI_FLASH_MAX_WRITE_SIZE 256 -/* Maximum single read size (in bytes) for the W25Q64FV SPI flash */ -/* Limited by size of internal driver buffer, not the chip */ +/* + * Maximum message size (in bytes) for the W25Q64FV SPI flash + * Instruction (1) + Address (3) + Data (256) = 260 + * Limited by chip maximum input length of write instruction + */ +#define SPI_FLASH_MAX_MESSAGE_SIZE (SPI_FLASH_MAX_WRITE_SIZE + 4) + +/* Maximum single read size in bytes. Limited by size of the message buffer */ #define SPI_FLASH_MAX_READ_SIZE (SPI_FLASH_MAX_MESSAGE_SIZE - 4) /* Status register write protect structure */ -enum wp { +enum spi_flash_wp { SPI_WP_NONE, SPI_WP_HARDWARE, SPI_WP_POWER_CYCLE, @@ -50,37 +56,23 @@ enum wp { }; /** - * Determines whether SPI is initialized - * @return 1 if initialized, 0 otherwise. - */ -int spi_flash_ready(void); - -/** - * Waits for chip to finish current operation. Must be called after - * erase/write operations to ensure successive commands are executed. - * @return EC_SUCCESS or error on timeout + * Waits for the chip to finish the current operation. Must be called + * after erase/erite operations to ensure successive commands are executed. + * + * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_wait(void); -/** - * Initialize SPI module, registers, and clocks - */ -void spi_flash_initialize(void); - -/** - * Shutdown SPI - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_shutdown(void); - /** * Returns the contents of SPI flash status register 1 + * * @return register contents */ uint8_t spi_flash_get_status1(void); /** * Returns the contents of SPI flash status register 2 + * * @return register contents */ uint8_t spi_flash_get_status2(void); @@ -88,25 +80,31 @@ uint8_t spi_flash_get_status2(void); /** * Sets the SPI flash status registers (non-volatile bits only) * Pass reg2 == -1 to only set reg1. + * * @param reg1 Status register 1 * @param reg2 Status register 2 (optional) + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_set_status(int reg1, int reg2); /** * Returns the content of SPI flash + * * @param buf Buffer to write flash contents * @param offset Flash offset to start reading from * @param bytes Number of bytes to read. Limited by receive buffer to 256. + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_read(uint8_t *buf, unsigned int offset, unsigned int bytes); /** * Erase SPI flash. + * * @param offset Flash offset to start erasing * @param bytes Number of bytes to erase + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_erase(unsigned int offset, unsigned int bytes); @@ -114,9 +112,11 @@ int spi_flash_erase(unsigned int offset, unsigned int bytes); /** * Write to SPI flash. Assumes already erased. * Limited to SPI_FLASH_MAX_WRITE_SIZE by chip. + * * @param offset Flash offset to write * @param bytes Number of bytes to write * @param data Data to write to flash + * * @return EC_SUCCESS, or non-zero if any error. */ int spi_flash_write(unsigned int offset, unsigned int bytes, @@ -124,50 +124,62 @@ int spi_flash_write(unsigned int offset, unsigned int bytes, /** * Returns the SPI flash manufacturer ID and device ID [8:0] + * * @return flash manufacturer + device ID */ uint16_t spi_flash_get_id(void); /** * Returns the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity) + * * @return flash JEDEC ID */ uint32_t spi_flash_get_jedec_id(void); /** * Returns the SPI flash unique ID (serial) + * * @return flash unique ID */ uint64_t spi_flash_get_unique_id(void); /** - * Check for SPI flash status register write protection - * Cannot sample WP pin, will consider hardware WP to be no protection + * Check for SPI flash status register write protection. + * Note that this does not check the hardware WP pin as we might not be + * able to read the WP pin status. + * * @param wp Status register write protection mode + * * @return EC_SUCCESS for no protection, or non-zero if error. */ int spi_flash_check_wp(void); /** * Set SPI flash status register write protection + * * @param wp Status register write protection mode + * * @return EC_SUCCESS for no protection, or non-zero if error. */ -int spi_flash_set_wp(enum wp); +int spi_flash_set_wp(enum spi_flash_wp); /** * Check for SPI flash block write protection + * * @param offset Flash block offset to check * @param bytes Flash block length to check - * @return EC_SUCCESS for no protection, or non-zero if error. + * + * @return EC_SUCCESS if no protection, or non-zero if error. */ int spi_flash_check_protect(unsigned int offset, unsigned int bytes); /** - * Set SPI flash block write protection + * Set SPI flash block write protection. * If offset == bytes == 0, remove protection. + * * @param offset Flash block offset to protect * @param bytes Flash block length to protect + * * @return EC_SUCCESS, or non-zero if error. */ int spi_flash_set_protect(unsigned int offset, unsigned int bytes);