diff --git a/devices/flash-gr712rc/Makefile b/devices/flash-gr712rc/Makefile new file mode 100644 index 00000000..8e04acc5 --- /dev/null +++ b/devices/flash-gr712rc/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for flash-gr712rc +# +# Copyright 2023 Phoenix Systems +# +# %LICENSE% +# + +OBJS += $(addprefix $(PREFIX_O)devices/flash-gr712rc/, flashdrv.o flash.o) diff --git a/devices/flash-gr712rc/cmds.h b/devices/flash-gr712rc/cmds.h new file mode 100644 index 00000000..d414aa4b --- /dev/null +++ b/devices/flash-gr712rc/cmds.h @@ -0,0 +1,43 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * Intel StrataFlash J3 commands + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef _PROM_H_ +#define _PROM_H_ + + +#define FLASH_RD_ARRAY 0xffu /* Read Array */ +#define FLASH_RD_ID 0x90u /* Read Identifier Codes */ +#define FLASH_RD_QUERY 0x98u /* Read Query */ +#define FLASH_RD_STATUS 0x70u /* Read Status Register */ +#define FLASH_CLR_STATUS 0x50u /* Clear Status Register */ + +#define FLASH_WR_BUF 0xe8u /* Write to Buffer */ +#define FLASH_WR_CONFIRM 0xd0u /* Write Confirm */ +#define FLASH_WR_BYTE 0x40u /* Byte Program */ +#define FLASH_BE_CYC1 0x20u /* Block Erase (1st bus cycle) */ +#define FLASH_BE_PROG_SUS 0xb0u /* Block Erase, Program Suspend */ +#define FLASH_BE_PROG_RES 0xd0u /* Block Erase, Program Resume */ + +#define FLASH_CFG_CYC1 0xb8u /* Configuration Register (1st bus cycle) */ +#define FLASH_CFG_CYC2 0xccu /* Configuration Register (2nd bus cycle) */ +#define FLASH_LOCK_CYC1 0x60u /* Set Block Lock-Bit (1st bus cycle) */ +#define FLASH_LOCK_CYC2 0x01u /* Set Block Lock-Bit (2nd bus cycle) */ +#define FLASH_UNLOCK_CYC1 0x60u /* Clear Block Lock-Bit (1st bus cycle) */ +#define FLASH_UNLOCK_CYC2 0xd0u /* Clear Block Lock-Bit (2nd bus cycle) */ +#define FLASH_PROT_CYC1 0xc0u /* Protection Program (1st bus cycle) */ + + +#endif diff --git a/devices/flash-gr712rc/flash.c b/devices/flash-gr712rc/flash.c new file mode 100644 index 00000000..3aff5b48 --- /dev/null +++ b/devices/flash-gr712rc/flash.c @@ -0,0 +1,234 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR712RC Flash on PROM interface driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include + +#include "cmds.h" +#include "flash.h" +#include "ftmctrl.h" + + +/* Valid address located on flash - for executing commands */ +#define FLASH_VALID_ADDR 0x1000 + +/* Flash status register */ +#define STS_WSM_READY (1 << 7) /* Write state machine ready */ +#define STS_ERASE_SUSP (1 << 6) /* Erase operation suspended */ +#define STS_ERASE_CLR_LOCK (1 << 5) /* Erase/clear lock-bit error */ +#define STS_PROG_SET_LOCK (1 << 4) /* Program/set lock-bit error */ +#define STS_PROG_VOLT_ERR (1 << 3) /* Low programming voltage detected */ +#define STS_PROG_SUSP (1 << 2) /* Program operation suspended */ +#define STS_DEV_PROTECT (1 << 1) /* Block lock-bit detected */ +#define STS_FULL_CHECK (STS_ERASE_CLR_LOCK | STS_PROG_SET_LOCK | STS_PROG_VOLT_ERR | STS_DEV_PROTECT) +#define XSR_WRBUF_RDY (1 << 7) /* Write buffer ready */ + + +static const struct { + const u8 id; + const char *name; +} flash_vendors[] = { + { .id = 0x89, .name = "Intel" } +}; + + +static u8 flash_readStatusCmd(void) +{ + *(vu8 *)FLASH_VALID_ADDR = FLASH_RD_STATUS; + + return hal_cpuLoadAlternate8((u8 *)FLASH_VALID_ADDR, ASI_CACHE_MISS); +} + + +static u8 flash_readStatusDirect(void) +{ + return hal_cpuLoadAlternate8((u8 *)FLASH_VALID_ADDR, ASI_CACHE_MISS); +} + + +static void flash_clearStatus(void) +{ + *(vu8 *)FLASH_VALID_ADDR = FLASH_CLR_STATUS; +} + + +static int flash_statusCheck(u8 status, const char *msg) +{ + int ret = ((status & STS_FULL_CHECK) == 0) ? EOK : -EIO; + + if (ret < 0) { + log_error("\ndev/flash: %s error: status 0x%x\n", msg, status); + } + + flash_clearStatus(); + + return ret; +} + + +static int flash_statusWait(u8 (*readStatus)(void), time_t timeout) +{ + time_t start = hal_timerGet(); + + while ((readStatus() & STS_WSM_READY) == 0) { + if ((timeout > 0u) && (hal_timerGet() - start) > timeout) { + return -ETIME; + } + } + + return EOK; +} + + +static int flash_clearLockBits(void) +{ + *(vu8 *)FLASH_VALID_ADDR = FLASH_UNLOCK_CYC1; + *(vu8 *)FLASH_VALID_ADDR = FLASH_UNLOCK_CYC2; + + (void)flash_statusWait(flash_readStatusDirect, 0u); + + return flash_statusCheck(flash_readStatusCmd(), "clear lock bits"); +} + + +static u16 flash_deserialize16(u16 value) +{ + return ((value & 0xff) << 8) | ((value >> 8) & 0xff); +} + + +int flash_writeBuffer(flash_device_t *dev, addr_t addr, const u8 *data, u8 len) +{ + size_t i; + u8 xsr; + + for (i = 0; i < len; ++i) { + if (data[i] != 0xff) { + break; + } + } + + if (i == len) { + return EOK; + } + + do { + *(vu8 *)addr = FLASH_WR_BUF; + xsr = hal_cpuLoadAlternate8((u8 *)FLASH_VALID_ADDR, ASI_CACHE_MISS); + } while ((xsr & XSR_WRBUF_RDY) == 0); + + *(vu8 *)addr = len - 1; + + hal_memcpy((void *)addr, data, len); + + *(vu8 *)addr = FLASH_WR_CONFIRM; + + if (flash_statusWait(flash_readStatusDirect, + CFI_TIMEOUT_MAX_PROGRAM(dev->cfi.toutTypical.bufWrite, dev->cfi.toutMax.bufWrite)) < 0) { + return -ETIME; + } + + return flash_statusCheck(flash_readStatusDirect(), "write buffer"); +} + + +int flash_blockErase(addr_t blkAddr, time_t timeout) +{ + *(vu8 *)blkAddr = FLASH_BE_CYC1; + *(vu8 *)blkAddr = FLASH_WR_CONFIRM; + + if (flash_statusWait(flash_readStatusDirect, 0) < 0) { + return -ETIME; + } + + return flash_statusCheck(flash_readStatusDirect(), "block erase"); +} + + +void flash_read(flash_device_t *dev, addr_t offs, void *buff, size_t len) +{ + ftmctrl_WrEn(); + *(vu8 *)FLASH_VALID_ADDR = FLASH_RD_ARRAY; + ftmctrl_WrDis(); + + hal_cpuFlushDCache(); + + hal_memcpy(buff, (void *)offs + dev->ahbStartAddr, len); +} + + +void flash_printInfo(int major, int minor, flash_cfi_t *cfi) +{ + size_t i; + const char *vendor; + for (i = 0; i < sizeof(flash_vendors) / sizeof(flash_vendors[0]); i++) { + if (flash_vendors[i].id == cfi->vendorData[0]) { + vendor = flash_vendors[i].name; + break; + } + } + + if (vendor == NULL) { + vendor = "Unknown"; + } + + lib_printf("\ndev/flash: configured %s %u MB flash(%d.%d)", vendor, CFI_SIZE(cfi->chipSz) / (1024 * 1024), major, minor); +} + + +static int flash_query(flash_cfi_t *cfi) +{ + size_t i; + u8 *ptr = (u8 *)cfi; + + *(vu8 *)FLASH_VALID_ADDR = FLASH_RD_QUERY; + for (i = 0; i < sizeof(flash_cfi_t); ++i) { + ptr[i] = hal_cpuLoadAlternate8((u8 *)(i * 2), ASI_CACHE_MISS); + } + + cfi->cmdSet1 = flash_deserialize16(cfi->cmdSet1); + cfi->addrExt1 = flash_deserialize16(cfi->addrExt1); + cfi->cmdSet2 = flash_deserialize16(cfi->cmdSet2); + cfi->addrExt2 = flash_deserialize16(cfi->addrExt2); + cfi->fdiDesc = flash_deserialize16(cfi->fdiDesc); + cfi->bufSz = flash_deserialize16(cfi->bufSz); + + for (i = 0; i < cfi->regionCnt; i++) { + cfi->regions[i].count = flash_deserialize16(cfi->regions[i].count); + cfi->regions[i].size = flash_deserialize16(cfi->regions[i].size); + } + return EOK; +} + + +int flash_init(flash_device_t *dev) +{ + int ret; + + ftmctrl_WrEn(); + + flash_clearLockBits(); + + /* Reset */ + *(vu8 *)FLASH_VALID_ADDR = FLASH_RD_ARRAY; + + dev->blockSz = FLASH_BLOCKSZ; + + ret = flash_query(&dev->cfi); + + ftmctrl_WrDis(); + + return ret; +} diff --git a/devices/flash-gr712rc/flash.h b/devices/flash-gr712rc/flash.h new file mode 100644 index 00000000..42bb2f48 --- /dev/null +++ b/devices/flash-gr712rc/flash.h @@ -0,0 +1,91 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR712RC Flash on PROM interface driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FLASH_H_ +#define _FLASH_H_ + + +#include + +/* Timeouts in ms */ +#define CFI_TIMEOUT_MAX_PROGRAM(typical, maximum) (((1u << typical) * (1u << maximum)) / 1000u) +#define CFI_TIMEOUT_MAX_ERASE(typical, maximum) ((1u << typical) * (1u << maximum)) + +#define CFI_SIZE(size) (1u << (u32)size) + +#define FLASH_BLOCKSZ 0x20000 + + +typedef struct { + u8 wordProgram; + u8 bufWrite; + u8 blkErase; + u8 chipErase; +} __attribute__((packed)) flash_cfi_timeout_t; + + +typedef struct { + u8 vendorData[0x10]; + u8 qry[3]; + u16 cmdSet1; + u16 addrExt1; + u16 cmdSet2; + u16 addrExt2; + struct { + u8 vccMin; + u8 vccMax; + u8 vppMin; + u8 vppMax; + } __attribute__((packed)) voltages; + flash_cfi_timeout_t toutTypical; + flash_cfi_timeout_t toutMax; + u8 chipSz; + u16 fdiDesc; + u16 bufSz; + u8 regionCnt; + struct { + u16 count; + u16 size; + } __attribute__((packed)) regions[4]; +} __attribute__((packed)) flash_cfi_t; + + +typedef struct { + flash_cfi_t cfi; + + addr_t ahbStartAddr; + addr_t blockBufAddr; + size_t blockSz; + u8 blockBufDirty; + u8 blockBuf[FLASH_BLOCKSZ]; +} flash_device_t; + + +int flash_writeBuffer(flash_device_t *dev, addr_t addr, const u8 *data, u8 len); + + +int flash_blockErase(addr_t blkAddr, time_t timeout); + + +void flash_read(flash_device_t *dev, addr_t offs, void *buff, size_t len); + + +void flash_printInfo(int major, int minor, flash_cfi_t *cfi); + + +int flash_init(flash_device_t *dev); + + +#endif diff --git a/devices/flash-gr712rc/flashdrv.c b/devices/flash-gr712rc/flashdrv.c new file mode 100644 index 00000000..d9d41353 --- /dev/null +++ b/devices/flash-gr712rc/flashdrv.c @@ -0,0 +1,325 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * GR712RC Flash on PROM interface driver + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include + +#include "flash.h" +#include "ftmctrl.h" + + +#define FLASH_NO 1u + + +static struct { + flash_device_t dev[FLASH_NO]; +} fdrv_common; + + +static addr_t fdrv_getBlockAddress(flash_device_t *dev, addr_t addr) +{ + return addr & ~(dev->blockSz - 1); +} + + +static int fdrv_isValidAddress(size_t memsz, addr_t offs, size_t len) +{ + if ((offs < memsz) && ((offs + len) <= memsz)) { + return 1; + } + + return 0; +} + + +static int fdrv_directBlockWrite(flash_device_t *dev, addr_t offs, const u8 *src) +{ + addr_t pos; + int res; + + ftmctrl_WrEn(); + + res = flash_blockErase(offs, CFI_TIMEOUT_MAX_ERASE(dev->cfi.toutTypical.blkErase, dev->cfi.toutMax.blkErase)); + if (res < 0) { + ftmctrl_WrDis(); + return res; + } + + for (pos = 0; pos < dev->blockSz; pos += CFI_SIZE(dev->cfi.bufSz)) { + res = flash_writeBuffer(dev, offs + pos, src + pos, CFI_SIZE(dev->cfi.bufSz)); + if (res < 0) { + ftmctrl_WrDis(); + return res; + } + } + + ftmctrl_WrDis(); + return pos; +} + + +/* Device driver interface */ + + +static ssize_t flashdrv_read(unsigned int minor, addr_t offs, void *buff, size_t len, time_t timeout) +{ + flash_device_t *dev; + + (void)timeout; + + if (minor >= FLASH_NO) { + return -ENXIO; + } + + dev = &fdrv_common.dev[minor]; + + if (fdrv_isValidAddress(CFI_SIZE(dev->cfi.chipSz), offs, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + flash_read(dev, offs, buff, len); + + return len; +} + + +static int flashdrv_sync(unsigned int minor) +{ + flash_device_t *dev; + + if (minor >= FLASH_NO) { + return -ENXIO; + } + + dev = &fdrv_common.dev[minor]; + + if ((dev->blockBufAddr == (addr_t)-1) || (dev->blockBufDirty == 0)) { + return EOK; + } + + return fdrv_directBlockWrite(dev, dev->blockBufAddr, dev->blockBuf); +} + + +static ssize_t flashdrv_write(unsigned int minor, addr_t offs, const void *buff, size_t len) +{ + ssize_t res; + addr_t blkOffs, currAddr; + const u8 *src = buff; + size_t doneBytes = 0, chunk; + flash_device_t *dev; + + if (minor >= FLASH_NO) { + return -ENXIO; + } + + dev = &fdrv_common.dev[minor]; + + if (fdrv_isValidAddress(CFI_SIZE(dev->cfi.chipSz), offs, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + while (doneBytes < len) { + currAddr = fdrv_getBlockAddress(dev, offs); + + blkOffs = offs - currAddr; + chunk = min(dev->blockSz - blkOffs, len - doneBytes); + + if (currAddr != dev->blockBufAddr) { + if ((blkOffs == 0) && (chunk == dev->blockSz)) { + /* Whole block to write */ + res = fdrv_directBlockWrite(dev, offs, src); + if (res < 0) { + return res; + } + } + else { + res = flashdrv_sync(minor); + if (res < 0) { + return res; + } + + dev->blockBufAddr = currAddr; + flash_read(dev, dev->blockBufAddr, dev->blockBuf, dev->blockSz); + } + } + + if (currAddr == dev->blockBufAddr) { + /* Sector to write to in cache */ + hal_memcpy(dev->blockBuf + blkOffs, src, chunk); + dev->blockBufDirty = 1; + } + + 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; + flash_device_t *dev; + size_t i; + + (void)flags; + + if (minor >= FLASH_NO) { + return -ENXIO; + } + + dev = &fdrv_common.dev[minor]; + + if (fdrv_isValidAddress(CFI_SIZE(dev->cfi.chipSz), addr, len) == 0) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + + if (len == (size_t)-1) { + /* Erase entire memory */ + dev->blockBufAddr = (addr_t)-1; + addr = 0; + end = CFI_SIZE(dev->cfi.chipSz); + lib_printf("\nErasing entire memory ..."); + } + else { + /* Erase sectors or blocks */ + addr = fdrv_getBlockAddress(dev, addr); + end = fdrv_getBlockAddress(dev, addr + len + dev->blockSz - 1u); + lib_printf("\nErasing blocks from 0x%x to 0x%x ...", addr, end); + } + + len = 0; + + ftmctrl_WrEn(); + + while (addr < end) { + if (addr == dev->blockBufAddr) { + dev->blockBufAddr = (addr_t)-1; + } + res = flash_blockErase(addr, CFI_TIMEOUT_MAX_ERASE(dev->cfi.toutTypical.blkErase, dev->cfi.toutMax.blkErase)); + if (res < 0) { + ftmctrl_WrDis(); + return res; + } + addr += dev->blockSz; + len += dev->blockSz; + } + + ftmctrl_WrDis(); + + 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; + flash_device_t *dev; + + if (minor >= FLASH_NO) { + return -ENXIO; + } + + dev = &fdrv_common.dev[minor]; + + fSz = CFI_SIZE(dev->cfi.chipSz); + fStart = dev->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) +{ + return flashdrv_sync(minor); +} + + +static int flashdrv_init(unsigned int minor) +{ + int res; + flash_device_t *dev; + + if (minor >= FLASH_NO) { + return -ENXIO; + } + + dev = &fdrv_common.dev[minor]; + + hal_memset(dev->blockBuf, 0xff, sizeof(dev->blockBuf)); + + res = flash_init(dev); + if (res != EOK) { + log_error("\ndev/flash: Failed to initialize flash%d", minor); + return res; + } + + flash_printInfo(DEV_STORAGE, minor, &dev->cfi); + + 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-gr712rc/ftmctrl.h b/devices/flash-gr712rc/ftmctrl.h new file mode 100644 index 00000000..6713b455 --- /dev/null +++ b/devices/flash-gr712rc/ftmctrl.h @@ -0,0 +1,48 @@ +/* + * Phoenix-RTOS + * + * Operating system loader + * + * FTMCTRL routines + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FTMCTRL_H_ +#define _FTMCTRL_H_ + +#include + + +#define FTMCTRL_BASE 0x80000000 + + +static inline void ftmctrl_WrEn(void) +{ + *(vu32 *)FTMCTRL_BASE |= (1 << 11); +} + + +static inline void ftmctrl_WrDis(void) +{ + *(vu32 *)FTMCTRL_BASE &= ~(1 << 11); +} + +static inline void ftmctrl_ioEn(void) +{ + *(vu32 *)FTMCTRL_BASE |= (1 << 19); +} + + +static inline void ftmctrl_ioDis(void) +{ + *(vu32 *)FTMCTRL_BASE &= ~(1 << 19); +} + + +#endif diff --git a/hal/sparcv8leon3/cpu.h b/hal/sparcv8leon3/cpu.h index e17f0cbf..d6baa76e 100644 --- a/hal/sparcv8leon3/cpu.h +++ b/hal/sparcv8leon3/cpu.h @@ -17,6 +17,10 @@ #define _CPU_H_ +/* Address space identifiers */ +#define ASI_CACHE_MISS 0x01 +#define ASI_CCTRL 0x02 + /* Processor State Register */ #define PSR_CWP 0x1f /* Current window pointer */ #define PSR_ET (1 << 5) /* Enable traps */ @@ -65,6 +69,16 @@ static inline void hal_cpuInstrBarrier(void) } +static inline u8 hal_cpuLoadAlternate8(const u8 *addr, int asi) +{ + u8 val; + /* clang-format off */ + __asm__ volatile("lduba [%1] %c2, %0" : "=r"(val) : "r"(addr), "i"(asi)); + /* clang-format on */ + return val; +} + + void hal_cpuFlushDCache(void); diff --git a/hal/sparcv8leon3/gaisler/gr712rc/Makefile b/hal/sparcv8leon3/gaisler/gr712rc/Makefile index f4c15c24..7ae1376c 100644 --- a/hal/sparcv8leon3/gaisler/gr712rc/Makefile +++ b/hal/sparcv8leon3/gaisler/gr712rc/Makefile @@ -8,9 +8,10 @@ CFLAGS += -DVADDR_KERNEL_INIT=$(VADDR_KERNEL_INIT) -PLO_COMMANDS := alias app call console copy dump echo go help kernel map mem phfs reboot script wait +PLO_COMMANDS := alias app call console copy dump echo go help kernel map mem phfs reboot script wait test-dev include devices/uart-grlib/Makefile include devices/ram-storage/Makefile +include devices/flash-gr712rc/Makefile OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/gaisler/$(TARGET_SUBFAMILY)/, _init.o hal.o gr712rc.o) diff --git a/hal/sparcv8leon3/gaisler/gr712rc/config.h b/hal/sparcv8leon3/gaisler/gr712rc/config.h index 5afeddb7..90ce8f19 100644 --- a/hal/sparcv8leon3/gaisler/gr712rc/config.h +++ b/hal/sparcv8leon3/gaisler/gr712rc/config.h @@ -35,8 +35,6 @@ #define NWINDOWS 8 -#define ASI_CCTRL 0x02 - /* Import platform specific definitions */ #include "ld/sparcv8leon3-gr712rc.ldt"