summaryrefslogtreecommitdiff
path: root/chip/stm32
diff options
context:
space:
mode:
authorVic Yang <victoryang@chromium.org>2014-07-02 14:55:07 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-07-15 09:07:40 +0000
commit9ef82030e6a005df990f8f2924cf54076cd2e8da (patch)
tree95706def6813f4891bb27981cb1698c818f80b30 /chip/stm32
parent94126fbfa34030e3469d2d7309d29e199a0daa24 (diff)
downloadchrome-ec-9ef82030e6a005df990f8f2924cf54076cd2e8da.tar.gz
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 <victoryang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/206582 Reviewed-by: Dmitry Torokhov <dtor@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Daming Chen <ddchen@chromium.org> Tested-by: Daming Chen <ddchen@chromium.org>
Diffstat (limited to 'chip/stm32')
-rw-r--r--chip/stm32/build.mk2
-rw-r--r--chip/stm32/registers.h2
-rw-r--r--chip/stm32/spi_flash.c1015
-rw-r--r--chip/stm32/spi_master.c211
4 files changed, 214 insertions, 1016 deletions
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_flash.c b/chip/stm32/spi_flash.c
deleted file mode 100644
index 966ff49dbc..0000000000
--- a/chip/stm32/spi_flash.c
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * 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 flash driver for Chrome EC, particularly fruitpie board with Winbond
- * W25Q64FVZPIG flash memory.
- *
- * This uses DMA to handle transmission and reception.
- */
-
-#include "config.h"
-#include "console.h"
-#include "dma.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "registers.h"
-#include "shared_mem.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
- */
-#define SPI_FLASH_SLEEP_USEC 100
-
-/*
- * This is the max time for 32kb flash erase
- */
-#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_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_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,
- unsigned int *len)
-{
- int blocks;
- int size;
- uint8_t cmp;
- uint8_t sec;
- uint8_t tb;
- uint8_t bp;
-
- /* Determine flags */
- cmp = (sr2 & SPI_FLASH_SR2_CMP) ? 1 : 0;
- sec = (sr1 & SPI_FLASH_SR1_SEC) ? 1 : 0;
- tb = (sr1 & SPI_FLASH_SR1_TB) ? 1 : 0;
- bp = (sr1 & (SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0))
- >> 2;
-
- /* Bad pointers or invalid data */
- if (!start || !len || sr1 == -1 || sr2 == -1)
- return EC_ERROR_INVAL;
-
- /* Not defined by datasheet */
- if (sec && bp == 6)
- return EC_ERROR_INVAL;
-
- /* Determine granularity (4kb sector or 64kb block) */
- /* Computation using 2 * 1024 is correct */
- size = sec ? (2 * 1024) : (64 * 1024);
-
- /* Determine number of blocks */
- /* Equivalent to pow(2, bp) with pow(2, 0) = 0 */
- blocks = bp ? (1 << bp) : 0;
- /* Datasheet specifies don't care for BP == 4, BP == 5 */
- if (sec && bp == 5)
- blocks = (1 << 4);
-
- /* Determine number of bytes */
- *len = size * blocks;
-
- /* Determine bottom/top of memory to protect */
- *start = tb ? 0 :
- (CONFIG_SPI_FLASH_SIZE - *len) % CONFIG_SPI_FLASH_SIZE;
-
- /* Reverse computations if complement set */
- if (cmp) {
- *start = (*start + *len) % CONFIG_SPI_FLASH_SIZE;
- *len = CONFIG_SPI_FLASH_SIZE - *len;
- }
-
- return EC_SUCCESS;
-}
-
-/**
- * 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,
- uint8_t *sr1, uint8_t *sr2)
-{
- char cmp = 0;
- char sec = 0;
- char tb = 0;
- char bp = 0;
- int blocks;
- int size;
-
- /* Bad pointers */
- if (!sr1 || !sr2 || *sr1 == -1 || *sr2 == -1)
- return EC_ERROR_INVAL;
-
- /* Invalid data */
- if ((start && !len) || start + len > CONFIG_SPI_FLASH_SIZE)
- return EC_ERROR_INVAL;
-
- /* Set complement bit based on whether length is power of 2 */
- if ((len & (len - 1)) != 0) {
- cmp = 1;
- start = (start + len) % CONFIG_SPI_FLASH_SIZE;
- len = CONFIG_SPI_FLASH_SIZE - len;
- }
-
- /* Set bottom/top bit based on start address */
- /* Do not set if len == 0 or len == CONFIG_SPI_FLASH_SIZE */
- if (!start && (len % CONFIG_SPI_FLASH_SIZE))
- tb = 1;
-
- /* Set sector bit and determine block length based on protect length */
- if (len == 0 || len >= 128 * 1024) {
- sec = 0;
- size = 64 * 1024;
- } else if (len >= 4 * 1024 && len <= 32 * 1024) {
- sec = 1;
- size = 2 * 1024;
- } else
- return EC_ERROR_INVAL;
-
- /* Determine number of blocks */
- if (len % size != 0)
- return EC_ERROR_INVAL;
- blocks = len / size;
-
- /* Determine bp = log2(blocks) with log2(0) = 0 */
- bp = blocks ? (31 - __builtin_clz(blocks)) : 0;
-
- /* Clear bits */
- *sr1 &= ~(SPI_FLASH_SR1_SEC | SPI_FLASH_SR1_TB |
- SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0);
- *sr2 &= ~SPI_FLASH_SR2_CMP;
-
- /* Set bits */
- *sr1 |= (sec ? SPI_FLASH_SR1_SEC : 0) | (tb ? SPI_FLASH_SR1_TB : 0)
- | (bp << 2);
- *sr2 |= (cmp ? SPI_FLASH_SR2_CMP : 0);
-
- 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)
-{
- timestamp_t timeout;
-
- timeout.val = get_time().val + SPI_FLASH_TIMEOUT_USEC;
- /* Wait until chip is not busy */
- while (spi_flash_get_status1() & SPI_FLASH_SR1_BUSY) {
- usleep(SPI_FLASH_SLEEP_USEC);
-
- if (get_time().val > timeout.val)
- return EC_ERROR_TIMEOUT;
- }
-
- 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);
-}
-
-/**
- * Returns the contents of SPI flash status register 1
- * @return register contents or -1 on error
- */
-uint8_t spi_flash_get_status1(void)
-{
- /* Get SR 1 */
- buf[0] = SPI_FLASH_READ_SR1;
- if (communicate(1, 1))
- return -1;
-
- return buf[1];
-}
-
-/**
- * Returns the contents of SPI flash status register 2
- * @return register contents or -1 on error
- */
-uint8_t spi_flash_get_status2(void)
-{
- /* Get SR 2 */
- buf[0] = SPI_FLASH_READ_SR2;
- if (communicate(1, 1))
- return -1;
-
- return buf[1];
-}
-
-/**
- * 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)
-{
- int rv = EC_SUCCESS;
-
- /* Register has protection */
- rv = spi_flash_check_wp();
- if (rv)
- return rv;
-
- /* Enable writing to SPI flash */
- rv = spi_flash_write_enable();
- 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);
- else
- rv = communicate(3, 0);
- if (rv)
- return rv;
-
- return rv;
-}
-
-/**
- * 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;
-
- 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;
-}
-
-/**
- * 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)
-{
- int rv = EC_SUCCESS;
-
- /* Invalid block size */
- if (block != 4 && block != 32)
- return EC_ERROR_INVAL;
-
- /* Not block aligned */
- if ((offset % (block * 1024)) != 0)
- return EC_ERROR_INVAL;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- return rv;
-
- /* Enable writing to SPI flash */
- rv = spi_flash_write_enable();
- if (rv)
- 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;
-
- rv = communicate(4, 0);
- if (rv)
- return rv;
-
- return rv;
-}
-
-/**
- * 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)
-{
- int rv = EC_SUCCESS;
-
- /* Invalid input */
- if (offset + bytes > CONFIG_SPI_FLASH_SIZE)
- return EC_ERROR_INVAL;
-
- /* Not aligned to sector (4kb) */
- if (offset % 4096 || bytes % 4096)
- return EC_ERROR_INVAL;
-
- /* Largest unit is block (32kb) */
- if (offset % (32 * 1024) == 0) {
- while (bytes != (bytes % (32 * 1024))) {
- rv = spi_flash_erase_block(offset, 32);
- if (rv)
- return rv;
-
- bytes -= 32 * 1024;
- offset += 32 * 1024;
- }
- }
-
- /* Largest unit is sector (4kb) */
- while (bytes != (bytes % (4 * 1024))) {
- rv = spi_flash_erase_block(offset, 4);
- if (rv)
- return rv;
-
- bytes -= 4 * 1024;
- offset += 4 * 1024;
- }
-
- return rv;
-}
-
-/**
- * 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;
-
- /* Invalid input */
- if (!data || offset + bytes > CONFIG_SPI_FLASH_SIZE ||
- bytes > SPI_FLASH_MAX_WRITE_SIZE)
- return EC_ERROR_INVAL;
-
- /* Enable writing to SPI flash */
- rv = spi_flash_write_enable();
- if (rv)
- return rv;
-
- /* 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);
-}
-
-/**
- * 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;
-
- /* Compose instruction */
- buf[0] = SPI_FLASH_MFR_DEV_ID;
- buf[1] = 0;
- buf[2] = 0;
- buf[3] = 0;
-
- if (communicate(4, 2))
- return -1;
-
- res = (buf[4] << 8) | buf[5];
- return res;
-}
-
-/**
- * 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;
-
- /* Compose instruction */
- buf[0] = SPI_FLASH_JEDEC_ID;
-
- if (communicate(1, 4))
- return -1;
-
- memcpy((uint8_t *) &res, buf + 1, 4);
- return res;
-}
-
-/**
- * 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;
-
- /* Compose instruction */
- buf[0] = SPI_FLASH_UNIQUE_ID;
- buf[1] = 0;
- buf[2] = 0;
- buf[3] = 0;
- buf[4] = 0;
-
- if (communicate(5, 8))
- return -1;
-
- memcpy((uint8_t *) &res, buf + 5, 8);
- return res;
-}
-
-/**
- * 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)
-{
- int sr2 = spi_flash_get_status2();
-
- /* Power cycle or OTP protection */
- if (sr2 & SPI_FLASH_SR2_SRP1)
- return EC_ERROR_ACCESS_DENIED;
-
- return EC_SUCCESS;
-}
-
-/**
- * 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 sr1 = spi_flash_get_status1();
- int sr2 = spi_flash_get_status2();
-
- switch (w) {
- case SPI_WP_NONE:
- sr1 &= ~SPI_FLASH_SR1_SRP0;
- sr2 &= ~SPI_FLASH_SR2_SRP1;
- break;
- case SPI_WP_HARDWARE:
- sr1 |= SPI_FLASH_SR1_SRP0;
- sr2 &= ~SPI_FLASH_SR2_SRP1;
- break;
- case SPI_WP_POWER_CYCLE:
- sr1 &= ~SPI_FLASH_SR1_SRP0;
- sr2 |= SPI_FLASH_SR2_SRP1;
- break;
- case SPI_WP_PERMANENT:
- sr1 |= SPI_FLASH_SR1_SRP0;
- sr2 |= SPI_FLASH_SR2_SRP1;
- break;
- default:
- return EC_ERROR_INVAL;
- }
-
- return spi_flash_set_status(sr1, sr2);
-}
-
-/**
- * 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)
-{
- uint8_t sr1 = spi_flash_get_status1();
- uint8_t sr2 = spi_flash_get_status2();
- unsigned int start;
- unsigned int len;
- int rv = EC_SUCCESS;
-
- /* Invalid value */
- if (sr1 == -1 || sr2 == -1 || offset + bytes > CONFIG_SPI_FLASH_SIZE)
- return EC_ERROR_INVAL;
-
- /* Compute current protect range */
- rv = reg_to_protect(sr1, sr2, &start, &len);
- if (rv)
- return rv;
-
- /* Check if ranges overlap */
- if (MAX(start, offset) < MIN(start + len, offset + bytes))
- return EC_ERROR_ACCESS_DENIED;
-
- return EC_SUCCESS;
-}
-
-/**
- * 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)
-{
- int rv;
- uint8_t sr1 = spi_flash_get_status1();
- uint8_t sr2 = spi_flash_get_status2();
-
- /* Invalid values */
- if (sr1 == -1 || sr2 == -1 || offset + bytes > CONFIG_SPI_FLASH_SIZE)
- return EC_ERROR_INVAL;
-
- /* Compute desired protect range */
- rv = protect_to_reg(offset, bytes, &sr1, &sr2);
- if (rv)
- return rv;
-
- return spi_flash_set_status(sr1, sr2);
-}
-
-static int command_spi_flashinfo(int argc, char **argv)
-{
- uint32_t jedec;
- uint64_t unique;
- int rv = EC_SUCCESS;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- return rv;
-
- jedec = spi_flash_get_jedec_id();
- unique = spi_flash_get_unique_id();
-
- ccprintf("Manufacturer ID: %02x\nDevice ID: %02x %02x\n",
- ((uint8_t *)&jedec)[0], ((uint8_t *)&jedec)[1],
- ((uint8_t *)&jedec)[2]);
- ccprintf("Unique ID: %02x %02x %02x %02x %02x %02x %02x %02x\n",
- ((uint8_t *)&unique)[0], ((uint8_t *)&unique)[1],
- ((uint8_t *)&unique)[2], ((uint8_t *)&unique)[3],
- ((uint8_t *)&unique)[4], ((uint8_t *)&unique)[5],
- ((uint8_t *)&unique)[6], ((uint8_t *)&unique)[7]);
- ccprintf("Capacity: %4d MB\n",
- SPI_FLASH_SIZE(((uint8_t *)&jedec)[2]) / 1024);
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spi_flashinfo, command_spi_flashinfo,
- NULL,
- "Print SPI flash info",
- NULL);
-
-#ifdef CONFIG_CMD_SPI_FLASH
-static int command_spi_flasherase(int argc, char **argv)
-{
- int offset = -1;
- int bytes = 4096;
- int rv = parse_offset_size(argc, argv, 1, &offset, &bytes);
-
- if (rv)
- return rv;
-
- /* Chip has protection */
- if (spi_flash_check_protect(offset, bytes))
- return EC_ERROR_ACCESS_DENIED;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- return rv;
-
- ccprintf("Erasing %d bytes at 0x%x...\n", bytes, offset);
- rv = spi_flash_erase(offset, bytes);
- if (rv)
- return rv;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spi_flasherase, command_spi_flasherase,
- "offset [bytes]",
- "Erase flash",
- NULL);
-
-static int command_spi_flashwrite(int argc, char **argv)
-{
- char *data;
- int offset = -1;
- int bytes = SPI_FLASH_MAX_WRITE_SIZE;
- int write_len;
- int rv = EC_SUCCESS;
- int i;
-
- rv = parse_offset_size(argc, argv, 1, &offset, &bytes);
- if (rv)
- return rv;
-
- /* 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;
-
- ccprintf("Writing %d bytes to 0x%x...\n", bytes, offset);
- while (bytes > 0) {
- watchdog_reload();
-
- /* First write multiples of 256, then (bytes % 256) last */
- write_len = ((bytes % SPI_FLASH_MAX_WRITE_SIZE) == bytes) ?
- bytes : SPI_FLASH_MAX_WRITE_SIZE;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- goto err_free;
-
- /* Perform write */
- rv = spi_flash_write(offset, write_len, data);
- if (rv)
- goto err_free;
-
- offset += write_len;
- bytes -= write_len;
- }
-
- 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;
-}
-DECLARE_CONSOLE_COMMAND(spi_flashwrite, command_spi_flashwrite,
- "offset [bytes]",
- "Write pattern to flash",
- NULL);
-
-static int command_spi_flashread(int argc, char **argv)
-{
- int i;
- int offset = -1;
- int bytes = -1;
- int read_len;
- int rv = EC_SUCCESS;
-
- rv = parse_offset_size(argc, argv, 1, &offset, &bytes);
- if (rv)
- return rv;
-
- /* Can't read past size of memory */
- if (offset + bytes > CONFIG_SPI_FLASH_SIZE)
- return EC_ERROR_INVAL;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- return rv;
-
- ccprintf("Reading %d bytes from 0x%x...\n", bytes, offset);
- /* Read <= 256 bytes to avoid allocating another buffer */
- while (bytes > 0) {
- watchdog_reload();
-
- /* First read (bytes % 256), then in multiples of 256 */
- read_len = (bytes % SPI_FLASH_MAX_READ_SIZE) ?
- (bytes % SPI_FLASH_MAX_READ_SIZE) :
- SPI_FLASH_MAX_READ_SIZE;
-
- rv = spi_flash_read(buf, offset, read_len);
- if (rv)
- return rv;
-
- for (i = 0; i < read_len; i++) {
- if (i % 16 == 0)
- ccprintf("%02x:", offset + i);
-
- ccprintf(" %02x", buf[i]);
-
- if (i % 16 == 15 || i == read_len - 1)
- ccputs("\n");
- }
-
- offset += read_len;
- bytes -= read_len;
- }
-
- ASSERT(bytes == 0);
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(spi_flashread, command_spi_flashread,
- "offset bytes",
- "Read flash",
- NULL);
-
-static int command_spi_flashread_sr(int argc, char **argv)
-{
- uint8_t sr1 = spi_flash_get_status1();
- uint8_t sr2 = spi_flash_get_status2();
-
- ccprintf("Status Register 1: 0x%02x\nStatus Register 2: 0x%02x\n",
- sr1, sr2);
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(spi_flash_rsr, command_spi_flashread_sr,
- NULL,
- "Read status registers",
- NULL);
-
-static int command_spi_flashwrite_sr(int argc, char **argv)
-{
- int val1 = 0;
- int val2 = 0;
- int rv = parse_offset_size(argc, argv, 1, &val1, &val2);
-
- if (rv)
- return rv;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- return rv;
-
- ccprintf("Writing 0x%02x to status register 1, ", val1);
- ccprintf("0x%02x to status register 2...\n", val2);
- rv = spi_flash_set_status(val1, val2);
- if (rv)
- return rv;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spi_flash_wsr, command_spi_flashwrite_sr,
- "value1 value2",
- "Write to status registers",
- NULL);
-
-static int command_spi_flashprotect(int argc, char **argv)
-{
- int val1 = 0;
- int val2 = 0;
- int rv = parse_offset_size(argc, argv, 1, &val1, &val2);
-
- if (rv)
- return rv;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
- if (rv)
- return rv;
-
- ccprintf("Setting protection for 0x%06x to 0x%06x\n", val1, val1+val2);
- rv = spi_flash_set_protect(val1, val2);
- if (rv)
- return rv;
-
- /* Wait for previous operation to complete */
- rv = spi_flash_wait();
-
- return rv;
-}
-DECLARE_CONSOLE_COMMAND(spi_flash_prot, command_spi_flashprotect,
- "offset len",
- "Set block protection",
- NULL);
-#endif
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;
+}