diff --git a/_targets/Makefile.riscv64-noelv b/_targets/Makefile.riscv64-noelv index 49653f5d6..f50d8062a 100644 --- a/_targets/Makefile.riscv64-noelv +++ b/_targets/Makefile.riscv64-noelv @@ -6,4 +6,4 @@ # Copyright 2024 Phoenix Systems # -DEFAULT_COMPONENTS := grlib-uart +DEFAULT_COMPONENTS := grlib-uart flashdrv diff --git a/_targets/Makefile.sparcv8leon-gr712rc b/_targets/Makefile.sparcv8leon-gr712rc index 0724bd6ef..af66a365b 100644 --- a/_targets/Makefile.sparcv8leon-gr712rc +++ b/_targets/Makefile.sparcv8leon-gr712rc @@ -6,4 +6,4 @@ # Copyright 2023 Phoenix Systems # -DEFAULT_COMPONENTS := grlib-multi gr712rc-flash +DEFAULT_COMPONENTS := grlib-multi flashdrv diff --git a/_targets/Makefile.sparcv8leon-gr740 b/_targets/Makefile.sparcv8leon-gr740 new file mode 100644 index 000000000..60884d8bf --- /dev/null +++ b/_targets/Makefile.sparcv8leon-gr740 @@ -0,0 +1,9 @@ +# +# Makefile for Phoenix-RTOS 3 device drivers +# +# sparcv8leon-gr740 drivers +# +# Copyright 2025 Phoenix Systems +# + +DEFAULT_COMPONENTS := grlib-multi flashdrv diff --git a/multi/grlib-multi/spacewire.c b/multi/grlib-multi/spacewire.c index bdcbf4c99..014eb88ea 100644 --- a/multi/grlib-multi/spacewire.c +++ b/multi/grlib-multi/spacewire.c @@ -26,14 +26,6 @@ #include #include -#if defined(__CPU_GR716) -#include -#elif defined(__CPU_GR712RC) -#include -#else -#error Unsupported target -#endif - #include #include "spacewire.h" @@ -653,6 +645,7 @@ static int spw_cguInit(int dev) .cgudev = cgudev_spw0 + dev, } }; + return platformctl(&pctl); #elif defined(__CPU_GR716) (void)dev; platformctl_t pctl = { @@ -664,8 +657,11 @@ static int spw_cguInit(int dev) .cgudev = cgudev_grspw, } }; -#endif return platformctl(&pctl); +#else + (void)dev; + return 0; +#endif } diff --git a/storage/flashdrv/Makefile b/storage/flashdrv/Makefile new file mode 100644 index 000000000..b8025969d --- /dev/null +++ b/storage/flashdrv/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for Phoenix-RTOS ftmctrl-flash driver +# +# Copyright 2023-2025 Phoenix Systems +# + +NAME := flashdrv +LOCAL_SRCS := common.c flashsrv.c +DEP_LIBS := libftmctrl libspimctrl +LIBS := libjffs2 libstorage libmtd libptable libcache + +include $(binary.mk) diff --git a/storage/flashdrv/common.c b/storage/flashdrv/common.c new file mode 100644 index 000000000..d8015a03a --- /dev/null +++ b/storage/flashdrv/common.c @@ -0,0 +1,28 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Common auxiliary functions + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include + + +off_t common_getSectorOffset(size_t sectorsz, off_t offs) +{ + return offs & ~(sectorsz - 1); +} + + +bool common_isValidAddress(size_t memsz, off_t offs, size_t len) +{ + return ((offs < memsz) && ((offs + len) <= memsz)); +} diff --git a/storage/flashdrv/drv/grlib-ftmctrl/Makefile b/storage/flashdrv/drv/grlib-ftmctrl/Makefile new file mode 100644 index 000000000..40ffc9808 --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for Phoenix-RTOS libftmctrl driver +# +# Copyright 2025 Phoenix Systems +# + +NAME := libftmctrl +LOCAL_SRCS := amd-flash.c flashdrv.c flash.c intel-flash.c + +include $(static-lib.mk) diff --git a/storage/flashdrv/drv/grlib-ftmctrl/amd-flash.c b/storage/flashdrv/drv/grlib-ftmctrl/amd-flash.c new file mode 100644 index 000000000..4b3496e33 --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/amd-flash.c @@ -0,0 +1,148 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * AMD command set flash interface + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "cmds.h" +#include "flash.h" + +#include + + +/* Valid offset on flash - for executing commands */ +#define FLASH_VALID_OFFS 0x0 +#define STS_FULL_CHECK ((1 << 5) | (1 << 4) | (1 << 3) | (1 << 1)) + +/* TODO: adapt also to x16 interface */ + +static void amd_unlockSequence(volatile uint8_t *base) +{ + *(base + 0x0aaa) = 0xaa; + *(base + 0x0555) = 0x55; +} + + +static void amd_issueReset(volatile uint8_t *base) +{ + *(base + FLASH_VALID_OFFS) = AMD_CMD_RESET; +} + + +static uint8_t amd_statusRead(volatile uint8_t *base) +{ + *(base + 0x0aaa) = CMD_RD_STATUS; + + return *(base + FLASH_VALID_OFFS); +} + + +static void amd_statusClear(volatile uint8_t *base) +{ + *(base + 0x0aaa) = AMD_CMD_CLR_STATUS; +} + + +static int amd_statusCheck(volatile uint8_t *base, const char *msg) +{ + int ret = 0; + uint8_t status = amd_statusRead(base); + + if ((status & STS_FULL_CHECK) != 0) { + LOG_ERROR("%s error: status 0x%x", msg, status); + ret = -EIO; + } + + return ret; +} + + +static void amd_issueWriteBuffer(volatile uint8_t *base, off_t sectorOffs, off_t programOffs, size_t len) +{ + (void)programOffs; + + amd_unlockSequence(base); + + *(base + sectorOffs) = AMD_CMD_WR_BUF; + *(base + sectorOffs) = (len - 1) & 0xff; +} + + +static void amd_issueWriteConfirm(volatile uint8_t *base, off_t sectorOffs) +{ + *(base + sectorOffs) = AMD_CMD_WR_CONFIRM; +} + + +static void amd_issueSectorErase(volatile uint8_t *base, off_t sectorOffs) +{ + amd_unlockSequence(base); + *(base + 0x0aaa) = AMD_CMD_BE_CYC1; + + amd_unlockSequence(base); + *(base + sectorOffs) = AMD_CMD_BE_CYC2; +} + + +static void amd_issueChipErase(volatile uint8_t *base) +{ + amd_unlockSequence(base); + *(base + 0x0aaa) = AMD_CMD_CE_CYC1; + + amd_unlockSequence(base); + *(base + 0x0aaa) = AMD_CMD_CE_CYC2; +} + + +static void amd_enterQuery(volatile uint8_t *base, off_t sectorOffs) +{ + *(base + sectorOffs + 0xaa) = CMD_RD_QUERY; +} + + +static void amd_exitQuery(volatile uint8_t *base) +{ + *base = AMD_CMD_EXIT_QUERY; +} + + +void ftmctrl_amd_register(void) +{ + static const flash_ops_t ops = { + .statusRead = amd_statusRead, + .statusCheck = amd_statusCheck, + .statusClear = amd_statusClear, + .issueReset = amd_issueReset, + .issueWriteBuffer = amd_issueWriteBuffer, + .issueWriteConfirm = amd_issueWriteConfirm, + .issueSectorErase = amd_issueSectorErase, + .issueChipErase = amd_issueChipErase, + .enterQuery = amd_enterQuery, + .exitQuery = amd_exitQuery + }; + + static const flash_dev_t amd_devices[] = { + { + .name = "Infineon S29GL01/512T", + .vendor = 0x01, + .device = 0x227e, + .chipWidth = 16, + .statusRdyMask = (1 << 7), + .usePolling = 1, + .ops = &ops, + }, + }; + + for (size_t i = 0; i < (sizeof(amd_devices) / sizeof(amd_devices[0])); ++i) { + ftmctrl_flash_register(&amd_devices[i]); + } +} diff --git a/storage/flashdrv/drv/grlib-ftmctrl/cmds.h b/storage/flashdrv/drv/grlib-ftmctrl/cmds.h new file mode 100644 index 000000000..a304140e5 --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/cmds.h @@ -0,0 +1,43 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Flash commands + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FTMCTRL_CMDS_H_ +#define _FTMCTRL_CMDS_H_ + + +/* Common flash commands */ +#define CMD_RD_QUERY 0x98u /* Read/Enter Query */ +#define CMD_RD_STATUS 0x70u /* Read Status Register */ + +/* Intel command set */ +#define INTEL_CMD_RESET 0xffu /* Reset/Read Array */ +#define INTEL_CMD_WR_BUF 0xe8u /* Write to Buffer */ +#define INTEL_CMD_WR_CONFIRM 0xd0u /* Write Confirm */ +#define INTEL_CMD_CLR_STATUS 0x50u /* Clear Status Register */ +#define INTEL_CMD_BE_CYC1 0x20u /* Block Erase (1st bus cycle) */ + +/* AMD command set */ +#define AMD_CMD_RESET 0xf0u /* Reset/ASO Exit */ +#define AMD_CMD_WR_BUF 0x25u /* Write to Buffer */ +#define AMD_CMD_WR_CONFIRM 0x29u /* Write Confirm */ +#define AMD_CMD_CLR_STATUS 0x71u /* Clear Status Register */ +#define AMD_CMD_CE_CYC1 0x80u /* Chip Erase (1st bus cycle) */ +#define AMD_CMD_CE_CYC2 0x10u /* Chip Erase (2nd bus cycle) */ +#define AMD_CMD_BE_CYC1 0x80u /* Block Erase (1st bus cycle) */ +#define AMD_CMD_BE_CYC2 0x30u /* Block Erase (2nd bus cycle) */ +#define AMD_CMD_EXIT_QUERY 0xf0u /* Exit Query */ + + +#endif diff --git a/storage/flashdrv/drv/grlib-ftmctrl/flash.c b/storage/flashdrv/drv/grlib-ftmctrl/flash.c new file mode 100644 index 000000000..12191861a --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/flash.c @@ -0,0 +1,385 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Copyright 2023-2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cmds.h" +#include "flash.h" +#include "flashdrv.h" +#include "ftmctrl.h" + +#define FLASH_DEVICES 2 + + +typedef union { + uint8_t b; + uint16_t w; +} flash_word_t; + + +static struct { + volatile uint8_t *base; + size_t nmodels; + const flash_dev_t *devs[FLASH_DEVICES]; +} common; + + +/* Timeout in us */ +static int flash_statusPoll(const struct _storage_devCtx_t *ctx, const flash_word_t src, volatile uint8_t *dst, time_t timeout) +{ + bool ready; + time_t start; + (void)gettime(&start, NULL); + + for (;;) { + switch (ftmctrl_portWidth(ctx->ftmctrl)) { + case 8: + ready = (src.b == *dst); + break; + + case 16: { + uint16_t val = dst[0] | (dst[1] << 8); + ready = (src.w == val); + break; + } + + default: + return -EINVAL; + } + + if (ready) { + break; + } + + if (timeout > 0u) { + time_t now; + (void)gettime(&now, NULL); + if ((now - start) > timeout) { + return -ETIME; + } + } + + usleep(100); + } + + return 0; +} + + +static int flash_statusWait(const struct _storage_devCtx_t *ctx, time_t timeout) +{ + time_t start; + (void)gettime(&start, NULL); + + while ((ctx->dev->ops->statusRead(common.base) & ctx->dev->statusRdyMask) == 0) { + if (timeout > 0u) { + time_t now; + (void)gettime(&now, NULL); + if ((now - start) > timeout) { + return -ETIME; + } + } + usleep(100); + } + + return 0; +} + + +static uint16_t flash_deserialize16(uint16_t value) +{ + return ((value & 0xff) << 8) | ((value >> 8) & 0xff); +} + + +int ftmctrl_flash_writeBuffer(const struct _storage_devCtx_t *ctx, off_t offs, const uint8_t *data, size_t len, time_t timeout) +{ + uint16_t val; + const int portWidth = ftmctrl_portWidth(ctx->ftmctrl); + + size_t i; + for (i = 0; i < len; i += portWidth / 8) { + if (portWidth == 8) { + if (data[i] != 0xffu) { + break; + } + } + else if (portWidth == 16) { + val = data[i] | (data[i + 1] << 8); + if (val != 0xffffu) { + break; + } + } + else { + return -EINVAL; + } + } + + len -= i; + offs += i; + data += i; + + if (len == 0) { + return 0; + } + + off_t sectorOffs = common_getSectorOffset(ctx->sectorsz, offs); + + ctx->dev->ops->issueWriteBuffer(common.base, sectorOffs, offs, len); + + i = 0; + while (i < len) { + switch (portWidth) { + case 8: + *(common.base + offs + i) = data[i]; + i++; + break; + + case 16: + val = data[i] | (data[i + 1] << 8); + *(uint16_t *)(common.base + offs + i) = val; + i += 2; + break; + + default: + break; + } + } + + ctx->dev->ops->issueWriteConfirm(common.base, sectorOffs); + + int res; + if (ctx->dev->usePolling != 0) { + flash_word_t word; + switch (portWidth) { + case 8: + word.b = data[len - 1]; + break; + + case 16: + word.w = data[len - 2] | (data[len - 1] << 8); + break; + + default: + return -EINVAL; + } + res = flash_statusPoll(ctx, word, common.base + offs + len - (portWidth / 8), timeout); + } + else { + res = flash_statusWait(ctx, timeout); + } + + int status = ctx->dev->ops->statusCheck(common.base, "write buffer"); + + ctx->dev->ops->statusClear(common.base); + + return (res == 0) ? status : res; +} + + +int ftmctrl_flash_sectorErase(const struct _storage_devCtx_t *ctx, off_t sectorOffs, time_t timeout) +{ + ctx->dev->ops->issueSectorErase(common.base, sectorOffs); + + int res; + if (ctx->dev->usePolling != 0) { + flash_word_t word; + int portWidth = ftmctrl_portWidth(ctx->ftmctrl); + switch (portWidth) { + case 8: + word.b = 0xffu; + break; + + case 16: + word.w = 0xffffu; + break; + + default: + return -EINVAL; + } + res = flash_statusPoll(ctx, word, common.base + sectorOffs + ctx->sectorsz - (portWidth / 8), timeout); + } + else { + res = flash_statusWait(ctx, timeout); + } + + int status = ctx->dev->ops->statusCheck(common.base, "sector erase"); + + ctx->dev->ops->statusClear(common.base); + + return (res == 0) ? status : res; +} + + +int ftmctrl_flash_chipErase(const struct _storage_devCtx_t *ctx, time_t timeout) +{ + if (ctx->dev->ops->issueChipErase == NULL) { + return -ENOSYS; + } + + ctx->dev->ops->issueChipErase(common.base); + + int res = flash_statusWait(ctx, timeout); + + int status = ctx->dev->ops->statusCheck(common.base, "chip erase"); + + ctx->dev->ops->statusClear(common.base); + + return (res == 0) ? status : res; +} + + +void ftmctrl_flash_read(const struct _storage_devCtx_t *ctx, off_t offs, void *buff, size_t len) +{ + ctx->dev->ops->issueReset(common.base); + + memcpy(buff, (void *)(common.base + offs), len); +} + + +void ftmctrl_flash_printInfo(const struct _storage_devCtx_t *ctx) +{ + LOG("configured %s %u MB flash", ctx->dev->name, CFI_SIZE(ctx->cfi.chipSz) / (1024 * 1024)); +} + + +static void flash_reset(void) +{ + /* Issue both commands as we don't know yet which flash we have */ + *common.base = AMD_CMD_RESET; + + /* Small delay */ + for (volatile int i = 0; i < 1000; i++) { } + + *common.base = INTEL_CMD_RESET; +} + + +static const flash_dev_t *flash_query(cfi_info_t *cfi) +{ + for (size_t i = 0; i < common.nmodels; i++) { + const addr_t queryOffs = 0x0; + const flash_dev_t *dev = common.devs[i]; + + flash_reset(); + dev->ops->enterQuery(common.base, queryOffs); + + /* Check 'QRY' header */ + uint8_t buf[3]; + for (size_t j = 0; j < sizeof(cfi->qry); ++j) { + const size_t offs = offsetof(cfi_info_t, qry); + buf[j] = *(common.base + queryOffs + (offs + j) * 2); + } + + if ((buf[0] != 'Q') || (buf[1] != 'R') || (buf[2] != 'Y')) { + continue; + } + + uint8_t *ptr = (uint8_t *)cfi; + for (size_t j = 0; j < sizeof(cfi_info_t); ++j) { + ptr[j] = *(common.base + queryOffs + j * 2); + } + + uint16_t device; + memcpy(&device, &cfi->vendorData[1], sizeof(device)); + /* x8 flash */ + device = flash_deserialize16(device) & 0xff; + + if ((cfi->vendorData[0] != dev->vendor) || (device != (dev->device & 0xff))) { + /* Command succeeded, but this is not the flash we're checking now */ + dev->ops->exitQuery(common.base); + continue; + } + + 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 (size_t j = 0; j < cfi->regionCnt; j++) { + cfi->regions[j].count = flash_deserialize16(cfi->regions[j].count); + cfi->regions[j].size = flash_deserialize16(cfi->regions[j].size); + } + + dev->ops->exitQuery(common.base); + + return dev; + } + + return NULL; +} + + +int ftmctrl_flash_init(struct _storage_devCtx_t *ctx, addr_t flashBase) +{ + ftmctrl_amd_register(); + ftmctrl_intel_register(); + + /* Temporarily map one page on flash as uncached to be able to read status */ + common.base = mmap(NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, flashBase); + if (common.base == MAP_FAILED) { + LOG_ERROR("failed to map flash"); + return -ENOMEM; + } + + const flash_dev_t *dev = flash_query(&ctx->cfi); + + munmap((void *)common.base, _PAGE_SIZE); + + if (dev == NULL) { + LOG_ERROR("failed to query flash"); + return -ENODEV; + } + + ctx->dev = dev; + ctx->sectorsz = CFI_SIZE(ctx->cfi.chipSz) / (ctx->cfi.regions[0].count + 1); + + /* Map entire flash */ + common.base = mmap(NULL, CFI_SIZE(ctx->cfi.chipSz), PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, flashBase); + if (common.base == MAP_FAILED) { + LOG_ERROR("failed to map flash"); + return -ENOMEM; + } + + return 0; +} + + +void ftmctrl_flash_destroy(struct _storage_devCtx_t *ctx) +{ + (void)munmap((void *)common.base, CFI_SIZE(ctx->cfi.chipSz)); +} + + +void ftmctrl_flash_register(const flash_dev_t *dev) +{ + if (common.nmodels >= FLASH_DEVICES) { + LOG("Too many flashes: %s not registered. Please increase FLASH_DEVICES", dev->name); + } + else { + common.devs[common.nmodels++] = dev; + } +} diff --git a/storage/flashdrv/drv/grlib-ftmctrl/flash.h b/storage/flashdrv/drv/grlib-ftmctrl/flash.h new file mode 100644 index 000000000..c146d8b78 --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/flash.h @@ -0,0 +1,80 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Internal flash functions + * + * Copyright 2023-2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FTMCTRL_FLASH_H_ +#define _FTMCTRL_FLASH_H_ + + +#include + +#include "flashdrv.h" + + +typedef struct { + uint8_t (*statusRead)(volatile uint8_t *base); + int (*statusCheck)(volatile uint8_t *base, const char *msg); + void (*statusClear)(volatile uint8_t *base); + void (*issueReset)(volatile uint8_t *base); + void (*issueWriteBuffer)(volatile uint8_t *base, off_t sectorOffs, off_t programOffs, size_t len); + void (*issueWriteConfirm)(volatile uint8_t *base, off_t sectorOffs); + void (*issueSectorErase)(volatile uint8_t *base, off_t sectorOffs); + void (*issueChipErase)(volatile uint8_t *base); + void (*enterQuery)(volatile uint8_t *base, off_t sectorOffs); + void (*exitQuery)(volatile uint8_t *base); +} flash_ops_t; + + +typedef struct _flash_dev_t { + const uint8_t statusRdyMask; + const uint8_t usePolling; + const uint8_t chipWidth; + const uint8_t vendor; + const uint16_t device; + const char *name; + const flash_ops_t *ops; +} flash_dev_t; + + +int ftmctrl_flash_writeBuffer(const struct _storage_devCtx_t *ctx, off_t offs, const uint8_t *data, size_t len, time_t timeout); + +/* Timeout in us */ +int ftmctrl_flash_sectorErase(const struct _storage_devCtx_t *ctx, off_t sectorOffs, time_t timeout); + + +int ftmctrl_flash_chipErase(const struct _storage_devCtx_t *ctx, time_t timeout); + + +void ftmctrl_flash_read(const struct _storage_devCtx_t *ctx, off_t offs, void *buff, size_t len); + + +void ftmctrl_flash_printInfo(const struct _storage_devCtx_t *ctx); + + +int ftmctrl_flash_init(struct _storage_devCtx_t *ctx, addr_t flashBase); + + +void ftmctrl_flash_destroy(struct _storage_devCtx_t *ctx); + + +void ftmctrl_flash_register(const flash_dev_t *dev); + + +void ftmctrl_amd_register(void); + + +void ftmctrl_intel_register(void); + + +#endif diff --git a/storage/flashdrv/drv/grlib-ftmctrl/flashdrv.c b/storage/flashdrv/drv/grlib-ftmctrl/flashdrv.c new file mode 100644 index 000000000..ed534a1c3 --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/flashdrv.c @@ -0,0 +1,374 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Copyright 2023-2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flashdrv.h" +#include "flash.h" +#include "ftmctrl.h" + + +#define LIBCACHE_LINECNT 1024 +#define LIBCACHE_POLICY LIBCACHE_WRITE_THROUGH + + +/* MTD interface */ + + +static int _flashdrv_mtdRead(storage_t *strg, off_t offs, void *buff, size_t len, size_t *retlen) +{ + struct _storage_devCtx_t *ctx = strg->dev->ctx; + if (!common_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len)) { + *retlen = 0; + return -EINVAL; + } + + if (len == 0u) { + *retlen = 0; + return 0; + } + + ftmctrl_WrEn(ctx->ftmctrl); + ftmctrl_flash_read(ctx, offs, buff, len); + ftmctrl_WrDis(ctx->ftmctrl); + + *retlen = len; + + return 0; +} + + +static ssize_t _flashdrv_mtdReadCb(uint64_t offs, void *buff, size_t len, cache_devCtx_t *ctx) +{ + storage_t *strg = ctx->strg; + size_t retlen; + ssize_t ret; + + ret = _flashdrv_mtdRead(strg, offs, buff, len, &retlen); + if (ret < 0) { + return ret; + } + + return (ssize_t)retlen; +} + + +static int flashdrv_mtdRead(storage_t *strg, off_t offs, void *buff, size_t len, size_t *retlen) +{ + mutexLock(strg->dev->ctx->lock); + int ret = cache_read(strg->dev->ctx->cache, offs, buff, len); + mutexUnlock(strg->dev->ctx->lock); + + if (ret < 0) { + *retlen = 0; + } + else { + *retlen = len; + ret = 0; + } + + return ret; +} + + +static int _flashdrv_mtdWrite(storage_t *strg, off_t offs, const void *buff, size_t len, size_t *retlen) +{ + struct _storage_devCtx_t *ctx = strg->dev->ctx; + if (!common_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len)) { + *retlen = 0; + return -EINVAL; + } + + if (len == 0u) { + *retlen = 0; + return 0; + } + + const uint8_t *src = buff; + size_t doneBytes = 0; + + int res = 0; + const size_t writeBuffsz = strg->dev->mtd->writeBuffsz; + + while (doneBytes < len) { + size_t chunk = min(writeBuffsz - (offs % writeBuffsz), len - doneBytes); + + ftmctrl_WrEn(ctx->ftmctrl); + res = ftmctrl_flash_writeBuffer(ctx, offs, src, chunk, CFI_TIMEOUT_MAX_PROGRAM(ctx->cfi.toutTypical.bufWrite, ctx->cfi.toutMax.bufWrite) * 2); + ftmctrl_WrDis(ctx->ftmctrl); + + if (res < 0) { + break; + } + + doneBytes += chunk; + src += chunk; + offs += chunk; + } + + *retlen = doneBytes; + + return res; +} + + +static ssize_t _flashdrv_mtdWriteCb(uint64_t offs, const void *buff, size_t len, cache_devCtx_t *ctx) +{ + storage_t *strg = ctx->strg; + size_t retlen; + ssize_t ret; + + if ((offs % strg->dev->mtd->writesz) != 0 || (len % strg->dev->mtd->writesz) != 0) { + return -EINVAL; + } + + ret = _flashdrv_mtdWrite(strg, offs, buff, len, &retlen); + if (ret < 0) { + return ret; + } + + return (ssize_t)retlen; +} + + +static int flashdrv_mtdWrite(storage_t *strg, off_t offs, const void *buff, size_t len, size_t *retlen) +{ + mutexLock(strg->dev->ctx->lock); + int ret = cache_write(strg->dev->ctx->cache, offs, buff, len, LIBCACHE_POLICY); + mutexUnlock(strg->dev->ctx->lock); + + if (ret < 0) { + *retlen = 0; + } + else { + *retlen = len; + ret = 0; + } + + return ret; +} + + +static int flashdrv_mtdErase(storage_t *strg, off_t offs, size_t len) +{ + if ((strg == NULL) || (strg->dev == NULL) || (strg->dev->ctx == NULL)) { + return -EINVAL; + } + + struct _storage_devCtx_t *ctx = strg->dev->ctx; + if (!common_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len) || ((offs & (ctx->sectorsz - 1)) != 0) || (len % ctx->sectorsz != 0)) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + mutexLock(ctx->lock); + ftmctrl_WrEn(ctx->ftmctrl); + + off_t end; + int res = -ENOSYS; + if ((offs == 0) && (len == CFI_SIZE(ctx->cfi.chipSz))) { + TRACE("erasing entire memory"); + res = ftmctrl_flash_chipErase(ctx, CFI_TIMEOUT_MAX_ERASE(ctx->cfi.toutTypical.chipErase, ctx->cfi.toutMax.chipErase)); + end = CFI_SIZE(ctx->cfi.chipSz); + } + else { + end = common_getSectorOffset(ctx->sectorsz, offs + len + ctx->sectorsz - 1u); + TRACE("erasing sectors from 0x%jx to 0x%jx", (uintmax_t)offs, (uintmax_t)end); + } + + if (res == -ENOSYS) { + off_t secOffs = offs; + while (secOffs < end) { + res = ftmctrl_flash_sectorErase(ctx, secOffs, CFI_TIMEOUT_MAX_ERASE(ctx->cfi.toutTypical.blkErase, ctx->cfi.toutMax.blkErase) * 2); + if (res < 0) { + break; + } + secOffs += ctx->sectorsz; + } + } + + ftmctrl_WrDis(ctx->ftmctrl); + res = cache_invalidate(ctx->cache, offs, end); + mutexUnlock(ctx->lock); + + return res; +} + + +static const storage_mtdops_t mtdOps = { + .erase = flashdrv_mtdErase, + .unPoint = NULL, + .point = NULL, + .read = flashdrv_mtdRead, + .write = flashdrv_mtdWrite, + + .meta_read = NULL, + .meta_write = NULL, + + .sync = NULL, + .lock = NULL, + .unLock = NULL, + .isLocked = NULL, + + .block_isBad = NULL, + .block_isReserved = NULL, + .block_markBad = NULL, + .block_maxBadNb = NULL, + + .suspend = NULL, + .resume = NULL, + .reboot = NULL, +}; + + +static void flashdrv_destroy(storage_t *strg) +{ + if (strg == NULL) { + return; + } + + if (strg->dev != NULL) { + if (strg->dev->ctx != NULL) { + if (strg->dev->ctx->cache != NULL) { + cache_deinit(strg->dev->ctx->cache); + } + (void)resourceDestroy(strg->dev->ctx->lock); + (void)munmap(strg->dev->ctx->ftmctrl, _PAGE_SIZE); + ftmctrl_flash_destroy(strg->dev->ctx); + } + free(strg->dev->ctx); + free(strg->dev->mtd); + free(strg->dev); + } + free(strg); + strg = NULL; +} + + +static storage_t *flashdrv_init(addr_t mctrlBase, addr_t flashBase) +{ + struct _storage_devCtx_t *ctx = malloc(sizeof(struct _storage_devCtx_t)); + if (ctx == NULL) { + return NULL; + } + ctx->ftmctrl = mmap(NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, mctrlBase); + if (ctx->ftmctrl == MAP_FAILED) { + free(ctx); + return NULL; + } + + ftmctrl_WrEn(ctx->ftmctrl); + int res = ftmctrl_flash_init(ctx, flashBase); + ftmctrl_WrDis(ctx->ftmctrl); + if (res < 0) { + (void)munmap(ctx->ftmctrl, _PAGE_SIZE); + free(ctx); + return NULL; + } + + if (mutexCreate(&ctx->lock) < 0) { + (void)munmap(ctx->ftmctrl, _PAGE_SIZE); + ftmctrl_flash_destroy(ctx); + free(ctx); + return NULL; + } + + storage_t *strg = calloc(1, sizeof(storage_t)); + if (strg == NULL) { + (void)munmap(ctx->ftmctrl, _PAGE_SIZE); + ftmctrl_flash_destroy(ctx); + free(ctx); + return NULL; + } + + strg->start = 0; + strg->size = CFI_SIZE(ctx->cfi.chipSz); + + strg->dev = calloc(1, sizeof(storage_dev_t)); + if (strg->dev == NULL) { + (void)munmap(ctx->ftmctrl, _PAGE_SIZE); + ftmctrl_flash_destroy(ctx); + free(ctx); + free(strg); + return NULL; + } + + /* Assign device context */ + strg->dev->ctx = ctx; + + storage_mtd_t *mtd = calloc(1, sizeof(storage_mtd_t)); + if (mtd == NULL) { + flashdrv_destroy(strg); + return NULL; + } + + /* MTD interface */ + mtd->ops = &mtdOps; + mtd->type = mtd_norFlash; + mtd->name = ctx->dev->name; + mtd->metaSize = 0; + mtd->oobSize = 0; + mtd->oobAvail = 0; + + uint8_t shift = ((ctx->dev->chipWidth == 16) && (ftmctrl_portWidth(ctx->ftmctrl) == 8)) ? 1 : 0; + mtd->writeBuffsz = CFI_SIZE(ctx->cfi.bufSz) >> shift; + mtd->writesz = 1; + mtd->erasesz = ctx->sectorsz; + + strg->dev->mtd = mtd; + + /* No block device interface */ + strg->dev->blk = NULL; + + /* Initialize cache */ + cache_ops_t cacheOps = { + .readCb = _flashdrv_mtdReadCb, + .writeCb = _flashdrv_mtdWriteCb, + .ctx = &strg->dev->ctx->cacheCtx + }; + strg->dev->ctx->cache = cache_init(strg->size, strg->dev->mtd->writeBuffsz, LIBCACHE_LINECNT, &cacheOps); + if (strg->dev->ctx->cache == NULL) { + flashdrv_destroy(strg); + return NULL; + } + strg->dev->ctx->cacheCtx.strg = strg; + + ftmctrl_flash_printInfo(ctx); + + return strg; +} + + +void __attribute__((constructor)) ftmctrl_register(void) +{ + static const struct flash_driver ftmctrl = { + .name = "ftmctrl", + .init = flashdrv_init, + .destroy = flashdrv_destroy, + }; + + flashsrv_register(&ftmctrl); +} diff --git a/storage/flashdrv/drv/grlib-ftmctrl/flashdrv.h b/storage/flashdrv/drv/grlib-ftmctrl/flashdrv.h new file mode 100644 index 000000000..b0fbac327 --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/flashdrv.h @@ -0,0 +1,44 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Copyright 2023-2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef _FTMCTRL_FLASHDRV_H_ +#define _FTMCTRL_FLASHDRV_H_ + + +#include +#include +#include +#include + + +struct cache_devCtx_s { + struct _storage_t *strg; +}; + + +struct _storage_devCtx_t { + cfi_info_t cfi; + const struct _flash_dev_t *dev; + + void *ftmctrl; + + handle_t lock; + size_t sectorsz; + + cachectx_t *cache; + cache_devCtx_t cacheCtx; +}; + + +#endif diff --git a/storage/gr712rc-flash/ftmctrl.h b/storage/flashdrv/drv/grlib-ftmctrl/ftmctrl.h similarity index 75% rename from storage/gr712rc-flash/ftmctrl.h rename to storage/flashdrv/drv/grlib-ftmctrl/ftmctrl.h index 73eda8040..59e57fe86 100644 --- a/storage/gr712rc-flash/ftmctrl.h +++ b/storage/flashdrv/drv/grlib-ftmctrl/ftmctrl.h @@ -1,11 +1,11 @@ /* * Phoenix-RTOS * - * Operating system loader + * GRLIB FTMCTRL Flash driver * * FTMCTRL routines * - * Copyright 2023 Phoenix Systems + * Copyright 2023-2025 Phoenix Systems * Author: Lukasz Leczkowski * * This file is part of Phoenix-RTOS. @@ -17,9 +17,6 @@ #define _FTMCTRL_H_ -#define FTMCTRL_BASE 0x80000000 - - #include @@ -47,4 +44,10 @@ static inline void ftmctrl_ioDis(volatile uint32_t *ftmctrl) } +static inline int ftmctrl_portWidth(volatile uint32_t *ftmctrl) +{ + return (((*ftmctrl >> 8) & 0x3) == 0) ? 8 : 16; +} + + #endif diff --git a/storage/flashdrv/drv/grlib-ftmctrl/intel-flash.c b/storage/flashdrv/drv/grlib-ftmctrl/intel-flash.c new file mode 100644 index 000000000..9e26a96df --- /dev/null +++ b/storage/flashdrv/drv/grlib-ftmctrl/intel-flash.c @@ -0,0 +1,133 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Intel command set flash interface + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "cmds.h" +#include "flash.h" + +#include + +#include + + +/* Valid offset on flash - for executing commands */ +#define FLASH_VALID_OFFS 0x0 +/* Flash status register */ +#define STS_FULL_CHECK ((1 << 5) | (1 << 4) | (1 << 3) | (1 << 1)) +#define XSR_WRBUF_RDY (1 << 7) /* Write buffer ready */ + + +static uint8_t intel_statusRead(volatile uint8_t *base) +{ + *(base + FLASH_VALID_OFFS) = CMD_RD_STATUS; + + return *base; +} + + +static void intel_statusClear(volatile uint8_t *base) +{ + *(base + FLASH_VALID_OFFS) = INTEL_CMD_CLR_STATUS; +} + + +static int intel_statusCheck(volatile uint8_t *base, const char *msg) +{ + int ret = 0; + uint8_t status = intel_statusRead(base); + + if ((status & STS_FULL_CHECK) != 0) { + LOG_ERROR("dev/flash: %s error: status 0x%x\n", msg, status); + ret = -EIO; + } + + intel_statusClear(base); + + return ret; +} + + +static void intel_issueReset(volatile uint8_t *base) +{ + *(base + FLASH_VALID_OFFS) = INTEL_CMD_RESET; +} + +static void intel_issueWriteBuffer(volatile uint8_t *base, off_t sectorOffs, off_t programOffs, size_t len) +{ + uint8_t xsr; + do { + *(base + programOffs) = INTEL_CMD_WR_BUF; + xsr = *base; + } while ((xsr & XSR_WRBUF_RDY) == 0); + + *(base + programOffs) = (len - 1) & 0xff; +} + + +static void intel_issueWriteConfirm(volatile uint8_t *base, off_t sectorOffs) +{ + *(base + sectorOffs) = INTEL_CMD_WR_CONFIRM; +} + + +static void intel_issueSectorErase(volatile uint8_t *base, off_t sectorOffs) +{ + *(base + sectorOffs) = INTEL_CMD_BE_CYC1; + *(base + sectorOffs) = INTEL_CMD_WR_CONFIRM; +} + + +static void intel_enterQuery(volatile uint8_t *base, off_t sectorOffs) +{ + *(base + sectorOffs) = CMD_RD_QUERY; +} + + +static void intel_exitQuery(volatile uint8_t *base) +{ + (void)base; +} + + +void ftmctrl_intel_register(void) +{ + static const flash_ops_t ops = { + .statusRead = intel_statusRead, + .statusCheck = intel_statusCheck, + .statusClear = intel_statusClear, + .issueReset = intel_issueReset, + .issueWriteBuffer = intel_issueWriteBuffer, + .issueWriteConfirm = intel_issueWriteConfirm, + .issueSectorErase = intel_issueSectorErase, + .issueChipErase = NULL, /* Not supported */ + .enterQuery = intel_enterQuery, + .exitQuery = intel_exitQuery + }; + + static const flash_dev_t intel_devices[] = { + { + .name = "Intel JS28F640J3", + .vendor = 0x89u, + .device = 0x0017u, + .chipWidth = 8, + .statusRdyMask = (1 << 7), + .usePolling = 0, + .ops = &ops, + }, + }; + + for (size_t i = 0; i < sizeof(intel_devices) / sizeof(intel_devices[0]); i++) { + ftmctrl_flash_register(&intel_devices[i]); + } +} diff --git a/storage/flashdrv/drv/grlib-spimctrl/Makefile b/storage/flashdrv/drv/grlib-spimctrl/Makefile new file mode 100644 index 000000000..8f17025c3 --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for Phoenix-RTOS libspimctrl driver +# +# Copyright 2025 Phoenix Systems +# + +NAME := libspimctrl +LOCAL_SRCS := flash.c flashdrv.c spimctrl.c +LOCAL_CFLAGS := -fvisibility=hidden + +include $(static-lib.mk) diff --git a/storage/flashdrv/drv/grlib-spimctrl/flash.c b/storage/flashdrv/drv/grlib-spimctrl/flash.c new file mode 100644 index 000000000..b714cfd72 --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/flash.c @@ -0,0 +1,440 @@ +/* + * Phoenix-RTOS + * + * GRLIB SPIMCTRL Flash driver + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include + +#include "flash.h" +#include "flashdrv.h" + + +#define FLASH_CMD_RDID 0x9fu + +/* Status register */ + +#define FLASH_SR_WIP 0x01u /* Write in progress */ +#define FLASH_SR_WEL 0x02u /* Write enable latch */ + +#define VID_MACRONIX 0xc2u +#define VID_SPANSION 0x01u + +/* clang-format off */ + +enum { write_disable = 0, write_enable }; + + +static const struct flash_cmds flash_spansionCmds = { + .rdsr = 0x05u, .wren = 0x06u, .wrdi = 0x04u, .rdear = 0x16u, + .wrear = 0x17u, .ce = 0x60u, .se = 0xd8u, .pp = 0x02u, .read = 0x03u +}; +/* clang-format on */ + + +static const struct flash_dev flash_devs[] = { + { "S25FL128S", VID_SPANSION, 0x2018u, &flash_spansionCmds } +}; + + +static struct { + void *base; +} common; + + +static int flash_readStatus(const struct flash_dev *dev, struct spimctrl *spimctrl, uint8_t *status) +{ + struct xferOp xfer; + const uint8_t cmd = dev->cmds->rdsr; + + xfer.type = xfer_opRead; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.rxData = status; + xfer.dataLen = 1; + + return spimctrl_xfer(spimctrl, &xfer); +} + + +static int flash_waitBusy(const struct flash_dev *dev, struct spimctrl *spimctrl, time_t timeout) +{ + int res; + uint8_t status = 0; + time_t now, end; + (void)gettime(&end, NULL); + + end += timeout * 1000; + + do { + res = flash_readStatus(dev, spimctrl, &status); + if (res < 0) { + return res; + } + + (void)gettime(&now, NULL); + if ((timeout > 0) && (now > end)) { + return -ETIME; + } + } while ((status & FLASH_SR_WIP) != 0); + + return 0; +} + + +static int flash_writeEnable(const struct flash_dev *dev, struct spimctrl *spimctrl, int enable) +{ + int res; + struct xferOp xfer; + uint8_t status = 0; + const uint8_t cmd = (enable == 1) ? dev->cmds->wren : dev->cmds->wrdi; + + res = flash_waitBusy(dev, 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 < 0) { + return res; + } + + res = flash_readStatus(dev, spimctrl, &status); + if (res < 0) { + return res; + } + + status = (status & FLASH_SR_WEL) ? 1 : 0; + + if (status != enable) { + return -EIO; + } + + return 0; +} + + +static int flash_readEAR(const struct flash_dev *dev, struct spimctrl *spimctrl, uint8_t *status) +{ + struct xferOp xfer; + const uint8_t cmd = dev->cmds->rdear; + + xfer.type = xfer_opRead; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.rxData = status; + xfer.dataLen = 1; + + return spimctrl_xfer(spimctrl, &xfer); +} + + +static int flash_writeEAR(const struct flash_dev *dev, struct spimctrl *spimctrl, uint8_t value) +{ + int res; + struct xferOp xfer; + const uint8_t cmd = dev->cmds->wrear; + + flash_writeEnable(dev, 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 < 0) { + return res; + } + + res = flash_readEAR(dev, spimctrl, &spimctrl->ear); + if (res < 0) { + return res; + } + + if (spimctrl->ear != value) { + return -EIO; + } + + return 0; +} + + +static int flash_validateEar(const struct flash_dev *dev, struct spimctrl *spimctrl, uint32_t addr) +{ + int res = 0; + const uint8_t desiredEar = (addr >> 24) & 0xffu; + + if (desiredEar != spimctrl->ear) { + res = flash_writeEAR(dev, spimctrl, desiredEar); + } + return res; +} + + +int spimctrl_flash_chipErase(const struct _storage_devCtx_t *ctx, time_t timeout) +{ + int res; + struct xferOp xfer; + const uint8_t cmd = ctx->dev->cmds->ce; + + res = flash_writeEnable(ctx->dev, ctx->spimctrl, write_enable); + if (res < 0) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.txData = NULL; + xfer.dataLen = 0; + + res = spimctrl_xfer(ctx->spimctrl, &xfer); + if (res < 0) { + return res; + } + + return flash_waitBusy(ctx->dev, ctx->spimctrl, timeout); +} + + +int spimctrl_flash_sectorErase(const struct _storage_devCtx_t *ctx, addr_t addr, time_t timeout) +{ + int res = 0; + struct xferOp xfer; + uint8_t cmd[4] = { ctx->dev->cmds->se, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + size_t regionEraseSz = 0, regionEnd = 0; + + for (size_t i = 0; i < ctx->cfi.regionCnt; i++) { + regionEnd += (ctx->cfi.regions[i].count + 1) * CFI_REGION_SIZE(ctx->cfi.regions[i].size); + if (addr < regionEnd) { + regionEraseSz = ctx->cfi.regions[i].size; + break; + } + } + + if (regionEraseSz == 0) { + return -EINVAL; + } + + for (size_t i = 0; i < ctx->sectorsz / regionEraseSz; i++) { + res = flash_validateEar(ctx->dev, ctx->spimctrl, addr); + if (res < 0) { + return res; + } + + res = flash_writeEnable(ctx->dev, ctx->spimctrl, write_enable); + if (res < 0) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = cmd; + xfer.cmdLen = 4; + xfer.txData = NULL; + xfer.dataLen = 0; + + res = spimctrl_xfer(ctx->spimctrl, &xfer); + if (res < 0) { + return res; + } + + res = flash_waitBusy(ctx->dev, ctx->spimctrl, timeout); + if (res < 0) { + return res; + } + + addr += regionEraseSz; + cmd[1] = (addr >> 16) & 0xff; + cmd[2] = (addr >> 8) & 0xff; + cmd[3] = addr & 0xff; + } + + return 0; +} + + +int spimctrl_flash_pageProgram(const struct _storage_devCtx_t *ctx, addr_t addr, const void *src, size_t len, time_t timeout) +{ + struct xferOp xfer; + const uint8_t cmd[4] = { ctx->dev->cmds->pp, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + + int res = flash_validateEar(ctx->dev, ctx->spimctrl, addr); + if (res < 0) { + return res; + } + + res = flash_writeEnable(ctx->dev, ctx->spimctrl, write_enable); + if (res < 0) { + return res; + } + + xfer.type = xfer_opWrite; + xfer.cmd = cmd; + xfer.cmdLen = 4; + xfer.txData = src; + xfer.dataLen = len; + + res = spimctrl_xfer(ctx->spimctrl, &xfer); + if (res < 0) { + return res; + } + + res = flash_waitBusy(ctx->dev, ctx->spimctrl, timeout); + + return res; +} + + +static ssize_t flash_readCmd(const struct flash_dev *dev, struct spimctrl *spimctrl, addr_t addr, void *data, size_t size) +{ + struct xferOp xfer; + const uint8_t cmd[4] = { dev->cmds->read, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; + + int res = flash_validateEar(dev, spimctrl, addr); + if (res < 0) { + 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 < 0) ? res : (ssize_t)size; +} + + +static ssize_t flash_readAhb(const struct flash_dev *dev, struct spimctrl *spimctrl, addr_t addr, void *data, size_t size) +{ + int res = flash_validateEar(dev, spimctrl, addr); + if (res < 0) { + return res; + } + + (void)memcpy(data, (void *)(addr + (uintptr_t)common.base), size); + + return (res < 0) ? res : (ssize_t)size; +} + + +ssize_t spimctrl_flash_readData(const struct _storage_devCtx_t *ctx, 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 flash_readCmd(ctx->dev, ctx->spimctrl, addr, data, size); + } + else { + /* Direct copy */ + return flash_readAhb(ctx->dev, ctx->spimctrl, addr, data, size); + } +} + + +static int flash_readId(const struct spimctrl *spimctrl, cfi_info_t *cfi) +{ + struct xferOp xfer; + const uint8_t cmd = FLASH_CMD_RDID; + + xfer.type = xfer_opRead; + xfer.cmd = &cmd; + xfer.cmdLen = 1; + xfer.rxData = (uint8_t *)cfi; + xfer.dataLen = sizeof(*cfi); + + return spimctrl_xfer(spimctrl, &xfer); +} + + +static uint16_t flash_deserialize16(uint16_t value) +{ + return ((value & 0xff) << 8) | ((value >> 8) & 0xff); +} + + +static const struct flash_dev *flash_query(struct spimctrl *spimctrl, cfi_info_t *cfi) +{ + if (flash_readId(spimctrl, cfi) < 0) { + return NULL; + } + + uint16_t device; + memcpy(&device, &cfi->vendorData[1], sizeof(device)); + device = flash_deserialize16(device); + + for (size_t i = 0; i < sizeof(flash_devs) / sizeof(flash_devs[0]); ++i) { + const struct flash_dev *flash = &flash_devs[i]; + if ((cfi->vendorData[0] == flash->vendor) && (device == flash->device)) { + + if (flash_readEAR(flash, spimctrl, &spimctrl->ear) < 0) { + return NULL; + } + + return flash; + } + } + + return NULL; +} + + +void spimctrl_flash_printInfo(const struct _storage_devCtx_t *ctx) +{ + LOG("configured %s %u MB flash", ctx->dev->name, CFI_SIZE(ctx->cfi.chipSz) / (1024 * 1024)); +} + + +int spimctrl_flash_init(struct _storage_devCtx_t *ctx, addr_t flashBase) +{ + ctx->dev = flash_query(ctx->spimctrl, &ctx->cfi); + + if (ctx->dev == NULL) { + return -1; + } + + ctx->sectorsz = 0; + + for (uint8_t reg = 0; reg < ctx->cfi.regionCnt; ++reg) { + if (ctx->sectorsz < CFI_REGION_SIZE(ctx->cfi.regions[reg].size)) { + ctx->sectorsz = CFI_REGION_SIZE(ctx->cfi.regions[reg].size); + } + } + + /* Map entire flash */ + common.base = mmap(NULL, CFI_SIZE(ctx->cfi.chipSz), PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, flashBase); + if (common.base == MAP_FAILED) { + LOG_ERROR("failed to map flash"); + return -ENOMEM; + } + + return 0; +} + + +void spimctrl_flash_destroy(struct _storage_devCtx_t *ctx) +{ + spimctrl_destroy(ctx->spimctrl); + (void)munmap(common.base, CFI_SIZE(ctx->cfi.chipSz)); +} diff --git a/storage/flashdrv/drv/grlib-spimctrl/flash.h b/storage/flashdrv/drv/grlib-spimctrl/flash.h new file mode 100644 index 000000000..98fb6582a --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/flash.h @@ -0,0 +1,72 @@ +/* + * Phoenix-RTOS + * + * GRLIB SPIMCTRL Flash driver + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _SPIMCTRL_FLASH_H_ +#define _SPIMCTRL_FLASH_H_ + + +#include + +#include + +#include "spimctrl.h" + +#define NOR_ERASED_STATE 0xffu +#define NOR_SECTORSZ_MAX 0x10000u +#define NOR_PAGESZ_MAX 0x100u + + +struct flash_cmds { + uint8_t rdsr; /* Read status register */ + uint8_t wren; /* Write enable */ + uint8_t wrdi; /* Write disable */ + uint8_t rdear; /* Read bank/extended address register */ + uint8_t wrear; /* Write bank/extended address register */ + uint8_t ce; /* Chip erase */ + uint8_t se; /* Sector erase */ + uint8_t pp; /* Page program */ + uint8_t read; +}; + + +struct flash_dev { + const char *name; + const uint8_t vendor; + const uint16_t device; + + const struct flash_cmds *cmds; +}; + + +int spimctrl_flash_chipErase(const struct _storage_devCtx_t *ctx, time_t timeout); + + +int spimctrl_flash_sectorErase(const struct _storage_devCtx_t *ctx, addr_t addr, time_t timeout); + + +int spimctrl_flash_pageProgram(const struct _storage_devCtx_t *ctx, addr_t addr, const void *src, size_t len, time_t timeout); + + +ssize_t spimctrl_flash_readData(const struct _storage_devCtx_t *ctx, addr_t addr, void *buff, size_t len); + + +void spimctrl_flash_printInfo(const struct _storage_devCtx_t *ctx); + + +int spimctrl_flash_init(struct _storage_devCtx_t *ctx, addr_t flashBase); + + +void spimctrl_flash_destroy(struct _storage_devCtx_t *ctx); + + +#endif /* _NOR_H_ */ diff --git a/storage/flashdrv/drv/grlib-spimctrl/flashdrv.c b/storage/flashdrv/drv/grlib-spimctrl/flashdrv.c new file mode 100644 index 000000000..cbd66816d --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/flashdrv.c @@ -0,0 +1,376 @@ +/* + * Phoenix-RTOS + * + * GRLIB SPIMCTRL Flash driver + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flashdrv.h" +#include "flash.h" + + +#define LIBCACHE_LINECNT 1024 +#define LIBCACHE_POLICY LIBCACHE_WRITE_BACK + + +/* MTD interface */ + + +static int _flashdrv_mtdRead(storage_t *strg, off_t offs, void *buff, size_t len, size_t *retlen) +{ + struct _storage_devCtx_t *ctx = strg->dev->ctx; + if (!common_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len)) { + *retlen = 0; + return -EINVAL; + } + + if (len == 0u) { + *retlen = 0; + return 0; + } + + ssize_t ret = spimctrl_flash_readData(ctx, offs, buff, len); + if (ret < 0) { + *retlen = 0; + return ret; + } + + *retlen = len; + + return 0; +} + + +static ssize_t _flashdrv_mtdReadCb(uint64_t offs, void *buff, size_t len, cache_devCtx_t *ctx) +{ + storage_t *strg = ctx->strg; + size_t retlen; + ssize_t ret; + + ret = _flashdrv_mtdRead(strg, offs, buff, len, &retlen); + if (ret < 0) { + return ret; + } + + return (ssize_t)retlen; +} + + +static int flashdrv_mtdRead(storage_t *strg, off_t offs, void *buff, size_t len, size_t *retlen) +{ + mutexLock(strg->dev->ctx->lock); + int ret = cache_read(strg->dev->ctx->cache, offs, buff, len); + mutexUnlock(strg->dev->ctx->lock); + + if (ret < 0) { + *retlen = 0; + } + else { + *retlen = len; + ret = 0; + } + + return ret; +} + + +static int _flashdrv_mtdWrite(storage_t *strg, off_t offs, const void *buff, size_t len, size_t *retlen) +{ + struct _storage_devCtx_t *ctx = strg->dev->ctx; + if (!common_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len)) { + *retlen = 0; + return -EINVAL; + } + + if (len == 0u) { + *retlen = 0; + return 0; + } + + const uint8_t *src = buff; + size_t doneBytes = 0; + + int res = 0; + const size_t pagesz = strg->dev->mtd->writeBuffsz; + + while (doneBytes < len) { + size_t chunk = min(pagesz - (offs % pagesz), len - doneBytes); + + res = spimctrl_flash_pageProgram(ctx, offs, src, chunk, + CFI_TIMEOUT_MAX_PROGRAM(ctx->cfi.toutTypical.bufWrite, ctx->cfi.toutMax.bufWrite)); + + if (res < 0) { + break; + } + + doneBytes += chunk; + src += chunk; + offs += chunk; + } + + *retlen = doneBytes; + + return res; +} + + +static ssize_t _flashdrv_mtdWriteCb(uint64_t offs, const void *buff, size_t len, cache_devCtx_t *ctx) +{ + storage_t *strg = ctx->strg; + size_t retlen; + ssize_t ret; + + if ((offs % strg->dev->mtd->writesz) != 0 || (len % strg->dev->mtd->writesz) != 0) { + return -EINVAL; + } + + ret = _flashdrv_mtdWrite(strg, offs, buff, len, &retlen); + if (ret < 0) { + return ret; + } + + return (ssize_t)retlen; +} + + +static int flashdrv_mtdWrite(storage_t *strg, off_t offs, const void *buff, size_t len, size_t *retlen) +{ + mutexLock(strg->dev->ctx->lock); + int ret = cache_write(strg->dev->ctx->cache, offs, buff, len, LIBCACHE_POLICY); + mutexUnlock(strg->dev->ctx->lock); + + if (ret < 0) { + *retlen = 0; + } + else { + *retlen = len; + ret = 0; + } + + return ret; +} + + +static int flashdrv_mtdErase(storage_t *strg, off_t offs, size_t len) +{ + if ((strg == NULL) || (strg->dev == NULL) || (strg->dev->ctx == NULL)) { + return -EINVAL; + } + + struct _storage_devCtx_t *ctx = strg->dev->ctx; + if (!common_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len) || ((offs & (ctx->sectorsz - 1)) != 0) || (len % ctx->sectorsz != 0)) { + return -EINVAL; + } + + if (len == 0u) { + return 0; + } + + mutexLock(ctx->lock); + + off_t end; + int res = -ENOSYS; + if ((offs == 0) && (len == CFI_SIZE(ctx->cfi.chipSz))) { + TRACE("erasing entire memory"); + res = spimctrl_flash_chipErase(ctx, CFI_TIMEOUT_MAX_ERASE(ctx->cfi.toutTypical.chipErase, ctx->cfi.toutMax.chipErase)); + end = CFI_SIZE(ctx->cfi.chipSz); + } + else { + end = common_getSectorOffset(ctx->sectorsz, offs + len + ctx->sectorsz - 1u); + TRACE("erasing sectors from 0x%jx to 0x%jx", (uintmax_t)offs, (uintmax_t)end); + } + + if (res == -ENOSYS) { + off_t secOffs = offs; + while (secOffs < end) { + res = spimctrl_flash_sectorErase(ctx, secOffs, CFI_TIMEOUT_MAX_ERASE(ctx->cfi.toutTypical.blkErase, ctx->cfi.toutMax.blkErase)); + if (res < 0) { + break; + } + secOffs += ctx->sectorsz; + } + } + + res = cache_invalidate(ctx->cache, offs, end); + mutexUnlock(ctx->lock); + + return res; +} + + +static const storage_mtdops_t mtdOps = { + .erase = flashdrv_mtdErase, + .unPoint = NULL, + .point = NULL, + .read = flashdrv_mtdRead, + .write = flashdrv_mtdWrite, + + .meta_read = NULL, + .meta_write = NULL, + + .sync = NULL, + .lock = NULL, + .unLock = NULL, + .isLocked = NULL, + + .block_isBad = NULL, + .block_isReserved = NULL, + .block_markBad = NULL, + .block_maxBadNb = NULL, + + .suspend = NULL, + .resume = NULL, + .reboot = NULL, +}; + + +static void flashdrv_destroy(storage_t *strg) +{ + if (strg == NULL) { + return; + } + + if (strg->dev != NULL) { + if (strg->dev->ctx != NULL) { + if (strg->dev->ctx->cache != NULL) { + cache_deinit(strg->dev->ctx->cache); + } + (void)resourceDestroy(strg->dev->ctx->lock); + spimctrl_flash_destroy(strg->dev->ctx); + free(strg->dev->ctx->spimctrl); + } + free(strg->dev->ctx); + free(strg->dev->mtd); + free(strg->dev); + } + free(strg); + strg = NULL; +} + + +static storage_t *flashdrv_init(addr_t mctrlBase, addr_t flashBase) +{ + struct _storage_devCtx_t *ctx = calloc(1, sizeof(struct _storage_devCtx_t)); + if (ctx == NULL) { + LOG_ERROR(); + return NULL; + } + + ctx->spimctrl = calloc(1, sizeof(struct spimctrl)); + if (ctx->spimctrl == NULL) { + free(ctx); + return NULL; + } + + if (spimctrl_init(ctx->spimctrl, mctrlBase) < 0) { + free(ctx->spimctrl); + free(ctx); + return NULL; + } + + if (spimctrl_flash_init(ctx, flashBase) < 0) { + free(ctx->spimctrl); + free(ctx); + return NULL; + } + + if (mutexCreate(&ctx->lock) < 0) { + spimctrl_flash_destroy(ctx); + free(ctx->spimctrl); + free(ctx); + return NULL; + } + + storage_t *strg = calloc(1, sizeof(storage_t)); + if (strg == NULL) { + spimctrl_flash_destroy(ctx); + free(ctx->spimctrl); + free(ctx); + return NULL; + } + + strg->start = 0; + strg->size = CFI_SIZE(ctx->cfi.chipSz); + + strg->dev = calloc(1, sizeof(storage_dev_t)); + if (strg->dev == NULL) { + spimctrl_flash_destroy(ctx); + free(ctx->spimctrl); + free(ctx); + free(strg); + return NULL; + } + + /* Assign device context */ + strg->dev->ctx = ctx; + + storage_mtd_t *mtd = calloc(1, sizeof(storage_mtd_t)); + if (mtd == NULL) { + flashdrv_destroy(strg); + return NULL; + } + + /* MTD interface */ + mtd->ops = &mtdOps; + mtd->type = mtd_norFlash; + mtd->name = ctx->dev->name; + mtd->metaSize = 0; + mtd->oobSize = 0; + mtd->oobAvail = 0; + + mtd->writeBuffsz = CFI_SIZE(ctx->cfi.bufSz); + mtd->writesz = 1; + mtd->erasesz = ctx->sectorsz; + + strg->dev->mtd = mtd; + + /* No block device interface */ + strg->dev->blk = NULL; + + /* Initialize cache */ + cache_ops_t cacheOps = { + .readCb = _flashdrv_mtdReadCb, + .writeCb = _flashdrv_mtdWriteCb, + .ctx = &strg->dev->ctx->cacheCtx + }; + strg->dev->ctx->cache = cache_init(strg->size, strg->dev->mtd->writeBuffsz, LIBCACHE_LINECNT, &cacheOps); + if (strg->dev->ctx->cache == NULL) { + flashdrv_destroy(strg); + return NULL; + } + strg->dev->ctx->cacheCtx.strg = strg; + + spimctrl_flash_printInfo(ctx); + + return strg; +} + + +void __attribute__((constructor)) spimctrl_register(void) +{ + static const struct flash_driver spimctrl = { + .name = "spimctrl", + .init = flashdrv_init, + .destroy = flashdrv_destroy, + }; + + flashsrv_register(&spimctrl); +} diff --git a/storage/flashdrv/drv/grlib-spimctrl/flashdrv.h b/storage/flashdrv/drv/grlib-spimctrl/flashdrv.h new file mode 100644 index 000000000..c830a7093 --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/flashdrv.h @@ -0,0 +1,44 @@ +/* + * Phoenix-RTOS + * + * GRLIB SPIMCTRL Flash driver + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef _SPIMCTRL_FLASHDRV_H_ +#define _SPIMCTRL_FLASHDRV_H_ + + +#include +#include +#include +#include + + +struct cache_devCtx_s { + struct _storage_t *strg; +}; + + +struct _storage_devCtx_t { + cfi_info_t cfi; + const struct flash_dev *dev; + + struct spimctrl *spimctrl; + + handle_t lock; + size_t sectorsz; + + cachectx_t *cache; + cache_devCtx_t cacheCtx; +}; + + +#endif diff --git a/storage/flashdrv/drv/grlib-spimctrl/spimctrl.c b/storage/flashdrv/drv/grlib-spimctrl/spimctrl.c new file mode 100644 index 000000000..6ef253050 --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/spimctrl.c @@ -0,0 +1,161 @@ +/* + * Phoenix-RTOS + * + * GRLIB SPIMCTRL driver + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include + +#include + +#include "spimctrl.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(volatile uint32_t *spimctrlBase) +{ + *(spimctrlBase + flash_ctrl) = USR_CTRL; + *(spimctrlBase + flash_ctrl) &= ~CHIP_SEL; +} + + +static int spimctrl_busy(const struct spimctrl *spimctrl) +{ + return (*(spimctrl->base + flash_stat) & CORE_BUSY) >> 1; +} + + +static int spimctrl_ready(const struct spimctrl *spimctrl) +{ + uint32_t val = (*(spimctrl->base + flash_stat) & (INITIALIZED | OPER_DONE)); + + return (val == INITIALIZED) ? 1 : 0; +} + + +static void spimctrl_tx(volatile uint32_t *spimctrlBase, uint8_t cmd) +{ + *(spimctrlBase + flash_tx) = cmd; + while ((*(spimctrlBase + flash_stat) & OPER_DONE) == 0) { } + *(spimctrlBase + flash_stat) |= OPER_DONE; +} + + +static uint8_t spimctrl_rx(volatile uint32_t *spimctrlBase) +{ + return *(spimctrlBase + flash_rx) & 0xff; +} + + +static void spimctrl_read(const struct spimctrl *spimctrl, struct xferOp *op) +{ + spimctrl_userCtrl(spimctrl->base); + + /* send command */ + for (size_t i = 0; i < op->cmdLen; i++) { + spimctrl_tx(spimctrl->base, op->cmd[i]); + } + + /* read data */ + for (size_t i = 0; i < op->dataLen; i++) { + spimctrl_tx(spimctrl->base, 0x00u); + op->rxData[i] = spimctrl_rx(spimctrl->base); + } + + *(spimctrl->base + flash_ctrl) &= ~USR_CTRL; +} + + +static void spimctrl_write(const struct spimctrl *spimctrl, struct xferOp *op) +{ + spimctrl_userCtrl(spimctrl->base); + + /* Send command */ + for (size_t i = 0; i < op->cmdLen; i++) { + spimctrl_tx(spimctrl->base, op->cmd[i]); + } + + /* Send data */ + for (size_t i = 0; i < op->dataLen; i++) { + spimctrl_tx(spimctrl->base, op->txData[i]); + } + + *(spimctrl->base + flash_ctrl) &= ~USR_CTRL; +} + + +int spimctrl_xfer(const struct spimctrl *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 0; +} + + +void spimctrl_reset(const struct spimctrl *spimctrl) +{ + *(spimctrl->base + flash_ctrl) = CORE_RST; +} + + +int spimctrl_init(struct spimctrl *spimctrl, addr_t mctrlBase) +{ + spimctrl->base = mmap(NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, mctrlBase); + if (spimctrl->base == MAP_FAILED) { + return -ENOMEM; + } + + /* Reset core */ + spimctrl_reset(spimctrl); + + return 0; +} + + +void spimctrl_destroy(struct spimctrl *spimctrl) +{ + (void)munmap((void *)spimctrl->base, _PAGE_SIZE); +} diff --git a/storage/flashdrv/drv/grlib-spimctrl/spimctrl.h b/storage/flashdrv/drv/grlib-spimctrl/spimctrl.h new file mode 100644 index 000000000..db75d6686 --- /dev/null +++ b/storage/flashdrv/drv/grlib-spimctrl/spimctrl.h @@ -0,0 +1,57 @@ +/* + * Phoenix-RTOS + * + * GRLIB SPIMCTRL driver + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _SPIMCTRL_H_ +#define _SPIMCTRL_H_ + +#include +#include + + +struct spimctrl { + volatile uint32_t *base; + + uint8_t ear; +}; + + +struct xferOp { + /* clang-format off */ + enum { xfer_opRead = 0, xfer_opWrite } type; + /* clang-format on */ + const uint8_t *cmd; + size_t cmdLen; + union { + const uint8_t *txData; + uint8_t *rxData; + }; + size_t dataLen; +}; + + +/* Execute a transfer through spimctrl */ +int spimctrl_xfer(const struct spimctrl *spimctrl, struct xferOp *op); + + +/* Reset spimctrl core */ +void spimctrl_reset(const struct spimctrl *spimctrl); + + +/* Initialize spimctrl instance */ +int spimctrl_init(struct spimctrl *spimctrl, addr_t mctrlBase); + + +void spimctrl_destroy(struct spimctrl *spimctrl); + + +#endif diff --git a/storage/gr712rc-flash/flashsrv.c b/storage/flashdrv/flashsrv.c similarity index 65% rename from storage/gr712rc-flash/flashsrv.c rename to storage/flashdrv/flashsrv.c index fdedbb83b..c3b9c57f2 100644 --- a/storage/gr712rc-flash/flashsrv.c +++ b/storage/flashdrv/flashsrv.c @@ -1,9 +1,11 @@ /* * Phoenix-RTOS * - * GR712RC Flash server + * GRLIB FTMCTRL Flash driver * - * Copyright 2023 Phoenix Systems + * Flash server + * + * Copyright 2023-2025 Phoenix Systems * Author: Lukasz Leczkowski * * This file is part of Phoenix-RTOS. @@ -28,10 +30,29 @@ #include #include -#include "flashdrv.h" +#include #define STRG_PATH "mtd0" +#define MAX_DRIVERS 4 + + +struct flashsrv_opts { + const struct flash_driver *driver; + addr_t mctrlBase; + addr_t flashBase; + struct { + char *partname; + char *fs; + } root; +}; + + +static struct { + const struct flash_driver *registry[MAX_DRIVERS]; + size_t ndrivers; +} common; + /* Flash server operations */ @@ -93,7 +114,7 @@ static int flashsrv_sync(storage_t *strg) storage_mtd_t *mtd = strg->dev->mtd; if ((mtd != NULL) && (mtd->ops != NULL) && (mtd->ops->sync != NULL)) { mtd->ops->sync(strg); - return EOK; + return 0; } return -EINVAL; @@ -115,7 +136,7 @@ static int flashsrv_getAttr(storage_t *strg, int type, long long *attr) return -EINVAL; } - return EOK; + return 0; } @@ -131,46 +152,46 @@ static void flashsrv_msgHandler(void *arg, msg_t *msg) case mtOpen: case mtClose: strg = storage_get(msg->oid.id); - msg->o.err = (strg == NULL) ? -EINVAL : EOK; + msg->o.err = (strg == NULL) ? -EINVAL : 0; TRACE("mtOpen/mtClose: %d", msg->o.err); break; case mtRead: strg = storage_get(msg->oid.id); - TRACE("mtRead: id: %u, size: %d, off: %llu", msg->oid.id, msg->o.size, msg->i.io.offs); + TRACE("mtRead: id: %ju, size: %zu, off: %ju", (uintmax_t)msg->oid.id, msg->o.size, (uintmax_t)msg->i.io.offs); msg->o.err = flashsrv_read(strg, msg->i.io.offs, msg->o.data, msg->o.size); break; case mtWrite: strg = storage_get(msg->oid.id); - TRACE("mtWrite: id: %u, size: %d, off: %llu", msg->oid.id, msg->o.size, msg->i.io.offs); + TRACE("mtWrite: id: %ju, size: %zu, off: %ju", (uintmax_t)msg->oid.id, msg->o.size, (uintmax_t)msg->i.io.offs); msg->o.err = flashsrv_write(strg, msg->i.io.offs, msg->o.data, msg->o.size); break; case mtSync: strg = storage_get(msg->oid.id); - TRACE("mtSync: id: %u", msg->oid.id); + TRACE("mtSync: id: %ju", (uintmax_t)msg->oid.id); msg->o.err = flashsrv_sync(strg); break; case mtGetAttr: strg = storage_get(msg->oid.id); - TRACE("mtGetAttr: id: %u, type: %d", msg->oid.id, msg->i.attr.type); + TRACE("mtGetAttr: id: %ju, type: %d", (uintmax_t)msg->oid.id, msg->i.attr.type); msg->o.err = flashsrv_getAttr(strg, msg->i.attr.type, &msg->o.attr.val); break; case mtMount: - TRACE("mtMount: id: %u, fstype: %s, mode: %ld", msg->oid.id, imnt->fstype, imnt->mode); + TRACE("mtMount: id: %ju, fstype: %s, mode: %ld", (uintmax_t)msg->oid.id, imnt->fstype, imnt->mode); msg->o.err = storage_mountfs(storage_get(msg->oid.id), imnt->fstype, msg->i.data, imnt->mode, &imnt->mnt, &omnt->oid); break; case mtUmount: - TRACE("mtUmount: id: %u", msg->oid.id); + TRACE("mtUmount: id: %ju", (uintmax_t)msg->oid.id); msg->o.err = storage_umountfs(storage_get(msg->oid.id)); break; case mtMountPoint: - TRACE("mtMountPoint: id: %u", msg->oid.id); + TRACE("mtMountPoint: id: %ju", (uintmax_t)msg->oid.id); msg->o.err = storage_mountpoint(storage_get(msg->oid.id), &omnt->oid); break; @@ -213,7 +234,7 @@ static int flashsrv_mountRoot(const char *name, const char *fstype) return res; } - return EOK; + return 0; } @@ -223,109 +244,104 @@ static void flashsrv_help(const char *prog) printf("\t-r - mount partition as root\n"); printf("\t use psdisk to create partitions\n"); printf("\t-h - print this message\n"); + printf("\t-c - use as memory controller base address (required)\n"); + printf("\t-m - use as flash memory base address (required)\n"); + printf("\t-d - use for flash memory (required)\n"); + printf("\t available drivers: \n"); + printf("\t "); + + for (size_t i = 0; i < common.ndrivers; i++) { + printf("%s%s", common.registry[i]->name, (i < common.ndrivers - 1) ? ", " : "\n"); + } } -static int flashsrv_parseArgs(int argc, char **argv) +static int flashsrv_parseInitArgs(int argc, char **argv, struct flashsrv_opts *opts) { for (;;) { - int c = getopt(argc, argv, "r:h"); + int c = getopt(argc, argv, "r:d:c:m:h"); if (c == -1) { return 0; } - char *partName, *fs; switch (c) { - case 'r': - partName = optarg; - fs = strchr(optarg, ':'); - if (fs == NULL) { + case 'h': + flashsrv_help(argv[0]); + return -1; + + case 'd': { + const char *name = optarg; + for (size_t i = 0; i < common.ndrivers; i++) { + if (strcmp(common.registry[i]->name, name) == 0) { + opts->driver = common.registry[i]; + break; + } + } + if (opts->driver == NULL) { + LOG_ERROR("Unknown driver: %s", name); + return -1; + } + break; + } + + case 'r': { + opts->root.partname = optarg; + opts->root.fs = strchr(optarg, ':'); + if (opts->root.fs == NULL) { LOG_ERROR("Invalid argument: %s", optarg); return -1; } - *fs = '\0'; - fs++; - if (flashsrv_mountRoot(partName, fs) < 0) { - LOG_ERROR("Failed to mount root filesystem"); + *opts->root.fs = '\0'; + opts->root.fs++; + break; + } + + case 'c': + errno = 0; + opts->mctrlBase = strtoul(optarg, NULL, 0); + if ((opts->mctrlBase == 0) && (errno != 0)) { + LOG_ERROR("Invalid argument: %s", optarg); return -1; } break; - case 'h': - flashsrv_help(argv[0]); - return -1; + case 'm': + errno = 0; + opts->flashBase = strtoul(optarg, NULL, 0); + if ((opts->flashBase == 0) && (errno != 0)) { + LOG_ERROR("Invalid argument: %s", optarg); + return -1; + } + break; default: LOG_ERROR("Unknown option: %c", c); return -1; } } + + return 0; } /* Initialization functions */ -static int flashsrv_devInit(storage_t *strg, struct _storage_devCtx_t *ctx) +void flashsrv_register(const struct flash_driver *driver) { - if (strg->parent == NULL) { - strg->start = 0; - strg->size = CFI_SIZE(ctx->cfi.chipSz); + if (common.ndrivers < MAX_DRIVERS) { + common.registry[common.ndrivers++] = driver; } - - /* Initialize device structure */ - strg->dev = malloc(sizeof(storage_dev_t)); - if (strg->dev == NULL) { - return -ENOMEM; - } - - /* Initialize MTD interface */ - storage_mtd_t *mtd = malloc(sizeof(storage_mtd_t)); - if (mtd == NULL) { - free(strg->dev); - return -ENOMEM; - } - - mtd->ops = flashdrv_getMtdOps(); - mtd->type = mtd_norFlash; - mtd->name = "Intel"; - mtd->metaSize = 0; - mtd->oobSize = 0; - mtd->oobAvail = 0; - mtd->writeBuffsz = CFI_SIZE(ctx->cfi.bufSz); - mtd->writesz = 1; - mtd->erasesz = ctx->blockSz; - - strg->dev->mtd = mtd; - - /* No block device interface */ - strg->dev->blk = NULL; - - /* Assign device context */ - strg->dev->ctx = ctx; - - return EOK; -} - - -static void flashsrv_devDestroy(storage_t *strg) -{ - if (strg->dev != NULL) { - if (strg->dev->mtd != NULL) { - free(strg->dev->mtd); - } - if (strg->dev->blk != NULL) { - free(strg->dev->blk); - } - free(strg->dev); - strg->dev = NULL; + else { + LOG("Too many drivers: %s not registered. Please increase MAX_DRIVERS", driver->name); } } static ptable_t *flashsrv_ptableRead(storage_t *strg) { - uint32_t count, offs = CFI_SIZE(strg->dev->ctx->cfi.chipSz) - strg->dev->ctx->blockSz; + uint32_t count; + off_t offs = strg->size - strg->dev->mtd->erasesz; /* Read number of partitions */ if (flashsrv_read(strg, offs, &count, sizeof(count)) != sizeof(count)) { return NULL; @@ -334,7 +350,7 @@ static ptable_t *flashsrv_ptableRead(storage_t *strg) /* Verify ptable size */ uint32_t size = ptable_size(count); - if (size > strg->dev->ctx->blockSz) { + if (size > strg->dev->mtd->erasesz) { return NULL; } @@ -359,7 +375,7 @@ static ptable_t *flashsrv_ptableRead(storage_t *strg) return NULL; } - if (ptable_deserialize(ptable, CFI_SIZE(strg->dev->ctx->cfi.chipSz), strg->dev->ctx->blockSz) < 0) { + if (ptable_deserialize(ptable, strg->size, strg->dev->mtd->erasesz) < 0) { free(ptable); return NULL; } @@ -368,13 +384,11 @@ static ptable_t *flashsrv_ptableRead(storage_t *strg) } -static int flashsrv_partAdd(storage_t *parent, storage_t **newPart, uint32_t offset, uint32_t size, const char *name) +static int flashsrv_partAdd(storage_t *parent, uint32_t offset, uint32_t size, const char *name) { - unsigned int partId = 0; storage_t *part = parent->parts; if (part != NULL) { do { - partId++; part = part->next; } while (part != parent->parts); } @@ -388,19 +402,12 @@ static int flashsrv_partAdd(storage_t *parent, storage_t **newPart, uint32_t off part->parent = parent; part->start = offset; part->size = size; - - int res = flashsrv_devInit(part, parent->dev->ctx); - if (res < 0) { - LOG_ERROR("failed to initialize a partition"); - free(part); - return res; - } + part->dev = parent->dev; oid_t oid; - res = storage_add(part, &oid); + int res = storage_add(part, &oid); if (res < 0) { LOG_ERROR("failed to add a partition"); - flashsrv_devDestroy(part); free(part); return res; } @@ -409,7 +416,6 @@ static int flashsrv_partAdd(storage_t *parent, storage_t **newPart, uint32_t off if (snprintf(path, sizeof(path), "%s.%s", STRG_PATH, name) >= sizeof(path)) { LOG_ERROR("failed to build partition path"); storage_remove(part); - flashsrv_devDestroy(part); free(part); return -ENAMETOOLONG; } @@ -418,18 +424,13 @@ static int flashsrv_partAdd(storage_t *parent, storage_t **newPart, uint32_t off if (res < 0) { LOG_ERROR("failed to create partition device file"); storage_remove(part); - flashsrv_devDestroy(part); free(part); return res; } TRACE("initialized partition %s: offset=%u, size=%u", name, offset, size); - if (newPart != NULL) { - *newPart = part; - } - - return EOK; + return 0; } @@ -442,8 +443,7 @@ static int flashsrv_partsInit(storage_t *strg) } for (size_t i = 0; i < ptable->count; i++) { - storage_t *newPart; - if (flashsrv_partAdd(strg, &newPart, ptable->parts[i].offset, ptable->parts[i].size, (const char *)ptable->parts[i].name) < 0) { + if (flashsrv_partAdd(strg, ptable->parts[i].offset, ptable->parts[i].size, (const char *)ptable->parts[i].name) < 0) { LOG_ERROR("failed to add partition %s", (const char *)ptable->parts[i].name); free(ptable); return -1; @@ -456,36 +456,19 @@ static int flashsrv_partsInit(storage_t *strg) } -static storage_t *flashsrv_init(void) +static storage_t *flashsrv_init(struct flashsrv_opts *opts) { - storage_t *strg = calloc(1, sizeof(storage_t)); + storage_t *strg = opts->driver->init(opts->mctrlBase, opts->flashBase); if (strg == NULL) { - LOG_ERROR("failed to allocate storage_t"); - return NULL; - } - - struct _storage_devCtx_t *ctx = flashdrv_contextInit(); - if (ctx == NULL) { - LOG_ERROR("failed to initialize flash context"); - free(strg); - return NULL; - } - - int res = flashsrv_devInit(strg, ctx); - if (res < 0) { - LOG_ERROR("failed to initialize libstorage interface (%d)", res); - flashdrv_contextDestroy(ctx); - free(strg); + LOG_ERROR("failed initialize storage interface"); return NULL; } oid_t oid; - res = storage_add(strg, &oid); + int res = storage_add(strg, &oid); if (res < 0) { LOG_ERROR("failed to add storage device (%d)", res); - flashdrv_contextDestroy(ctx); - flashsrv_devDestroy(strg); - free(strg); + opts->driver->destroy(strg); return NULL; } @@ -493,9 +476,7 @@ static storage_t *flashsrv_init(void) if (res < 0) { LOG_ERROR("failed to create device file (%d)", res); storage_remove(strg); - flashdrv_contextDestroy(ctx); - flashsrv_devDestroy(strg); - free(strg); + opts->driver->destroy(strg); return NULL; } @@ -535,8 +516,26 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + struct flashsrv_opts opts = { + .driver = NULL, + .mctrlBase = (addr_t)-1, + .flashBase = (addr_t)-1, + .root = { 0 } + }; + + int err = flashsrv_parseInitArgs(argc, argv, &opts); + if (err < 0) { + exit(EXIT_FAILURE); + } + + if ((opts.driver == NULL) || (opts.mctrlBase == (addr_t)-1) || (opts.flashBase == (addr_t)-1)) { + LOG("Required arguments not present"); + flashsrv_help(argv[0]); + exit(EXIT_FAILURE); + } + /* Initialize storage library with the message handler for the flash memory */ - int err = storage_init(flashsrv_msgHandler, 16); + err = storage_init(flashsrv_msgHandler, 16); if (err < 0) { LOG_ERROR("failed to initialize server (%d)\n", err); exit(EXIT_FAILURE); @@ -550,7 +549,7 @@ int main(int argc, char **argv) } /* Initialize flash driver and mtd interface */ - storage_t *strg = flashsrv_init(); + storage_t *strg = flashsrv_init(&opts); if (strg == NULL) { exit(EXIT_FAILURE); } @@ -558,23 +557,28 @@ int main(int argc, char **argv) /* Read partition table and initialize */ if (flashsrv_partsInit(strg) < 0) { storage_remove(strg); - flashdrv_contextDestroy(strg->dev->ctx); - flashsrv_devDestroy(strg); - free(strg); + opts.driver->destroy(strg); exit(EXIT_FAILURE); } - if (flashsrv_parseArgs(argc, argv) < 0) { - storage_remove(strg); - flashdrv_contextDestroy(strg->dev->ctx); - flashsrv_devDestroy(strg); - free(strg); - exit(EXIT_FAILURE); + if ((opts.root.partname != NULL) && (opts.root.fs != NULL)) { + if (flashsrv_mountRoot(opts.root.partname, opts.root.fs) < 0) { + storage_remove(strg); + opts.driver->destroy(strg); + exit(EXIT_FAILURE); + } } /* Finished server initialization - kill parent */ kill(getppid(), SIGUSR1); - storage_run(1, 2 * _PAGE_SIZE); + err = storage_run(1, 2 * _PAGE_SIZE); + + storage_remove(strg); + opts.driver->destroy(strg); + + if (err < 0) { + exit(EXIT_FAILURE); + } return EXIT_SUCCESS; } diff --git a/storage/flashdrv/include/flashdrv/cfi.h b/storage/flashdrv/include/flashdrv/cfi.h new file mode 100644 index 000000000..6e67b7da4 --- /dev/null +++ b/storage/flashdrv/include/flashdrv/cfi.h @@ -0,0 +1,64 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * CFI + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FLASHDRV_CFI_H_ +#define _FLASHDRV_CFI_H_ + + +#include + +/* Timeouts in us */ +#define CFI_TIMEOUT_MAX_PROGRAM(typical, maximum) ((1u << (typical)) * (1u << (maximum))) +#define CFI_TIMEOUT_MAX_ERASE(typical, maximum) ((1u << (typical)) * (1u << (maximum)) * 1024u) + +#define CFI_SIZE(size) (1u << ((uint32_t)(size))) +#define CFI_REGION_SIZE(size) ((size) * 256u) + + +typedef struct { + uint8_t wordProgram; + uint8_t bufWrite; + uint8_t blkErase; + uint8_t chipErase; +} __attribute__((packed)) cfi_timeout_t; + + +typedef struct { + uint8_t vendorData[0x10]; + uint8_t qry[3]; + uint16_t cmdSet1; + uint16_t addrExt1; + uint16_t cmdSet2; + uint16_t addrExt2; + struct { + uint8_t vccMin; + uint8_t vccMax; + uint8_t vppMin; + uint8_t vppMax; + } __attribute__((packed)) voltages; + cfi_timeout_t toutTypical; + cfi_timeout_t toutMax; + uint8_t chipSz; + uint16_t fdiDesc; + uint16_t bufSz; + uint8_t regionCnt; + struct { + uint16_t count; + uint16_t size; + } __attribute__((packed)) regions[4]; +} __attribute__((packed)) cfi_info_t; + + +#endif diff --git a/storage/flashdrv/include/flashdrv/common.h b/storage/flashdrv/include/flashdrv/common.h new file mode 100644 index 000000000..00cb4e2bb --- /dev/null +++ b/storage/flashdrv/include/flashdrv/common.h @@ -0,0 +1,30 @@ +/* + * Phoenix-RTOS + * + * GRLIB FTMCTRL Flash driver + * + * Common auxiliary functions + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FLASHDRV_COMMON_H_ +#define _FLASHDRV_COMMON_H_ + + +#include +#include + + +off_t common_getSectorOffset(size_t sectorsz, off_t offs); + + +bool common_isValidAddress(size_t memsz, off_t offs, size_t len); + + +#endif diff --git a/storage/flashdrv/include/flashdrv/flash_interface.h b/storage/flashdrv/include/flashdrv/flash_interface.h new file mode 100644 index 000000000..bb424d460 --- /dev/null +++ b/storage/flashdrv/include/flashdrv/flash_interface.h @@ -0,0 +1,28 @@ +/* + * Phoenix-RTOS + * + * Flash driver interface + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FLASHDRV_FLASH_INTERFACE_H_ +#define _FLASHDRV_FLASH_INTERFACE_H_ + + +#include + + +struct flash_driver { + const char *name; + storage_t *(*init)(addr_t mctrlBase, addr_t flashBase); + void (*destroy)(storage_t *strg); +}; + + +#endif diff --git a/storage/flashdrv/include/flashdrv/flashsrv.h b/storage/flashdrv/include/flashdrv/flashsrv.h new file mode 100644 index 000000000..4f709c416 --- /dev/null +++ b/storage/flashdrv/include/flashdrv/flashsrv.h @@ -0,0 +1,30 @@ +/* + * Phoenix-RTOS + * + * Flash server + * + * Copyright 2025 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _FLASHDRV_FLASHSRV_H_ +#define _FLASHDRV_FLASHSRV_H_ + +#include +#include + +/* clang-format off */ +#define LOG(fmt, ...) do { (void)fprintf(stdout, "flashsrv: " fmt "\n", ##__VA_ARGS__); } while (0) +#define LOG_ERROR(fmt, ...) do { (void)fprintf(stdout, "flashsrv:%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); } while (0) +#define TRACE(fmt, ...) do { if (0) { (void)fprintf(stdout, "flashsrv:%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); } } while (0) +/* clang-format on */ + + +void flashsrv_register(const struct flash_driver *driver); + + +#endif diff --git a/storage/gr712rc-flash/Makefile b/storage/gr712rc-flash/Makefile deleted file mode 100644 index 521f3adbd..000000000 --- a/storage/gr712rc-flash/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# -# Makefile for Phoenix-RTOS gr712rc-flash driver -# -# Copyright 2023 Phoenix Systems -# - -NAME := libflashdrv-gr712rc -LOCAL_SRCS := flashdrv.c flash.c - -include $(static-lib.mk) - -NAME := gr712rc-flash -LOCAL_SRCS := flashsrv.c -DEP_LIBS := libflashdrv-gr712rc -LIBS := libjffs2 libstorage libmtd libptable - -include $(binary.mk) diff --git a/storage/gr712rc-flash/cmds.h b/storage/gr712rc-flash/cmds.h deleted file mode 100644 index 0f269ea89..000000000 --- a/storage/gr712rc-flash/cmds.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Phoenix-RTOS - * - * GR712RC Flash driver - * - * 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/storage/gr712rc-flash/flash.c b/storage/gr712rc-flash/flash.c deleted file mode 100644 index 23dd9ea9a..000000000 --- a/storage/gr712rc-flash/flash.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include "cmds.h" -#include "flash.h" -#include "flashdrv.h" -#include "ftmctrl.h" - -/* Physical addresses */ -#define FTMCTRL_BASE 0x80000000 -#define FLASH_BASE 0x0 - -/* 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 uint8_t id; - const char *name; -} flash_vendors[] = { - { .id = 0x89, .name = "Intel" } -}; - - -static struct { - volatile uint8_t *base; -} flash_common; - - -static uint8_t flash_readStatusCmd(void) -{ - *flash_common.base = FLASH_RD_STATUS; - - return *flash_common.base; -} - - -static uint8_t flash_readStatusDirect(void) -{ - return *flash_common.base; -} - - -static void flash_clearStatus(void) -{ - *flash_common.base = FLASH_CLR_STATUS; -} - - -static int flash_statusCheck(uint8_t status, const char *msg) -{ - int ret = ((status & STS_FULL_CHECK) == 0) ? EOK : -EIO; - - if (ret < 0) { - LOG_ERROR("%s error: status 0x%x", msg, status); - } - - flash_clearStatus(); - - return ret; -} - -/* Timeout in us */ -static int flash_statusWait(uint8_t (*readStatus)(void), time_t timeout) -{ - time_t start; - (void)gettime(&start, NULL); - - while ((readStatus() & STS_WSM_READY) == 0) { - if (timeout > 0u) { - time_t now; - (void)gettime(&now, NULL); - if ((now - start) > timeout) { - return -ETIME; - } - } - } - - return EOK; -} - - -static int flash_clearLockBits(void) -{ - *flash_common.base = FLASH_UNLOCK_CYC1; - *flash_common.base = FLASH_UNLOCK_CYC2; - - (void)flash_statusWait(flash_readStatusDirect, 0u); - - return flash_statusCheck(flash_readStatusCmd(), "clear lock bits"); -} - - -static uint16_t flash_deserialize16(uint16_t value) -{ - return ((value & 0xff) << 8) | ((value >> 8) & 0xff); -} - - -int flash_writeBuffer(const struct _storage_devCtx_t *ctx, off_t offs, const uint8_t *data, uint8_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) { - if (data[i] != 0xff) { - break; - } - } - - if (i == len) { - return EOK; - } - - uint8_t xsr; - do { - *(flash_common.base + offs) = FLASH_WR_BUF; - xsr = *flash_common.base; - } while ((xsr & XSR_WRBUF_RDY) == 0); - - *(flash_common.base + offs) = len - 1; - - for (i = 0; i < len; ++i) { - *(flash_common.base + offs + i) = data[i]; - } - - *(flash_common.base + offs) = FLASH_WR_CONFIRM; - - if (flash_statusWait(flash_readStatusDirect, - CFI_TIMEOUT_MAX_PROGRAM(ctx->cfi.toutTypical.bufWrite, ctx->cfi.toutMax.bufWrite)) < 0) { - return -ETIME; - } - - return flash_statusCheck(flash_readStatusDirect(), "write buffer"); -} - - -int flash_blockErase(off_t blkAddr, time_t timeout) -{ - *(flash_common.base + blkAddr) = FLASH_BE_CYC1; - *(flash_common.base + blkAddr) = FLASH_WR_CONFIRM; - - if (flash_statusWait(flash_readStatusDirect, timeout) < 0) { - return -ETIME; - } - - return flash_statusCheck(flash_readStatusDirect(), "block erase"); -} - - -void flash_read(const struct _storage_devCtx_t *ctx, off_t offs, void *buff, size_t len) -{ - *flash_common.base = FLASH_RD_ARRAY; - - memcpy(buff, (void *)(flash_common.base + offs), len); -} - - -void flash_printInfo(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"; - } - - LOG("configured %s %u MB flash", vendor, CFI_SIZE(cfi->chipSz) / (1024 * 1024)); -} - - -static void flash_query(flash_cfi_t *cfi) -{ - size_t i; - uint8_t *ptr = (uint8_t *)cfi; - - *flash_common.base = FLASH_RD_QUERY; - for (i = 0; i < sizeof(flash_cfi_t); ++i) { - ptr[i] = *(flash_common.base + i * 2); - } - - 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); - } -} - - -int flash_init(struct _storage_devCtx_t *ctx) -{ - /* Map one page on flash as uncached to be able to read status */ - flash_common.base = mmap( - NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, FLASH_BASE); - if (flash_common.base == MAP_FAILED) { - LOG_ERROR("failed to map flash"); - return -ENOMEM; - } - - flash_clearLockBits(); - - /* Reset */ - *flash_common.base = FLASH_RD_ARRAY; - - flash_query(&ctx->cfi); - - ctx->blockSz = CFI_SIZE(ctx->cfi.chipSz) / (ctx->cfi.regions[0].count + 1); - - (void)munmap((void *)flash_common.base, _PAGE_SIZE); - flash_common.base = mmap( - NULL, CFI_SIZE(ctx->cfi.chipSz), PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, FLASH_BASE); - if (flash_common.base == MAP_FAILED) { - LOG_ERROR("failed to map flash"); - return -ENOMEM; - } - - return EOK; -} diff --git a/storage/gr712rc-flash/flash.h b/storage/gr712rc-flash/flash.h deleted file mode 100644 index 299079820..000000000 --- a/storage/gr712rc-flash/flash.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Phoenix-RTOS - * - * GR712RC Flash driver - * - * Internal flash functions - * - * Copyright 2023 Phoenix Systems - * Author: Lukasz Leczkowski - * - * This file is part of Phoenix-RTOS. - * - * %LICENSE% - */ - -#ifndef _FLASH_H_ -#define _FLASH_H_ - - -#include - -#include "flashdrv.h" - -/* Timeouts in us */ -#define CFI_TIMEOUT_MAX_PROGRAM(typical, maximum) ((1u << typical) * (1u << maximum)) -#define CFI_TIMEOUT_MAX_ERASE(typical, maximum) ((1u << typical) * (1u << maximum) * 1024u) - - -int flash_writeBuffer(const struct _storage_devCtx_t *ctx, off_t offs, const uint8_t *data, uint8_t len); - -/* Timeout in us */ -int flash_blockErase(off_t blkAddr, time_t timeout); - - -void flash_read(const struct _storage_devCtx_t *ctx, off_t offs, void *buff, size_t len); - - -void flash_printInfo(flash_cfi_t *ctx); - - -int flash_init(struct _storage_devCtx_t *ctx); - - -#endif diff --git a/storage/gr712rc-flash/flashdrv.c b/storage/gr712rc-flash/flashdrv.c deleted file mode 100644 index 0c6eb7750..000000000 --- a/storage/gr712rc-flash/flashdrv.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include "flashdrv.h" -#include "flash.h" -#include "ftmctrl.h" - - -/* Helper functions */ - - -static off_t fdrv_getBlockAddress(const struct _storage_devCtx_t *ctx, off_t offs) -{ - return offs & ~(ctx->blockSz - 1); -} - - -static int fdrv_isValidAddress(size_t memsz, off_t offs, size_t len) -{ - if ((offs < memsz) && ((offs + len) <= memsz)) { - return 1; - } - - return 0; -} - - -/* MTD interface */ - - -static int flashdrv_mtdRead(storage_t *strg, off_t offs, void *buff, size_t len, size_t *retlen) -{ - if ((strg == NULL) || (strg->dev == NULL) || (strg->dev->ctx == NULL)) { - *retlen = 0; - return -EINVAL; - } - - struct _storage_devCtx_t *ctx = strg->dev->ctx; - if (fdrv_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len) == 0) { - *retlen = 0; - return -EINVAL; - } - - if (len == 0u) { - *retlen = 0; - return EOK; - } - - mutexLock(ctx->lock); - - ftmctrl_WrEn(ctx->ftmctrl); - flash_read(ctx, offs, buff, len); - ftmctrl_WrDis(ctx->ftmctrl); - - mutexUnlock(ctx->lock); - - *retlen = len; - - return EOK; -} - - -static int flashdrv_mtdWrite(storage_t *strg, off_t offs, const void *buff, size_t len, size_t *retlen) -{ - if ((strg == NULL) || (strg->dev == NULL) || (strg->dev->ctx == NULL)) { - *retlen = 0; - return -EINVAL; - } - - struct _storage_devCtx_t *ctx = strg->dev->ctx; - if (fdrv_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len) == 0) { - *retlen = 0; - return -EINVAL; - } - - if (len == 0u) { - *retlen = 0; - return EOK; - } - - const uint8_t *src = buff; - size_t doneBytes = 0; - - mutexLock(ctx->lock); - - while (doneBytes < len) { - size_t chunk = min(CFI_SIZE(ctx->cfi.bufSz), len - doneBytes); - ftmctrl_WrEn(ctx->ftmctrl); - flash_writeBuffer(ctx, offs, src, chunk); - ftmctrl_WrDis(ctx->ftmctrl); - - doneBytes += chunk; - src += chunk; - offs += chunk; - } - - mutexUnlock(ctx->lock); - *retlen = doneBytes; - - return EOK; -} - - -static int flashdrv_mtdErase(storage_t *strg, off_t offs, size_t len) -{ - if ((strg == NULL) || (strg->dev == NULL) || (strg->dev->ctx == NULL)) { - return -EINVAL; - } - - struct _storage_devCtx_t *ctx = strg->dev->ctx; - if (fdrv_isValidAddress(CFI_SIZE(ctx->cfi.chipSz), offs, len) == 0) { - return -EINVAL; - } - - if (len == 0u) { - return EOK; - } - - off_t end; - - mutexLock(ctx->lock); - - if (len == (size_t)-1) { - /* Erase entire memory */ - offs = 0; - end = CFI_SIZE(ctx->cfi.chipSz); - TRACE("erasing entire memory"); - } - else { - /* Erase sectors or blocks */ - offs = fdrv_getBlockAddress(ctx, offs); - end = fdrv_getBlockAddress(ctx, offs + len + ctx->blockSz - 1u); - TRACE("erasing blocks from 0x%llx to 0x%llx", offs, end); - } - - ftmctrl_WrEn(ctx->ftmctrl); - - while (offs < end) { - int res; - res = flash_blockErase(offs, CFI_TIMEOUT_MAX_ERASE(ctx->cfi.toutTypical.blkErase, ctx->cfi.toutMax.blkErase)); - if (res < 0) { - ftmctrl_WrDis(ctx->ftmctrl); - mutexUnlock(ctx->lock); - return res; - } - offs += ctx->blockSz; - } - - ftmctrl_WrDis(ctx->ftmctrl); - mutexUnlock(ctx->lock); - - return EOK; -} - - -static const storage_mtdops_t mtdOps = { - .erase = flashdrv_mtdErase, - .unPoint = NULL, - .point = NULL, - .read = flashdrv_mtdRead, - .write = flashdrv_mtdWrite, - - .meta_read = NULL, - .meta_write = NULL, - - .sync = NULL, - .lock = NULL, - .unLock = NULL, - .isLocked = NULL, - - .block_isBad = NULL, - .block_isReserved = NULL, - .block_markBad = NULL, - .block_maxBadNb = NULL, - - .suspend = NULL, - .resume = NULL, - .reboot = NULL, -}; - - -const storage_mtdops_t *flashdrv_getMtdOps(void) -{ - return &mtdOps; -} - - -struct _storage_devCtx_t *flashdrv_contextInit(void) -{ - struct _storage_devCtx_t *ctx = malloc(sizeof(struct _storage_devCtx_t)); - if (ctx == NULL) { - return NULL; - } - ctx->ftmctrl = mmap(NULL, _PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_DEVICE | MAP_PHYSMEM | MAP_ANONYMOUS, -1, FTMCTRL_BASE); - if (ctx->ftmctrl == MAP_FAILED) { - free(ctx); - return NULL; - } - - ftmctrl_WrEn(ctx->ftmctrl); - int res = flash_init(ctx); - ftmctrl_WrDis(ctx->ftmctrl); - if (res < 0) { - (void)munmap(ctx->ftmctrl, _PAGE_SIZE); - free(ctx); - return NULL; - } - - if (mutexCreate(&ctx->lock) < 0) { - (void)munmap(ctx->ftmctrl, _PAGE_SIZE); - free(ctx); - return NULL; - } - - flash_printInfo(&ctx->cfi); - - return ctx; -} - - -void flashdrv_contextDestroy(struct _storage_devCtx_t *ctx) -{ - (void)resourceDestroy(ctx->lock); - (void)munmap(ctx->ftmctrl, _PAGE_SIZE); - free(ctx); -} diff --git a/storage/gr712rc-flash/flashdrv.h b/storage/gr712rc-flash/flashdrv.h deleted file mode 100644 index 9f359b1f4..000000000 --- a/storage/gr712rc-flash/flashdrv.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Phoenix-RTOS - * - * GR712RC Flash driver - * - * Copyright 2023 Phoenix Systems - * Author: Lukasz Leczkowski - * - * This file is part of Phoenix-RTOS. - * - * %LICENSE% - */ - -#ifndef _GR712RC_FLASH_H_ -#define _GR712RC_FLASH_H_ - -#include -#include -#include - -/* clang-format off */ -#define LOG(fmt, ...) do { (void)fprintf(stdout, "gr712rc-flashsrv: " fmt "\n", ##__VA_ARGS__); } while (0) -#define LOG_ERROR(fmt, ...) do { (void)fprintf(stderr, "gr712rc-flashsrv:%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); } while (0) -#define TRACE(fmt, ...) do { if (0) { (void)fprintf(stdout, "gr712rc-flashsrv:%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); } } while (0) -/* clang-format on */ - -#define CFI_SIZE(size) (1u << (uint32_t)size) - - -typedef struct { - uint8_t wordProgram; - uint8_t bufWrite; - uint8_t blkErase; - uint8_t chipErase; -} __attribute__((packed)) flash_cfi_timeout_t; - - -typedef struct _flash_cfi_t { - uint8_t vendorData[0x10]; - uint8_t qry[3]; - uint16_t cmdSet1; - uint16_t addrExt1; - uint16_t cmdSet2; - uint16_t addrExt2; - struct { - uint8_t vccMin; - uint8_t vccMax; - uint8_t vppMin; - uint8_t vppMax; - } __attribute__((packed)) voltages; - flash_cfi_timeout_t toutTypical; - flash_cfi_timeout_t toutMax; - uint8_t chipSz; - uint16_t fdiDesc; - uint16_t bufSz; - uint8_t regionCnt; - struct { - uint16_t count; - uint16_t size; - } __attribute__((packed)) regions[4]; -} __attribute__((packed)) flash_cfi_t; - - -struct _storage_devCtx_t { - flash_cfi_t cfi; - - handle_t lock; - void *ftmctrl; - size_t blockSz; -}; - - -const storage_mtdops_t *flashdrv_getMtdOps(void); - - -struct _storage_devCtx_t *flashdrv_contextInit(void); - - -void flashdrv_contextDestroy(struct _storage_devCtx_t *ctx); - - -#endif diff --git a/tty/grlib-uart/grlib-uart.c b/tty/grlib-uart/grlib-uart.c index 41cca4737..993cbd09d 100644 --- a/tty/grlib-uart/grlib-uart.c +++ b/tty/grlib-uart/grlib-uart.c @@ -331,6 +331,32 @@ static int uart_cguInit(unsigned int n) ctl.task.cguctrl.cgu = cgu_primary; ctl.task.cguctrl.cgudev = cguinfo[n]; + return platformctl(&ctl); +#elif defined(__CPU_GR740) + static const unsigned int cguinfo[] = { + cgudev_apbuart0, + cgudev_apbuart1 + }; + platformctl_t ctl = { + .action = pctl_get, + .type = pctl_cguctrl, + .task.cguctrl.cgudev = cguinfo[n] + }; + + if (platformctl(&ctl) < 0) { + return -1; + } + + if (ctl.task.cguctrl.v.stateVal == 1) { + return 0; + } + + ctl.action = pctl_set; + ctl.type = pctl_cguctrl; + + ctl.task.cguctrl.v.state = enable; + ctl.task.cguctrl.cgudev = cguinfo[n]; + return platformctl(&ctl); #else return 0; @@ -338,10 +364,9 @@ static int uart_cguInit(unsigned int n) } -static int uart_init(unsigned int n, speed_t baud, int raw) +static int uart_iomuxInit(unsigned int n) { - libtty_callbacks_t callbacks; - uart_t *uart = &uart_common.uart; +#if defined(__CPU_GR716) platformctl_t ctl = { .action = pctl_set, .type = pctl_iomux, @@ -359,10 +384,40 @@ static int uart_init(unsigned int n, speed_t baud, int raw) ctl.task.iocfg.pin = info[n].txPin; + return platformctl(&ctl); +#elif defined(__CPU_GR740) + platformctl_t ctl = { + .action = pctl_set, + .type = pctl_iomux, + .task.iocfg = { + .opt = iomux_alternateio, + .pin = info[n].rxPin, + .pullup = 0, + .pulldn = 0, + } + }; + if (platformctl(&ctl) < 0) { return -1; } + ctl.task.iocfg.pin = info[n].txPin; + + return platformctl(&ctl); +#else + return 0; +#endif +} + + +static int uart_init(unsigned int n, speed_t baud, int raw) +{ + uart_t *uart = &uart_common.uart; + + if (uart_iomuxInit(n) < 0) { + return -1; + } + if (uart_cguInit(n) < 0) { return -1; } @@ -370,11 +425,12 @@ static int uart_init(unsigned int n, speed_t baud, int raw) /* Get info from AMBA PnP about APBUART */ unsigned int instance = n; ambapp_dev_t dev = { .devId = CORE_ID_APBUART }; - - ctl.action = pctl_get; - ctl.type = pctl_ambapp; - ctl.task.ambapp.dev = &dev; - ctl.task.ambapp.instance = &instance; + platformctl_t ctl = { + .action = pctl_get, + .type = pctl_ambapp, + .task.ambapp.dev = &dev, + .task.ambapp.instance = &instance + }; if (platformctl(&ctl) < 0) { return -1; @@ -393,10 +449,12 @@ static int uart_init(unsigned int n, speed_t baud, int raw) return -1; } - callbacks.arg = uart; - callbacks.set_cflag = uart_setCFlag; - callbacks.set_baudrate = uart_setBaudrate; - callbacks.signal_txready = uart_signalTXReady; + libtty_callbacks_t callbacks = { + .arg = uart, + .set_cflag = uart_setCFlag, + .set_baudrate = uart_setBaudrate, + .signal_txready = uart_signalTXReady, + }; if (libtty_init(&uart->tty, &callbacks, _PAGE_SIZE, baud) < 0) { munmap((void *)uart->base, _PAGE_SIZE);