From 0c3918fd86d994af1800e5ea621ec4801831b48a 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 | 377 +++++++++++++++++++++++ devices/flash-gr716/nor/flash.h | 92 ++++++ devices/flash-gr716/nor/nor.c | 385 ++++++++++++++++++++++++ devices/flash-gr716/nor/nor.h | 73 +++++ devices/flash-gr716/spimctrl.h | 86 ++++++ devices/flash-gr716/spimctrl/spimctrl.c | 166 ++++++++++ hal/sparcv8leon3/gr716/Makefile | 1 + 8 files changed, 1189 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..6cb28650 --- /dev/null +++ b/devices/flash-gr716/flashdrv.c @@ -0,0 +1,377 @@ +/* + * 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 fdrv_minorToInstance(unsigned int minor) +{ + switch (minor) { + case 0: return spimctrl_instance0; + case 1: return spimctrl_instance1; + default: return -1; + } +} + + +static struct nor_device *fdrv_minorToDevice(unsigned int minor) +{ + if (minor >= FLASH_NO) { + return NULL; + } + + return &fdrv_common.dev[minor]; +} + + +static inline int fdrv_getSectorIdFromAddress(struct nor_device *dev, addr_t addr) +{ + return addr / dev->nor->sectorSz; +} + + +static inline addr_t fdrv_getSectorAddress(struct nor_device *dev, addr_t addr) +{ + return addr & ~(dev->nor->sectorSz - 1u); +} + + +static int fdrv_isValidAddress(size_t memsz, addr_t offs, size_t len) +{ + if ((offs < memsz) && ((offs + len) <= memsz)) { + return 1; + } + + return 0; +} + + +static ssize_t fdrv_directSectorWrite(struct nor_device *dev, addr_t offs, const u8 *src) +{ + addr_t pos; + int res = nor_eraseSector(&dev->spimctrl, offs, dev->nor->tSE); + if (res < 0) { + return res; + } + + for (pos = 0; pos < dev->nor->sectorSz; pos += dev->nor->pageSz) { + res = nor_pageProgram(&dev->spimctrl, offs + pos, src + pos, dev->nor->pageSz, dev->nor->tPP); + if (res < 0) { + return res; + } + } + + return pos; +} + + +/* Device driver interface */ + + +static ssize_t flashdrv_read(unsigned int minor, addr_t offs, void *buff, size_t len, time_t timeout) +{ + int res; + struct nor_device *dev = fdrv_minorToDevice(minor); + size_t doneBytes = 0; + + if (dev == NULL) { + return -ENXIO; + } + + if (fdrv_isValidAddress(dev->nor->totalSz, offs, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + if (fdrv_getSectorAddress(dev, offs) == dev->sectorBufAddr) { + doneBytes = min(dev->sectorBufAddr + dev->nor->sectorSz - offs, len); + + hal_memcpy(buff, dev->sectorBuf + offs - dev->sectorBufAddr, doneBytes); + + if (len == doneBytes) { + return doneBytes; + } + + offs += doneBytes; + buff += doneBytes; + len -= doneBytes; + } + + res = nor_readData(&dev->spimctrl, offs, buff, len); + + return (res < 0) ? res : (doneBytes + res); +} + + +static int flashdrv_sync(unsigned int minor) +{ + struct nor_device *dev = fdrv_minorToDevice(minor); + + if (dev == NULL) { + return -ENXIO; + } + + if ((dev->sectorBufAddr == (addr_t)-1) || (dev->sectorBufDirty == 0)) { + return EOK; + } + + return fdrv_directSectorWrite(dev, dev->sectorBufAddr, dev->sectorBuf); +} + + +static ssize_t flashdrv_write(unsigned int minor, addr_t offs, const void *buff, size_t len) +{ + ssize_t res; + addr_t sectorOfs, lastSectorAddr, currAddr; + const u8 *src = buff; + size_t doneBytes = 0, chunk; + struct nor_device *dev = fdrv_minorToDevice(minor); + + if (dev == NULL) { + return -ENXIO; + } + + if (fdrv_isValidAddress(dev->nor->totalSz, offs, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + lastSectorAddr = fdrv_getSectorAddress(dev, offs + len - 1u); + + while (doneBytes < len) { + currAddr = fdrv_getSectorAddress(dev, offs); + + if ((currAddr != dev->sectorBufAddr) && (currAddr == lastSectorAddr)) { + /* Last sector to write to, cache it */ + res = flashdrv_sync(minor); + if (res < 0) { + return res; + } + + dev->sectorBufAddr = currAddr; + res = nor_readData(&dev->spimctrl, dev->sectorBufAddr, dev->sectorBuf, dev->nor->sectorSz); + if (res < 0) { + return res; + } + } + + if (currAddr == dev->sectorBufAddr) { + /* Sector to write to in cache */ + sectorOfs = offs - dev->sectorBufAddr; + chunk = min(dev->nor->sectorSz - sectorOfs, len - doneBytes); + + hal_memcpy(dev->sectorBuf + sectorOfs, src, chunk); + dev->sectorBufDirty = 1; + } + else { + chunk = dev->nor->sectorSz; + res = fdrv_directSectorWrite(dev, offs, src); + if (res < 0) { + return res; + } + } + + src += chunk; + offs += chunk; + doneBytes += chunk; + } + + 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; + struct nor_device *dev = fdrv_minorToDevice(minor); + + (void)flags; + + if (dev == NULL) { + return -ENXIO; + } + + if (fdrv_isValidAddress(dev->nor->totalSz, addr, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + /* Chip Erase */ + + if (len == (size_t)-1) { + dev->sectorBufAddr = (addr_t)-1; + len = dev->nor->totalSz; + lib_printf("\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 = fdrv_getSectorAddress(dev, addr); + end = fdrv_getSectorAddress(dev, addr + len + dev->nor->sectorSz - 1u); + + lib_printf("\nErasing sectors from 0x%x to 0x%x ...", addr, end); + + len = 0; + while (addr < end) { + if (addr == dev->sectorBufAddr) { + dev->sectorBufAddr = (addr_t)-1; + } + 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 = fdrv_minorToDevice(minor); + + if (dev == NULL) { + 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 = fdrv_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 = fdrv_minorToDevice(minor); + int instance = fdrv_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); + + res = nor_deviceInit(dev); + if (res != EOK) { + log_error("\ndev/flash: Initialization failed"); + return res; + } + + res = nor_probe(&dev->spimctrl, &dev->nor, &vendor); + if (res != EOK) { + log_error("\ndev/flash: Initialization failed"); + return res; + } + + 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..0d8cbce5 --- /dev/null +++ b/devices/flash-gr716/nor/nor.c @@ -0,0 +1,385 @@ +/* + * 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); +} + + +static int nor_writeEAR(spimctrl_t *spimctrl, u8 value) +{ + int res; + 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, &spimctrl->ear); + if (res < EOK) { + return res; + } + + if (spimctrl->ear != value) { + return -EIO; + } + + return EOK; +} + + +static int nor_validateEar(spimctrl_t *spimctrl, u32 addr) +{ + int res = EOK; + const u8 desiredEar = (addr >> 24) & 0xffu; + + if (desiredEar != spimctrl->ear) { + res = nor_writeEAR(spimctrl, desiredEar); + } + return res; +} + + +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 }; + + res = nor_validateEar(spimctrl, addr); + 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); + + return res; +} + + +int nor_pageProgram(spimctrl_t *spimctrl, addr_t addr, const void *src, size_t len, time_t timeout) +{ + struct xferOp xfer; + const u8 cmd[4] = { FLASH_CMD_PP, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + + int res = nor_validateEar(spimctrl, addr); + 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); + + return res; +} + +static ssize_t nor_readCmd(spimctrl_t *spimctrl, addr_t addr, void *data, size_t size) +{ + struct xferOp xfer; + const u8 cmd[4] = { FLASH_CMD_READ, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + + int res = nor_validateEar(spimctrl, addr); + if (res < EOK) { + return res; + } + + 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 = nor_validateEar(spimctrl, addr); + if (res < EOK) { + return res; + } + + hal_memcpy(data, (void *)addr + spimctrl->ahbStartAddr, size); + + 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 (can be read without EAR change) + */ + 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; +} + + +int nor_deviceInit(struct nor_device *dev) +{ + int res = nor_readEAR(&dev->spimctrl, &dev->spimctrl.ear); + if (res < EOK) { + return res; + } + + dev->sectorBufAddr = (addr_t)-1; + dev->sectorBufDirty = 0; + hal_memset(dev->sectorBuf, NOR_ERASED_STATE, sizeof(dev->sectorBuf)); + + return EOK; +} diff --git a/devices/flash-gr716/nor/nor.h b/devices/flash-gr716/nor/nor.h new file mode 100644 index 00000000..c7a0145e --- /dev/null +++ b/devices/flash-gr716/nor/nor.h @@ -0,0 +1,73 @@ +/* + * 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; + + addr_t sectorBufAddr; + u8 sectorBufDirty; + 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; +}; + + +/* All addresses are relative to NOR base address */ + + +extern int nor_deviceInit(struct nor_device *dev); + + +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 addr, 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..1b20574e --- /dev/null +++ b/devices/flash-gr716/spimctrl.h @@ -0,0 +1,86 @@ +/* + * 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; + u8 ear; +} 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)