From e79e34765e683f3af8bf36e63ded80db03d2898c Mon Sep 17 00:00:00 2001 From: lukileczo Date: Sat, 18 Mar 2023 18:47:22 +0100 Subject: [PATCH] gr716: implement flashdrv JIRA: RTOS-478 --- devices/flash-gr716/Makefile | 9 + devices/flash-gr716/flashdrv.c | 395 ++++++++++++++++++++++++ devices/flash-gr716/nor/flash.h | 92 ++++++ devices/flash-gr716/nor/nor.c | 386 +++++++++++++++++++++++ devices/flash-gr716/nor/nor.h | 74 +++++ devices/flash-gr716/spimctrl.h | 85 +++++ devices/flash-gr716/spimctrl/spimctrl.c | 166 ++++++++++ hal/sparcv8leon3/gr716/Makefile | 1 + 8 files changed, 1208 insertions(+) create mode 100644 devices/flash-gr716/Makefile create mode 100644 devices/flash-gr716/flashdrv.c create mode 100644 devices/flash-gr716/nor/flash.h create mode 100644 devices/flash-gr716/nor/nor.c create mode 100644 devices/flash-gr716/nor/nor.h create mode 100644 devices/flash-gr716/spimctrl.h create mode 100644 devices/flash-gr716/spimctrl/spimctrl.c diff --git a/devices/flash-gr716/Makefile b/devices/flash-gr716/Makefile new file mode 100644 index 00000000..0902dddb --- /dev/null +++ b/devices/flash-gr716/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for flash-gr716 +# +# Copyright 2023 Phoenix Systems +# +# %LICENSE% +# + +OBJS += $(addprefix $(PREFIX_O)devices/flash-gr716/, flashdrv.o nor/nor.o spimctrl/spimctrl.o) diff --git a/devices/flash-gr716/flashdrv.c b/devices/flash-gr716/flashdrv.c new file mode 100644 index 00000000..8c9c48db --- /dev/null +++ b/devices/flash-gr716/flashdrv.c @@ -0,0 +1,395 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR716 Flash driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include + +#include "spimctrl.h" +#include "nor/nor.h" + + +/* GR716 has 2 SPI memory controllers + * On GR716-MINI only SPIMCTRL0 is connected to external flash + */ + + +#define FLASH_NO 1u + +static struct { + struct nor_device dev[SPIMCTRL_NUM]; +} fdrv_common; + + +/* Auxiliary helpers */ + + +static int minorToInstance(unsigned int minor) +{ + switch (minor) { + case 0: return spimctrl_instance0; + case 1: return spimctrl_instance1; + default: return -1; + } +} + + +static struct nor_device *minorToDevice(unsigned int minor) +{ + if (minor >= FLASH_NO) { + return NULL; + } + + return &fdrv_common.dev[minor]; +} + + +static inline int get_sectorIdFromAddress(struct nor_device *dev, addr_t addr) +{ + return addr / dev->nor->sectorSz; +} + + +static inline addr_t get_sectorAddress(struct nor_device *dev, addr_t addr) +{ + return addr & ~(dev->nor->sectorSz - 1u); +} + + +static int flashdrv_isValidAddress(size_t memsz, addr_t offs, size_t len) +{ + if ((offs < memsz) && ((offs + len) <= memsz)) { + return 1; + } + + return 0; +} + +/* Device driver interface */ + + +static ssize_t flashdrv_read(unsigned int minor, addr_t offs, void *buff, size_t len, time_t timeout) +{ + struct nor_device *dev = minorToDevice(minor); + + if ((dev == NULL) || (dev->active == 0)) { + return -ENXIO; + } + + if (flashdrv_isValidAddress(dev->nor->totalSz, offs, len) == 0) { + return -EINVAL; + } + + return nor_readData(&dev->spimctrl, offs, buff, len); +} + + +static int flashdrv_sync(unsigned int minor) +{ + int res; + struct nor_device *dev = minorToDevice(minor); + addr_t sectorOfs, pos, sectorAddr; + + if (dev == NULL) { + return -ENXIO; + } + + /* Initial sector value check, nothing to do */ + if (dev->sectorPrevAddr == (addr_t)-1) { + return EOK; + } + + sectorAddr = get_sectorAddress(dev, dev->sectorPrevAddr); + + /* All 'ones' in buffer means sector is erased ... */ + for (pos = 0; pos < dev->nor->sectorSz; pos++) { + if (dev->sectorBuf[pos] != NOR_ERASED_STATE) { + break; + } + } + + /* ... then erase may be skipped */ + if (pos != dev->nor->sectorSz) { + res = nor_eraseSector(&dev->spimctrl, sectorAddr, dev->nor->tSE); + if (res < 0) { + return res; + } + } + + for (sectorOfs = 0; sectorOfs < dev->nor->sectorSz; sectorOfs += dev->nor->pageSz) { + /* Just erased NOR page contains all 'ones' */ + for (pos = 0; pos < dev->nor->pageSz; ++pos) { + if (*(dev->sectorBuf + sectorOfs + pos) != NOR_ERASED_STATE) { + break; + } + } + + /* then if buffer is the same skip single page program */ + if (pos == dev->nor->pageSz) { + continue; + } + + res = nor_pageProgram(&dev->spimctrl, sectorAddr + sectorOfs, dev->sectorBuf + sectorOfs, dev->nor->pageSz, dev->nor->tPP); + + if (res < 0) { + return res; + } + } + + dev->sectorSyncAddr = dev->sectorPrevAddr; + + return dev->nor->sectorSz; +} + + +static ssize_t bufferSync(unsigned int minor, addr_t dstAddr, int isLast) +{ + ssize_t res = 0; + struct nor_device *dev = minorToDevice(minor); + const int sectorLast = get_sectorIdFromAddress(dev, dev->sectorPrevAddr); + const int sectorCurr = get_sectorIdFromAddress(dev, dstAddr); + + if (sectorCurr == sectorLast) { + return res; + } + + res = flashdrv_sync(minor); + if (res < 0) { + return res; + } + + if (dev->nor->totalSz <= dstAddr) { + dev->sectorPrevAddr = (addr_t)-1; + dev->sectorSyncAddr = (addr_t)-1; + } + + if (isLast != 0) { + return res; + } + + if (dev->nor->totalSz < (dstAddr + dev->nor->sectorSz)) { + return -EIO; + } + + res = nor_readData(&dev->spimctrl, dstAddr, dev->sectorBuf, dev->nor->sectorSz); + if (res < 0) { + return res; + } + + dev->sectorPrevAddr = dstAddr; + + return res; +} + + +static ssize_t flashdrv_write(unsigned int minor, addr_t offs, const void *buff, size_t len) +{ + ssize_t res; + addr_t sectorOfs; + const u8 *src = buff; + size_t doneBytes, chunkSz; + struct nor_device *dev = minorToDevice(minor); + + if ((dev == NULL) || (dev->active == 0)) { + return -ENXIO; + } + + if (flashdrv_isValidAddress(dev->nor->totalSz, offs, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + doneBytes = 0; + while (doneBytes < len) { + res = bufferSync(minor, offs, 0); + if (res < 0) { + return res; /* FIXME: should error or doneBytes to be returned ? */ + } + + sectorOfs = offs & (dev->nor->sectorSz - 1u); + chunkSz = min(len - doneBytes, dev->nor->sectorSz - sectorOfs); + + hal_memcpy(dev->sectorBuf + sectorOfs, src, chunkSz); + + offs += chunkSz; + src += chunkSz; + doneBytes += chunkSz; + } + + if (doneBytes > 0u) { + res = bufferSync(minor, offs, 1); + if (res < 0) { + return res; /* FIXME: should error or doneBytes to be returned ? */ + } + } + + return doneBytes; +} + + +static ssize_t flashdrv_erase(unsigned int minor, addr_t addr, size_t len, unsigned int flags) +{ + ssize_t res; + addr_t end, addr_mask; + struct nor_device *dev = minorToDevice(minor); + + (void)flags; + + if ((dev == NULL) || (dev->active == 0)) { + return -ENXIO; + } + + if (flashdrv_isValidAddress(dev->nor->totalSz, addr, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + /* Invalidate sync */ + dev->sectorPrevAddr = (addr_t)-1; + dev->sectorSyncAddr = (addr_t)-1; + + /* Chip Erase */ + if (len == (size_t)-1) { + len = dev->nor->totalSz; + log_info("\nErasing all data from flash device ..."); + res = nor_eraseChip(&dev->spimctrl, dev->nor->tCE); + + return (res < 0) ? res : (ssize_t)(len); + } + + /* Erase sectors or blocks */ + + addr_mask = dev->nor->sectorSz - 1u; + end = (addr + len + addr_mask) & ~addr_mask; + addr &= ~addr_mask; + + log_info("\nErasing sectors from 0x%x to 0x%x ...", addr, end); + + len = 0; + while (addr < end) { + res = nor_eraseSector(&dev->spimctrl, addr, dev->nor->tSE); + if (res < 0) { + return res; + } + addr += dev->nor->sectorSz; + len += dev->nor->sectorSz; + } + + return len; +} + + +static int flashdrv_map(unsigned int minor, addr_t addr, size_t sz, int mode, addr_t memaddr, size_t memsz, int memmode, addr_t *a) +{ + size_t fSz; + addr_t fStart; + struct nor_device *dev = minorToDevice(minor); + + if ((dev == NULL) || (dev->active == 0)) { + return -ENXIO; + } + + fSz = dev->nor->totalSz; + fStart = dev->spimctrl.ahbStartAddr; + *a = fStart; + + /* Check if region is located on flash */ + if ((addr + sz) >= fSz) { + return -EINVAL; + } + + /* Check if flash is mappable to map region */ + if ((fStart <= memaddr) && (fStart + fSz) >= (memaddr + memsz)) { + return dev_isMappable; + } + + /* Device mode cannot be higher than map mode to copy data */ + if ((mode & memmode) != mode) { + return -EINVAL; + } + + /* flash is not mappable to any region in I/O mode */ + return dev_isNotMappable; +} + + +static int flashdrv_done(unsigned int minor) +{ + struct nor_device *dev = minorToDevice(minor); + + if (dev == NULL) { + return -ENXIO; + } + + spimctrl_reset(&dev->spimctrl); + + return EOK; +} + + +static int flashdrv_init(unsigned int minor) +{ + int res; + const char *vendor; + struct nor_device *dev = minorToDevice(minor); + int instance = minorToInstance(minor); + + if (dev == NULL) { + return -ENXIO; + } + + res = spimctrl_init(&dev->spimctrl, instance); + if (res != EOK) { + log_error("\ndev/flash: Failed to initialize spimctrl%d", instance); + return res; + } + + log_info("\ndev/flash: Initialized spimctrl%d", instance); + + nor_deviceInit(dev); + + nor_probe(&dev->spimctrl, &dev->nor, &vendor); + + dev->active = 1; + + lib_printf("\ndev/flash/nor: Configured %s %s %dMB nor flash(%d.%d)", + vendor, dev->nor->name, dev->nor->totalSz >> 20, DEV_STORAGE, minor); + + return EOK; +} + + +__attribute__((constructor)) static void flashdrv_reg(void) +{ + static const dev_handler_t h = { + .init = flashdrv_init, + .done = flashdrv_done, + .read = flashdrv_read, + .write = flashdrv_write, + .erase = flashdrv_erase, + .sync = flashdrv_sync, + .map = flashdrv_map + }; + + hal_memset(&fdrv_common, 0, sizeof(fdrv_common)); + + devs_register(DEV_STORAGE, FLASH_NO, &h); +} diff --git a/devices/flash-gr716/nor/flash.h b/devices/flash-gr716/nor/flash.h new file mode 100644 index 00000000..883bd120 --- /dev/null +++ b/devices/flash-gr716/nor/flash.h @@ -0,0 +1,92 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Macronix MX25L flash commands + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FLASH_H_ +#define _FLASH_H_ + + +/* Status register */ + +#define FLASH_SR_WIP 0x01u /* Write in progress */ +#define FLASH_SR_WEL 0x02u /* Write enable latch */ + +/* ID */ + +#define FLASH_CMD_RDID 0x9Fu /* Read Identification */ +#define FLASH_CMD_RES 0xABu /* Read Electronic ID */ +#define FLASH_CMD_REMS 0x90u /* Read Electronic & Device ID */ + +/* Register */ + +#define FLASH_CMD_WRSR 0x01u /* Write Status Register */ +#define FLASH_CMD_RDSR 0x05u /* Read Status Register */ +#define FLASH_CMD_WRSCUR 0x2Fu /* Write Security Register */ +#define FLASH_CMD_RDSCUR 0x2Bu /* Read Security Register */ +#define FLASH_CMD_RDCR 0x15u /* Read Configuration Register */ +#define FLASH_CMD_RDEAR 0xC8u /* Read Extended Address Register */ +#define FLASH_CMD_WREAR 0xC5u /* Write Extended Address Register */ +#define FLASH_CMD_RDFBR 0x16u /* Read Fast Boot Register */ +#define FLASH_CMD_WRFBR 0x17u /* Write Fast Boot Register */ +#define FLASH_CMD_ESFBR 0x18u /* Erase Fast Boot Register */ +#define FLASH_CMD_WRLR 0x2Cu /* Write Lock Register */ +#define FLASH_CMD_RDLR 0x2Du /* Read Lock Register */ +#define FLASH_CMD_RDSPB 0xE2u /* Read SPB Status */ +#define FLASH_CMD_WRSPB 0xE3u /* Write SPB Bit */ +#define FLASH_CMD_ESSPB 0xE4u /* Erase All SPB Status */ +#define FLASH_CMD_SPBLK 0xA6u /* SPB lock Set */ +#define FLASH_CMD_RDSPBLK 0xA7u /* Read SPB lock Register */ +#define FLASH_CMD_WRPASS 0x28u /* Write Password Register */ +#define FLASH_CMD_RDPASS 0x27u /* Read Password Register */ +#define FLASH_CMD_PASSULK 0x29u /* Password Unlock */ +#define FLASH_CMD_RDDPB 0xE0u /* Read DPB Register */ +#define FLASH_CMD_WRDPB 0xE1u /* Write DPB Register */ + +/* Read */ + +#define FLASH_CMD_READ 0x03u /* Read */ +#define FLASH_CMD_FASTREAD 0x0Bu /* Fast Read */ +#define FLASH_CMD_RDSFDP 0x5Au /* Read SFDP */ + +/* Program */ + +#define FLASH_CMD_WREN 0x06u /* Write Enable */ +#define FLASH_CMD_WRDI 0x04u /* Write Disable */ +#define FLASH_CMD_PP 0x02u /* Page Program */ + +/* Erase */ + +#define FLASH_CMD_SE 0x20u /* Sector Erase */ +#define FLASH_CMD_BE32K 0x52u /* Block Erase 32kB */ +#define FLASH_CMD_BE 0xD8u /* Block Erase */ +#define FLASH_CMD_CE 0x60u /* Chip Erase */ + +/* Mode setting */ + +#define FLASH_CMD_DP 0xB9u /* Deep Power Down */ +#define FLASH_CMD_RDP 0xABu /* Release from Deep Power Down */ +#define FLASH_CMD_ENSO 0xB1u /* Enter Secured OTP */ +#define FLASH_CMD_EXSO 0xC1u /* Exit Secured OTP */ +#define FLASH_CMD_WPSEL 0x68u /* Enable block protect mode */ +#define FLASH_CMD_SBL 0xC0u /* SBL Set Burst Length */ + +/* Reset */ + +#define FLASH_CMD_RSTEN 0x66u /* Reset Enable */ +#define FLASH_CMD_RST 0x99u /* Reset Memory */ +#define FLASH_CMD_RSTQIO 0xF5u /* Reset Quad I/O */ +#define FLASH_CMD_NOP 0x00u /* No Operation */ + + +#endif /* _FLASH_H_ */ diff --git a/devices/flash-gr716/nor/nor.c b/devices/flash-gr716/nor/nor.c new file mode 100644 index 00000000..4a65fe2e --- /dev/null +++ b/devices/flash-gr716/nor/nor.c @@ -0,0 +1,386 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR716 flash driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include + +#include "flash.h" +#include "nor.h" + + +/* clang-format off */ +#define FLASH_ID(vid, pid) (((((vid) & 0xffu) << 16) | ((pid) & 0xff00u) | ((pid) & 0xffu)) << 8) + + +enum { write_disable = 0, write_enable }; + + +static const char *nor_vendors[] = { + "\xef" " Winbond", + "\x20" "Micron", + "\x9d" " ISSI", + "\xc2"" Macronix", + NULL +}; +/* clang-format on */ + + +static const struct nor_info flashInfo[] = { + /* Macronix (MXIX) */ + { FLASH_ID(0xc2u, 0x2019u), "MX25L25635F", 32 * 1024 * 1024, 0x100, 0x1000, 2, 120, 150 * 1000 }, +}; + + +static int nor_readId(spimctrl_t *spimctrl, u32 *id) +{ + struct xferOp xfer; + const u8 cmd[4] = { FLASH_CMD_RDID, FLASH_CMD_NOP, FLASH_CMD_NOP, 0x00u }; + + xfer.type = xfer_opRead; + xfer.cmd = cmd; + xfer.cmdLen = 4; + xfer.rxData = (u8 *)id; + xfer.dataLen = 3; + + return spimctrl_xfer(spimctrl, &xfer); +} + + +static int nor_readStatus(spimctrl_t *spimctrl, u8 *status) +{ + struct xferOp xfer; + const u8 cmd = FLASH_CMD_RDSR; + + xfer.type = xfer_opRead; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.rxData = status; + xfer.dataLen = 1; + + return spimctrl_xfer(spimctrl, &xfer); +} + + +static int nor_writeEnable(spimctrl_t *spimctrl, int enable) +{ + int res; + struct xferOp xfer; + u8 status = 0; + const u8 cmd = (enable == 1) ? FLASH_CMD_WREN : FLASH_CMD_WRDI; + + res = nor_waitBusy(spimctrl, 0); + if (res < 0) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.txData = NULL; + xfer.dataLen = 0; + + res = spimctrl_xfer(spimctrl, &xfer); + if (res < EOK) { + return res; + } + + res = nor_readStatus(spimctrl, &status); + if (res < EOK) { + return res; + } + + status = (status & FLASH_SR_WEL) ? 1 : 0; + + if (status != enable) { + return -EIO; + } + + return EOK; +} + + +static int nor_readEAR(spimctrl_t *spimctrl, u8 *status) +{ + struct xferOp xfer; + const u8 cmd = FLASH_CMD_RDEAR; + + xfer.type = xfer_opRead; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.rxData = status; + xfer.dataLen = 1; + + return spimctrl_xfer(spimctrl, &xfer); +} + + +int nor_writeEAR(spimctrl_t *spimctrl, u8 value) +{ + int res; + u8 status = 0; + struct xferOp xfer; + const u8 cmd = FLASH_CMD_WREAR; + + nor_writeEnable(spimctrl, write_enable); + + xfer.type = xfer_opWrite; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.txData = &value; + xfer.dataLen = 1; + + res = spimctrl_xfer(spimctrl, &xfer); + if (res < EOK) { + return res; + } + + res = nor_readEAR(spimctrl, &status); + if (res < EOK) { + return res; + } + + if (status != value) { + return -EIO; + } + + return EOK; +} + + +int nor_waitBusy(spimctrl_t *spimctrl, time_t timeout) +{ + int res; + u8 status = 0; + const time_t end = hal_timerGet() + timeout; + + do { + res = nor_readStatus(spimctrl, &status); + if (res < 0) { + return res; + } + + if ((timeout > 0) && (hal_timerGet() > end)) { + return -ETIME; + } + } while ((status & FLASH_SR_WIP) != 0); + + return EOK; +} + + +int nor_eraseChip(spimctrl_t *spimctrl, time_t timeout) +{ + int res; + struct xferOp xfer; + const u8 cmd = FLASH_CMD_CE; + + res = nor_writeEnable(spimctrl, write_enable); + if (res < EOK) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.txData = NULL; + xfer.dataLen = 0; + + res = spimctrl_xfer(spimctrl, &xfer); + if (res < EOK) { + return res; + } + + return nor_waitBusy(spimctrl, timeout); +} + + +int nor_eraseSector(spimctrl_t *spimctrl, addr_t addr, time_t timeout) +{ + int res; + struct xferOp xfer; + const u8 cmd[4] = { FLASH_CMD_SE, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + + /* Check if we extend 3-byte address range */ + if ((addr & 0xff000000) != 0) { + res = nor_writeEAR(spimctrl, 1); + if (res < EOK) { + return res; + } + } + + res = nor_writeEnable(spimctrl, write_enable); + if (res < EOK) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = cmd; + xfer.cmdLen = 4; + xfer.txData = NULL; + xfer.dataLen = 0; + + res = spimctrl_xfer(spimctrl, &xfer); + if (res < EOK) { + return res; + } + + res = nor_waitBusy(spimctrl, timeout); + + if ((addr & 0xff000000) != 0) { + res = nor_writeEAR(spimctrl, 0); + } + + return res; +} + + +int nor_pageProgram(spimctrl_t *spimctrl, addr_t dstAddr, const void *src, size_t len, time_t timeout) +{ + int res; + struct xferOp xfer; + const u8 cmd[4] = { FLASH_CMD_PP, (dstAddr >> 16) & 0xff, (dstAddr >> 8) & 0xff, dstAddr & 0xff }; + + /* Check if we extend 3-byte address range */ + if ((dstAddr & 0xff000000) != 0) { + res = nor_writeEAR(spimctrl, 1); + if (res < EOK) { + return res; + } + } + + res = nor_writeEnable(spimctrl, write_enable); + if (res < EOK) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = cmd; + xfer.cmdLen = 4; + xfer.txData = src; + xfer.dataLen = len; + + res = spimctrl_xfer(spimctrl, &xfer); + if (res < EOK) { + return res; + } + + res = nor_waitBusy(spimctrl, timeout); + + if ((dstAddr & 0xff000000) != 0) { + res = nor_writeEAR(spimctrl, 0); + } + + return res; +} + +static ssize_t nor_readCmd(spimctrl_t *spimctrl, addr_t addr, void *data, size_t size) +{ + int res; + struct xferOp xfer; + const u8 cmd[4] = { FLASH_CMD_READ, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + + xfer.type = xfer_opRead; + xfer.cmd = cmd; + xfer.cmdLen = 4; + xfer.rxData = data; + xfer.dataLen = size; + + res = spimctrl_xfer(spimctrl, &xfer); + + return res < EOK ? res : (ssize_t)size; +} + + +static ssize_t nor_readAhb(spimctrl_t *spimctrl, addr_t addr, void *data, size_t size) +{ + int res; + /* Check if we extend 3-byte address range */ + if ((addr & 0xff000000) != 0) { + res = nor_writeEAR(spimctrl, 1); + if (res < EOK) { + return res; + } + } + + hal_memcpy(data, (void *)addr + spimctrl->ahbStartAddr, size); + + if ((addr & 0xff000000) != 0) { + res = nor_writeEAR(spimctrl, 0); + } + + return res < EOK ? res : (ssize_t)size; +} + + +ssize_t nor_readData(spimctrl_t *spimctrl, addr_t addr, void *data, size_t size) +{ + if (((addr & 0xff000000) == 0) && (((addr + size) & 0xff000000) != 0)) { + /* If we'd have to change EAR register during read, + * read data through command (no EAR setting needed) + */ + return nor_readCmd(spimctrl, addr, data, size); + } + else { + /* Direct copy */ + return nor_readAhb(spimctrl, addr, data, size); + } +} + + +int nor_probe(spimctrl_t *spimctrl, const struct nor_info **nor, const char **pVendor) +{ + int res; + size_t i; + u32 jedecId = 0; + + res = nor_readId(spimctrl, &jedecId); + if (res < EOK) { + return res; + } + log_info("\ndev/flash/nor: Probing flash id 0x%08x", jedecId); + + res = -ENXIO; + for (i = 0; i < sizeof(flashInfo) / sizeof(flashInfo[0]); ++i) { + if (flashInfo[i].jedecId == jedecId) { + *nor = &flashInfo[i]; + res = EOK; + break; + } + } + + if (res != EOK) { + log_error("\ndev/flash/nor: Unsupported flash id 0x%08x", jedecId); + return res; + } + + *pVendor = "Unknown"; + + for (i = 0; nor_vendors[i]; ++i) { + if (*(u8 *)nor_vendors[i] == (jedecId >> 24)) { + *pVendor = &nor_vendors[i][2]; + break; + } + } + + return EOK; +} + + +void nor_deviceInit(struct nor_device *dev) +{ + dev->sectorPrevAddr = (addr_t)-1; + dev->sectorSyncAddr = (addr_t)-1; + hal_memset(dev->sectorBuf, NOR_ERASED_STATE, sizeof(dev->sectorBuf)); +} diff --git a/devices/flash-gr716/nor/nor.h b/devices/flash-gr716/nor/nor.h new file mode 100644 index 00000000..1d29cc05 --- /dev/null +++ b/devices/flash-gr716/nor/nor.h @@ -0,0 +1,74 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR716 flash driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _NOR_H_ +#define _NOR_H_ + +#include +#include "../spimctrl.h" + +#define NOR_ERASED_STATE 0xff +#define NOR_SECTORSZ_MAX 0x1000 +#define NOR_PAGESZ_MAX 0x100 + + +struct nor_device { + const struct nor_info *nor; + spimctrl_t spimctrl; + int active; + + addr_t sectorPrevAddr; + addr_t sectorSyncAddr; + u8 sectorBuf[NOR_SECTORSZ_MAX]; +}; + + +struct nor_info { + u32 jedecId; + const char *name; + size_t totalSz; + size_t pageSz; + size_t sectorSz; + time_t tPP; + time_t tSE; + time_t tCE; +}; + + +extern void nor_deviceInit(struct nor_device *dev); + + +extern int nor_writeEAR(spimctrl_t *spimctrl, u8 value); + + +extern int nor_waitBusy(spimctrl_t *spimctrl, time_t timeout); + + +extern int nor_eraseChip(spimctrl_t *spimctrl, time_t timeout); + + +extern int nor_eraseSector(spimctrl_t *spimctrl, addr_t addr, time_t timeout); + + +extern int nor_pageProgram(spimctrl_t *spimctrl, addr_t dstAddr, const void *src, size_t len, time_t timeout); + + +extern ssize_t nor_readData(spimctrl_t *spimctrl, addr_t addr, void *data, size_t size); + + +extern int nor_probe(spimctrl_t *spimctrl, const struct nor_info **nor, const char **pVendor); + + +#endif /* _NOR_H_ */ diff --git a/devices/flash-gr716/spimctrl.h b/devices/flash-gr716/spimctrl.h new file mode 100644 index 00000000..7543352c --- /dev/null +++ b/devices/flash-gr716/spimctrl.h @@ -0,0 +1,85 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR716 Flash driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _SPIMCTRL_H_ +#define _SPIMCTRL_H_ + + +#include + + +#define SPIMCTRL_NUM 2 + +#define FLASH0_AHB_ADDR 0x02000000 +#define FLASH1_AHB_ADDR 0x04000000 + +/* clang-format off */ +enum { spimctrl_instance0 = 0, spimctrl_instance1 }; +/* clang-format on */ + +typedef struct _spimctrl_t { + vu32 *base; + addr_t ahbStartAddr; + u8 instance; +} spimctrl_t; + + +struct xferOp { + /* clang-format off */ + enum { xfer_opRead = 0, xfer_opWrite } type; + /* clang-format on */ + const u8 *cmd; + size_t cmdLen; + union { + const u8 *txData; + u8 *rxData; + }; + size_t dataLen; +}; + + +static inline addr_t spimctrl_ahbAddr(int instance) +{ + switch (instance) { + case spimctrl_instance0: return FLASH0_AHB_ADDR; + case spimctrl_instance1: return FLASH1_AHB_ADDR; + default: return 0; + } +} + + +static inline void *spimctrl_getBase(int instance) +{ + switch (instance) { + case spimctrl_instance0: return SPIMCTRL0_BASE; + case spimctrl_instance1: return SPIMCTRL1_BASE; + default: return NULL; + } +} + + +/* Execute a transfer through spimctrl */ +extern int spimctrl_xfer(spimctrl_t *spimctrl, struct xferOp *op); + + +/* Reset spimctrl core */ +extern void spimctrl_reset(spimctrl_t *spimctrl); + + +/* Initialize spimctrl instance */ +extern int spimctrl_init(spimctrl_t *spimctrl, int instance); + + +#endif diff --git a/devices/flash-gr716/spimctrl/spimctrl.c b/devices/flash-gr716/spimctrl/spimctrl.c new file mode 100644 index 00000000..7770f918 --- /dev/null +++ b/devices/flash-gr716/spimctrl/spimctrl.c @@ -0,0 +1,166 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR716 Flash driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + +#include "../spimctrl.h" +#include "../nor/flash.h" + +/* Control register */ + +#define USR_CTRL (1 << 0) +#define CHIP_SEL (1 << 3) +#define CORE_RST (1 << 4) + +/* Status register */ + +#define OPER_DONE (1 << 0) +#define CORE_BUSY (1 << 1) +#define INITIALIZED (1 << 2) + + +enum { + flash_cfg, /* Flash configuration : 0x00 */ + flash_ctrl, /* Flash control : 0x04 */ + flash_stat, /* Flash status : 0x08 */ + flash_rx, /* Flash receive : 0x0C */ + flash_tx, /* Flash transmit : 0x10 */ + flash_econf, /* EDAC configuration : 0x14 */ + flash_estat /* EDAC status : 0x18 */ +}; + + +static void spimctrl_userCtrl(vu32 *spimctrlBase) +{ + *(spimctrlBase + flash_ctrl) = USR_CTRL; + *(spimctrlBase + flash_ctrl) &= ~CHIP_SEL; +} + + +static int spimctrl_busy(spimctrl_t *spimctrl) +{ + return (*(spimctrl->base + flash_stat) & CORE_BUSY) >> 1; +} + + +static int spimctrl_ready(spimctrl_t *spimctrl) +{ + u32 val = (*(spimctrl->base + flash_stat) & (INITIALIZED | OPER_DONE)); + + return (val == INITIALIZED) ? 1 : 0; +} + + +static void spimctrl_tx(vu32 *spimctrlBase, u8 cmd) +{ + *(spimctrlBase + flash_tx) = cmd; + while ((*(spimctrlBase + flash_stat) & OPER_DONE) == 0) { } + *(spimctrlBase + flash_stat) |= OPER_DONE; +} + + +static u8 spimctrl_rx(vu32 *spimctrlBase) +{ + return *(spimctrlBase + flash_rx) & 0xff; +} + + +static void spimctrl_read(spimctrl_t *spimctrl, struct xferOp *op) +{ + size_t i; + + spimctrl_userCtrl(spimctrl->base); + + /* send command */ + for (i = 0; i < op->cmdLen; i++) { + spimctrl_tx(spimctrl->base, op->cmd[i]); + } + + /* read data */ + for (i = 0; i < op->dataLen; i++) { + spimctrl_tx(spimctrl->base, FLASH_CMD_NOP); + op->rxData[i] = spimctrl_rx(spimctrl->base); + } + + *(spimctrl->base + flash_ctrl) &= ~USR_CTRL; +} + + +static void spimctrl_write(spimctrl_t *spimctrl, struct xferOp *op) +{ + size_t i; + + spimctrl_userCtrl(spimctrl->base); + + /* Send command */ + for (i = 0; i < op->cmdLen; i++) { + spimctrl_tx(spimctrl->base, op->cmd[i]); + } + + /* Send data */ + for (i = 0; i < op->dataLen; i++) { + spimctrl_tx(spimctrl->base, op->txData[i]); + } + + *(spimctrl->base + flash_ctrl) &= ~USR_CTRL; +} + + +int spimctrl_xfer(spimctrl_t *spimctrl, struct xferOp *op) +{ + if ((spimctrl_busy(spimctrl) == 1) || spimctrl_ready(spimctrl) == 0) { + return -EBUSY; + } + + switch (op->type) { + case xfer_opRead: + spimctrl_read(spimctrl, op); + break; + case xfer_opWrite: + spimctrl_write(spimctrl, op); + break; + default: + return -EINVAL; + } + return EOK; +} + + +void spimctrl_reset(spimctrl_t *spimctrl) +{ + *(spimctrl->base + flash_ctrl) = CORE_RST; +} + + +int spimctrl_init(spimctrl_t *spimctrl, int instance) +{ + void *base = spimctrl_getBase(instance); + if (base == NULL) { + return -EINVAL; + } + spimctrl->base = base; + spimctrl->ahbStartAddr = spimctrl_ahbAddr(instance); + spimctrl->instance = instance; + + /* Enable clock if needed */ + if (_gr716_cguClkStatus(cgu_primary, cgudev_spimctrl0 + instance) == 0) { + _gr716_cguClkEnable(cgu_primary, cgudev_spimctrl0 + instance); + } + + /* Reset core */ + spimctrl_reset(spimctrl); + + return EOK; +} diff --git a/hal/sparcv8leon3/gr716/Makefile b/hal/sparcv8leon3/gr716/Makefile index 05c51be3..efcf928c 100644 --- a/hal/sparcv8leon3/gr716/Makefile +++ b/hal/sparcv8leon3/gr716/Makefile @@ -21,5 +21,6 @@ PLO_COMMANDS = alias app call console copy dump echo go help kernel map phfs reb include devices/gpio-gr716/Makefile include devices/uart-gr716/Makefile +include devices/flash-gr716/Makefile OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/$(TARGET_SUBFAMILY)/, hal.o gr716.o timer.o console.o)