/* * spi.c * * Copyright (C) 2019-2020 Sylvain Munaut * SPDX-License-Identifier: LGPL-3.0-or-later */ #include #include #include "config.h" #include "spi.h" struct wb_qpi { uint32_t csr; uint32_t _rsvd0; uint32_t rf_nw; uint32_t rf_wt; uint32_t _rsvd1[12]; uint32_t cf[16]; } __attribute__((packed,aligned(4))); #define CSR_RF_EMPTY (1 << 15) #define CSR_RF_FULL (1 << 14) #define CSR_RF_OVFL (1 << 13) #define CSR_CF_EMPTY (1 << 11) #define CSR_CF_FUL (1 << 10) #define CSR_CS(n) ((n) << 4) #define CSR_GRANT (1 << 2) #define CSR_REL (1 << 2) #define CSR_REQ (1 << 1) #define CSR_IDLE (1 << 0) #define CMD_SPI (0 << 2) #define CMD_QPI_RD (1 << 2) #define CMD_QPI_WR (2 << 2) #define CMD_QPI_CMD (3 << 2) #define CMD_LEN(n) ((n)-1) static volatile struct wb_qpi * const qpi_regs = (void*)QPI_BASE; void spi_init(void) { /* Nothing to do */ } void spi_xfer(unsigned cs, const struct spi_xfer_chunk *xfer, unsigned n) { uint8_t rxd; /* Request external control & Setup CS */ qpi_regs->csr = CSR_CS(cs) | CSR_REQ; while (!(qpi_regs->csr & CSR_GRANT)); /* Run the chunks */ while (n--) { for (unsigned int i=0; ilen; i++) { qpi_regs->cf[CMD_SPI | CMD_LEN(1)] = xfer->write ? (xfer->data[i] << 24) : 0x00; rxd = qpi_regs->rf_wt; if (xfer->read) xfer->data[i] = rxd; } xfer++; } /* Release external control */ qpi_regs->csr = CSR_REL; while (qpi_regs->csr & CSR_GRANT); } #define FLASH_CMD_DEEP_POWER_DOWN 0xb9 #define FLASH_CMD_WAKE_UP 0xab #define FLASH_CMD_WRITE_ENABLE 0x06 #define FLASH_CMD_WRITE_ENABLE_VOLATILE 0x50 #define FLASH_CMD_WRITE_DISABLE 0x04 #define FLASH_CMD_READ_MANUF_ID 0x9f #define FLASH_CMD_READ_UNIQUE_ID 0x4b #define FLASH_CMD_READ_SR1 0x05 #define FLASH_CMD_READ_SR2 0x35 #define FLASH_CMD_WRITE_SR1 0x01 #define FLASH_CMD_READ_DATA 0x03 #define FLASH_CMD_PAGE_PROGRAM 0x02 #define FLASH_CMD_CHIP_ERASE 0x60 #define FLASH_CMD_SECTOR_ERASE 0x20 void flash_cmd(uint8_t cmd) { struct spi_xfer_chunk xfer[1] = { { .data = (void*)&cmd, .len = 1, .read = false, .write = true, }, }; spi_xfer(SPI_CS_FLASH, xfer, 1); } void flash_deep_power_down(void) { flash_cmd(FLASH_CMD_DEEP_POWER_DOWN); } void flash_wake_up(void) { flash_cmd(FLASH_CMD_WAKE_UP); } void flash_write_enable(void) { flash_cmd(FLASH_CMD_WRITE_ENABLE); } void flash_write_enable_volatile(void) { flash_cmd(FLASH_CMD_WRITE_ENABLE_VOLATILE); } void flash_write_disable(void) { flash_cmd(FLASH_CMD_WRITE_DISABLE); } void flash_manuf_id(void *manuf) { uint8_t cmd = FLASH_CMD_READ_MANUF_ID; struct spi_xfer_chunk xfer[2] = { { .data = (void*)&cmd, .len = 1, .read = false, .write = true, }, { .data = (void*)manuf, .len = 3, .read = true, .write = false, }, }; spi_xfer(SPI_CS_FLASH, xfer, 2); } void flash_unique_id(void *id) { uint8_t cmd = FLASH_CMD_READ_UNIQUE_ID; struct spi_xfer_chunk xfer[3] = { { .data = (void*)&cmd, .len = 1, .read = false, .write = true, }, { .data = (void*)0, .len = 4, .read = false, .write = false, }, { .data = (void*)id, .len = 8, .read = true, .write = false, }, }; spi_xfer(SPI_CS_FLASH, xfer, 3); } uint8_t flash_read_sr(int n) { uint8_t cmd = (n == 2) ? FLASH_CMD_READ_SR2 : FLASH_CMD_READ_SR1; uint8_t rv; struct spi_xfer_chunk xfer[2] = { { .data = (void*)&cmd, .len = 1, .read = false, .write = true, }, { .data = (void*)&rv, .len = 1, .read = true, .write = false, }, }; spi_xfer(SPI_CS_FLASH, xfer, 2); return rv; } void flash_write_sr(uint8_t sr) { uint8_t cmd[2] = { FLASH_CMD_WRITE_SR1, sr }; struct spi_xfer_chunk xfer[1] = { { .data = (void*)cmd, .len = 2, .read = false, .write = true, }, }; spi_xfer(SPI_CS_FLASH, xfer, 1); } void flash_read(void *dst, uint32_t addr, unsigned len) { uint8_t cmd[4] = { FLASH_CMD_READ_DATA, ((addr >> 16) & 0xff), ((addr >> 8) & 0xff), (addr & 0xff) }; struct spi_xfer_chunk xfer[2] = { { .data = (void*)cmd, .len = 4, .read = false, .write = true, }, { .data = (void*)dst, .len = len, .read = true, .write = false, }, }; spi_xfer(SPI_CS_FLASH, xfer, 2); } void flash_page_program(const void *src, uint32_t addr, unsigned len) { uint8_t cmd[4] = { FLASH_CMD_PAGE_PROGRAM, ((addr >> 16) & 0xff), ((addr >> 8) & 0xff), (addr & 0xff) }; struct spi_xfer_chunk xfer[2] = { { .data = (void*)cmd, .len = 4, .read = false, .write = true, }, { .data = (void*)src, .len = len, .read = false, .write = true, }, }; spi_xfer(SPI_CS_FLASH, xfer, 2); } void flash_sector_erase(uint32_t addr) { uint8_t cmd[4] = { FLASH_CMD_SECTOR_ERASE, ((addr >> 16) & 0xff), ((addr >> 8) & 0xff), (addr & 0xff) }; struct spi_xfer_chunk xfer[1] = { { .data = (void*)cmd, .len = 4, .read = false, .write = true, }, }; spi_xfer(SPI_CS_FLASH, xfer, 1); }