/* * 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 spi { uint32_t _rsvd0[6]; uint32_t irq; /* 0110 - SPIIRQ - Interrupt Status Register */ uint32_t irqen; /* 0111 - SPIIRQEN - Interrupt Control Register */ uint32_t cr0; /* 1000 - CR0 - Control Register 0 */ uint32_t cr1; /* 1001 - CR1 - Control Register 1 */ uint32_t cr2; /* 1010 - CR2 - Control Register 2 */ uint32_t br; /* 1011 - BR - Baud Rate Register */ uint32_t sr; /* 1100 - SR - Status Register */ uint32_t txdr; /* 1101 - TXDR - Transmit Data Register */ uint32_t rxdr; /* 1110 - RXDR - Receive Data Register */ uint32_t csr; /* 1111 - CSR - Chip Select Register */ } __attribute__((packed,aligned(4))); #define SPI_CR0_TIDLE(xcnt) (((xcnt) & 3) << 6) #define SPI_CR0_TTRAIL(xcnt) (((xcnt) & 7) << 3) #define SPI_CR0_TLEAD(xcnt) (((xcnt) & 7) << 0) #define SPI_CR1_ENABLE (1 << 7) #define SPI_CR1_WKUPEN_USER (1 << 6) #define SPI_CR1_TXEDGE (1 << 4) #define SPI_CR2_MASTER (1 << 7) #define SPI_CR2_MCSH (1 << 6) #define SPI_CR2_SDBRE (1 << 5) #define SPI_CR2_CPOL (1 << 2) #define SPI_CR2_CPHA (1 << 1) #define SPI_CR2_LSBF (1 << 0) #define SPI_SR_TIP (1 << 7) #define SPI_SR_BUSY (1 << 6) #define SPI_SR_TRDY (1 << 4) #define SPI_SR_RRDY (1 << 3) #define SPI_SR_TOE (1 << 2) #define SPI_SR_ROE (1 << 1) #define SPI_SR_MDF (1 << 0) static volatile struct spi * const spi_regs[] = { (void*)(SPI_FLASH_BASE), #ifdef BOARD_E1_TRACER (void*)(SPI_LIU_BASE), #endif }; void spi_init(void) { /* Channel 0: Flash */ spi_regs[0]->cr0 = SPI_CR0_TIDLE(3) | SPI_CR0_TTRAIL(7) | SPI_CR0_TLEAD(7); spi_regs[0]->cr1 = SPI_CR1_ENABLE; spi_regs[0]->cr2 = SPI_CR2_MASTER | SPI_CR2_MCSH; spi_regs[0]->br = 3; spi_regs[0]->csr = 0xf; #ifdef BOARD_E1_TRACER /* Channel 1: LIU */ spi_regs[1]->cr0 = SPI_CR0_TIDLE(3) | SPI_CR0_TTRAIL(7) | SPI_CR0_TLEAD(7); spi_regs[1]->cr1 = SPI_CR1_ENABLE | SPI_CR1_TXEDGE; spi_regs[1]->cr2 = SPI_CR2_MASTER | SPI_CR2_LSBF | SPI_CR2_MCSH | SPI_CR2_CPHA; spi_regs[1]->br = 3; spi_regs[1]->csr = 0xf; #endif } void spi_xfer(unsigned cs, const struct spi_xfer_chunk *xfer, unsigned n) { uint8_t rxd; unsigned chan = (cs >> 2); cs &= 3; /* Setup CS */ spi_regs[chan]->csr = 0xf ^ (1 << cs); /* Run the chunks */ while (n--) { for (unsigned int i=0; ilen; i++) { spi_regs[chan]->txdr = xfer->write ? xfer->data[i] : 0x00; while (!(spi_regs[chan]->sr & SPI_SR_RRDY)); rxd = spi_regs[chan]->rxdr; if (xfer->read) xfer->data[i] = rxd; } xfer++; } /* Clear CS */ spi_regs[chan]->csr = 0xf; } #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_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(void) { uint8_t cmd = 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); }