diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h index 450587c69a..170960cf8c 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h @@ -2,6 +2,8 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#define MG_OTA MG_OTA_EXTERNAL_FLASH +#define MG_DEVICE MG_DEVICE_RT1020_QUAD_NOR #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c index 3fd164e515..f3a1833808 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c @@ -22,6 +22,7 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); } } + static void timer_fn(void *arg) { gpio_toggle(LED); // Blink LED struct mg_tcpip_if *ifp = arg; // And show @@ -32,15 +33,42 @@ static void timer_fn(void *arg) { } int main(void) { - gpio_output(LED); // Setup blue LED + gpio_output(LED); // Setup green LED uart_init(UART_DEBUG, 115200); // Initialise debug printf ethernet_init(); // Initialise ethernet pins + + #ifdef MQTT_DASHBOARD + // User can customise the MQTT url, device ID or the root topic below + #define DEVICE_ID "RT1060" + g_url = MQTT_SERVER_URL; + g_device_id = DEVICE_ID; + g_root_topic = DEFAULT_ROOT_TOPIC; + #endif + MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000)); struct mg_mgr mgr; // Initialise mg_mgr_init(&mgr); // Mongoose event manager mg_log_set(MG_LL_DEBUG); // Set log level + //mg_ota_boot(); // Call bootloader: continue to load, or boot another FW + +#if MG_OTA == MG_OTA_FLASH + // Demonstrate the use of mg_flash_{load/save} functions for keeping device + // configuration data on flash. Increment boot count on every boot. + struct deviceconfig { + uint32_t boot_count; + char some_other_data[40]; + }; + uint32_t key = 0x12345678; // A unique key, one per data type + struct deviceconfig dc = {}; // Initialise to some default values + mg_flash_load(NULL, key, &dc, sizeof(dc)); // Load from flash + dc.boot_count++; // Increment boot count + mg_flash_save(NULL, key, &dc, sizeof(dc)); // And save back + MG_INFO(("Boot count: %u", dc.boot_count)); +#endif + + // Initialise Mongoose network stack // Initialise Mongoose network stack struct mg_tcpip_driver_imxrt_data driver_data = {.mdc_cr = 24, .phy_addr = 2}; struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(), @@ -65,6 +93,6 @@ int main(void) { for (;;) { mg_mgr_poll(&mgr, 0); } - return 0; } + diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h index 450587c69a..b7584012ab 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h @@ -2,6 +2,8 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#define MG_OTA MG_OTA_EXTERNAL_FLASH +#define MG_DEVICE MG_DEVICE_RT1060_QUAD_NOR #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c index fe0e6f06e7..39be7659ac 120000 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.c @@ -1 +1 @@ -../../device-dashboard/net.c \ No newline at end of file +../../mqtt-dashboard/device/net.c \ No newline at end of file diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h index 9de896ef4e..cb6bd99fed 120000 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/net.h @@ -1 +1 @@ -../../device-dashboard/net.h \ No newline at end of file +../../mqtt-dashboard/device/net.h \ No newline at end of file diff --git a/mongoose.c b/mongoose.c index 997118b654..cd591dba73 100644 --- a/mongoose.c +++ b/mongoose.c @@ -323,6 +323,514 @@ bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) { } #endif +#ifdef MG_ENABLE_LINES +#line 1 "src/device_rt1020_quad_nor.c" +#endif + + + +#if MG_DEVICE == MG_DEVICE_RT1020_QUAD_NOR + +#define BIT(x) (1UL << (x)) + +#define FLEXSPI_LUT_OPERAND0(x) (((uint32_t)(((uint32_t)(x)))) & 0xFFU) +#define FLEXSPI_LUT_NUM_PADS0(x) (((uint32_t)(((uint32_t)(x)) << 8U)) & 0x300U) +#define FLEXSPI_LUT_OPCODE0(x) (((uint32_t)(((uint32_t)(x)) << 10U)) & 0xFC00U) +#define FLEXSPI_LUT_OPERAND1(x) (((uint32_t)(((uint32_t)(x)) << 16U)) & 0xFF0000U) +#define FLEXSPI_LUT_NUM_PADS1(x) (((uint32_t)(((uint32_t)(x)) << 24U)) & 0x3000000U) +#define FLEXSPI_LUT_OPCODE1(x) (((uint32_t)(((uint32_t)(x)) << 26U)) & 0xFC000000U) + +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define __FLEXSPI_QSPI_LUT { \ + [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), \ + [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04),\ + [4 * 1 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x04),\ + [4 * 3 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0x0),\ + [4 * 5 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x20, RADDR_SDR, FLEXSPI_1PAD, 0x18),\ + [4 * 8 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xD8, RADDR_SDR, FLEXSPI_1PAD, 0x18),\ + [4 * 9 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x02, RADDR_SDR, FLEXSPI_1PAD, 0x18),\ + [4 * 9 + 1] = FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0x04, STOP, FLEXSPI_1PAD, 0x0),\ + [4 * 11 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x60, STOP, FLEXSPI_1PAD, 0x0),\ +} + +typedef struct _serial_nor_config_option +{ + union + { + struct + { + uint32_t max_freq : 4; //!< Maximum supported Frequency + uint32_t misc_mode : 4; //!< miscellaneous mode + uint32_t quad_mode_setting : 4; //!< Quad mode setting + uint32_t cmd_pads : 4; //!< Command pads + uint32_t query_pads : 4; //!< SFDP read pads + uint32_t device_type : 4; //!< Device type + uint32_t option_size : 4; //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4 + uint32_t tag : 4; //!< Tag, must be 0x0E + } B; + uint32_t U; + } option0; + union + { + struct + { + uint32_t dummy_cycles : 8; //!< Dummy cycles before read + uint32_t reserved0 : 8; //!< Reserved for future use + uint32_t pinmux_group : 4; //!< The pinmux group selection + uint32_t reserved1 : 8; //!< Reserved for future use + uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A + } B; + uint32_t U; + } option1; +} serial_nor_config_option_t; + +typedef int status_t; + +typedef enum _FlexSPIOperationType +{ + kFlexSpiOperation_Command, //!< FlexSPI operation: Only command, both TX and + //! RX buffer are ignored. + kFlexSpiOperation_Config, //!< FlexSPI operation: Configure device mode, the + //! TX FIFO size is fixed in LUT. + kFlexSpiOperation_Write, //!< FlexSPI operation: Write, only TX buffer is + //! effective + kFlexSpiOperation_Read, //!< FlexSPI operation: Read, only Rx Buffer is + kFlexSpiOperation_End = kFlexSpiOperation_Read, +} flexspi_operation_t; + +typedef struct _FlexSpiXfer +{ + flexspi_operation_t operation; //!< FlexSPI operation + uint32_t baseAddress; //!< FlexSPI operation base address + uint32_t seqId; //!< Sequence Id + uint32_t seqNum; //!< Sequence Number + bool isParallelModeEnable; //!< Is a parallel transfer + uint32_t *txBuffer; //!< Tx buffer + uint32_t txSize; //!< Tx size in bytes + uint32_t *rxBuffer; //!< Rx buffer + uint32_t rxSize; //!< Rx size in bytes +} flexspi_xfer_t; + +typedef struct +{ + uint32_t version; + status_t (*init)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t +dst_addr, const uint32_t *src); + uint32_t reserved; + status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, +uint32_t lengthInBytes); + uint32_t reserved2; + status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t +*lutBase, uint32_t seqNumber); +status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer); +void (*clear_cache)(uint32_t instance); +} flexspi_nor_driver_interface_t; + +typedef struct +{ + const uint32_t version; //!< Bootloader version number + const char *copyright; //!< Bootloader Copyright + void (*runBootloader)(void *arg); //!< Function to start the bootloader executing + const uint32_t *reserved0; //!< Reserved + const flexspi_nor_driver_interface_t *flexSpiNorDriver; //!< FlexSPI NOR Flash API + const uint32_t *reserved1[2]; //!< Reserved + /*const rtwdog_driver_interface_t *rtwdogDriver; + const wdog_driver_interface_t *wdogDriver; + const uint32_t *reserved2;*/ +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t**)(0x0020001c)) +#define flexspi_nor (*(flexspi_nor_driver_interface_t**) ((char*) bootloader + 4*sizeof(uint32_t))) + +static flexspi_nor_config_t s_config = { + .memConfig = {.tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad + .csHoldTime = 3, + .csSetupTime = 3, + .controllerMiscOption = MG_BIT(4), + .deviceType = 1, // serial NOR + .sflashPadType = 4, + .serialClkFreq = 7, // 133MHz + .sflashA1Size = 8 * 1024 * 1024, + .lookupTable = __FLEXSPI_QSPI_LUT}, + .pageSize = 256, + .sectorSize = 4 * 1024, + .ipcmdSerialClkFreq = 1, + .blockSize = 64 * 1024, + .isUniformBlockSize = false}; +static uint32_t s_instance; + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 256 * 1024; // 256k +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +uint32_t mg_flash_init() { + uint32_t status = flexspi_nor->init(s_instance, &s_config);; + return status; +} + +bool mg_flash_erase(void *addr) { + bool ok = false; + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + } else { + uint32_t status = flexspi_nor->erase(s_instance, &s_config, (uint32_t) addr, mg_flash_sector_size()); + if (status == 0) ok = true; + } + if (ok) { + MG_DEBUG(("Successfully erased sector starting at %p", addr)); + } else { + MG_DEBUG(("Failed to erase sector starting at %p", addr)); + } + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +bool mg_flash_write(void *addr, const void *buf, size_t len) { + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = flexspi_nor->program(s_instance, &s_config, (uint32_t) dst, src); + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) ok = false; + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, + ok ? "ok" : "fail")); + return ok; +} + +void mg_device_reset(void) { + MG_DEBUG(("Resetting device...")); + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} +#endif + +#ifdef MG_ENABLE_LINES +#line 1 "src/device_rt1060_quad_nor.c" +#endif + + + +#if MG_DEVICE == MG_DEVICE_RT1060_QUAD_NOR + +typedef struct { + char memCfg[448]; + uint32_t pageSize; + uint32_t sectorSize; + uint32_t ipCmdSerialClkFreq; + char reserved[55]; +} flexspi_nor_config_t; + +typedef struct _serial_nor_config_option +{ + union + { + struct + { + uint32_t max_freq : 4; //!< Maximum supported Frequency + uint32_t misc_mode : 4; //!< miscellaneous mode + uint32_t quad_mode_setting : 4; //!< Quad mode setting + uint32_t cmd_pads : 4; //!< Command pads + uint32_t query_pads : 4; //!< SFDP read pads + uint32_t device_type : 4; //!< Device type + uint32_t option_size : 4; //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4 + uint32_t tag : 4; //!< Tag, must be 0x0E + } B; + uint32_t U; + } option0; + union + { + struct + { + uint32_t dummy_cycles : 8; //!< Dummy cycles before read + uint32_t reserved0 : 8; //!< Reserved for future use + uint32_t pinmux_group : 4; //!< The pinmux group selection + uint32_t reserved1 : 8; //!< Reserved for future use + uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A + } B; + uint32_t U; + } option1; +} serial_nor_config_option_t; + +typedef int status_t; + +typedef enum _FlexSPIOperationType +{ + kFlexSpiOperation_Command, //!< FlexSPI operation: Only command, both TX and + //! RX buffer are ignored. + kFlexSpiOperation_Config, //!< FlexSPI operation: Configure device mode, the + //! TX FIFO size is fixed in LUT. + kFlexSpiOperation_Write, //!< FlexSPI operation: Write, only TX buffer is + //! effective + kFlexSpiOperation_Read, //!< FlexSPI operation: Read, only Rx Buffer is + kFlexSpiOperation_End = kFlexSpiOperation_Read, +} flexspi_operation_t; + +typedef struct _FlexSpiXfer +{ + flexspi_operation_t operation; //!< FlexSPI operation + uint32_t baseAddress; //!< FlexSPI operation base address + uint32_t seqId; //!< Sequence Id + uint32_t seqNum; //!< Sequence Number + bool isParallelModeEnable; //!< Is a parallel transfer + uint32_t *txBuffer; //!< Tx buffer + uint32_t txSize; //!< Tx size in bytes + uint32_t *rxBuffer; //!< Rx buffer + uint32_t rxSize; //!< Rx size in bytes +} flexspi_xfer_t; + +typedef struct +{ + uint32_t version; + status_t (*init)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t +dst_addr, const uint32_t *src); + status_t (*erase_all)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, +uint32_t lengthInBytes); + status_t (*read)( + uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t addr, +uint32_t lengthInBytes); + void (*clear_cache)(uint32_t instance); + status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer); + status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t +*lutBase, uint32_t seqNumber); + status_t (*get_config)(uint32_t instance, flexspi_nor_config_t *config, +serial_nor_config_option_t *option); +} flexspi_nor_driver_interface_t; + +typedef struct +{ + const uint32_t version; //!< Bootloader version number + const char *copyright; //!< Bootloader Copyright + void (*runBootloader)(void *arg); //!< Function to start the bootloader executing + const uint32_t *reserved0; //!< Reserved + const flexspi_nor_driver_interface_t *flexSpiNorDriver; //!< FlexSPI NOR Flash API + const uint32_t *reserved1[2]; //!< Reserved + /*const rtwdog_driver_interface_t *rtwdogDriver; + const wdog_driver_interface_t *wdogDriver; + const uint32_t *reserved2;*/ +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t**)(0x0020001c)) +#define flexspi_nor (*(flexspi_nor_driver_interface_t**) ((char*) bootloader + 4*sizeof(uint32_t))) + +static flexspi_nor_config_t s_config; +static serial_nor_config_option_t s_option; +static uint32_t s_instance; + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 256 * 1024; // 256k +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +uint32_t mg_flash_init() { + uint32_t status = 0; + s_option.option0.U = 0xc0000008; + status = flexspi_nor->get_config(s_instance, &s_config, &s_option); + if (status != 0) { + MG_DEBUG(("Failed to get flexspi configuration")); + return status; + } + + status = flexspi_nor->init(s_instance, &s_config); + return status; +} + +bool mg_flash_erase(void *addr) { + bool ok = false; + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + } else { + uint32_t status = flexspi_nor->erase(s_instance, &s_config, (uint32_t) addr, mg_flash_sector_size()); + if (status == 0) ok = true; + } + if (ok) { + MG_DEBUG(("Successfully erased sector starting at %p", addr)); + } else { + MG_DEBUG(("Failed to erase sector starting at %p", addr)); + } + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +bool mg_flash_write(void *addr, const void *buf, size_t len) { + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = flexspi_nor->program(s_instance, &s_config, (uint32_t) dst, src); + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) ok = false; + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, + ok ? "ok" : "fail")); + return ok; +} + +void mg_device_reset(void) { + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} +#endif + #ifdef MG_ENABLE_LINES #line 1 "src/device_stm32h5.c" #endif @@ -5709,6 +6217,107 @@ MG_IRAM void mg_ota_boot(void) { mg_device_reset(); } } + +#elif MG_OTA == MG_OTA_EXTERNAL_FLASH + +static char *s_addr; // Current address to write to +static size_t s_size; // Firmware size to flash. In-progress indicator +static uint32_t s_crc32; // Firmware checksum + +struct mg_otadata { + uint32_t crc32, size, timestamp, status; +}; + +bool mg_ota_begin(size_t new_firmware_size) { + bool ok = false; + if (s_size) { + MG_ERROR(("OTA already in progress. Call mg_ota_end()")); + } else { + if (new_firmware_size > mg_flash_size()) { + MG_ERROR(("Firmware exceeds flash size")); + return false; + } + s_size = new_firmware_size; + uint32_t flash_init_status = mg_flash_init(); + if (flash_init_status) { + MG_ERROR(("Error initialising the flash structure")); + return false; + } else { + ok = true; + } + } + return ok; +} + +bool mg_ota_write(const void *buf, size_t len) { + bool ok = false; + if (s_size == 0) { + MG_ERROR(("OTA is not started, call mg_ota_begin()")); + } else { + size_t align = mg_flash_write_align(); + size_t len_aligned_down = MG_ROUND_DOWN(len, align); + if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down); + if (len_aligned_down < len) { + size_t left = len - len_aligned_down; + char tmp[align]; + memset(tmp, 0xff, sizeof(tmp)); + memcpy(tmp, (char *) buf + len_aligned_down, left); + ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); + } + s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC + MG_DEBUG(("%#x %p %lu -> %d", s_addr, buf, len, ok)); + s_addr += len; + } + return ok; +} + +bool mg_ota_end(void) { + bool ok = true; + size_t size = (size_t) s_addr; + uint32_t crc32 = mg_crc32(0, (char *) 0x60000000, size); + MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, + size, s_crc32 == crc32 ? "ok" : "fail")); + s_addr = 0; + s_size = 0; + MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); + return ok; +} + +static struct mg_otadata mg_otadata(int fw) { + (void) fw; + struct mg_otadata od = {}; + return od; +} + +int mg_ota_status(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.status; +} + +uint32_t mg_ota_crc32(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.crc32; +} + +uint32_t mg_ota_timestamp(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.timestamp; +} + +size_t mg_ota_size(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.size; +} + +MG_IRAM bool mg_ota_commit(void) { + return false; +} + +bool mg_ota_rollback(void) { + MG_DEBUG(("Rolling firmware not supported yet")); + return false; +} + #endif #ifdef MG_ENABLE_LINES diff --git a/mongoose.h b/mongoose.h index e64271cf47..5cb736b8b7 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1692,6 +1692,7 @@ void mg_rpc_list(struct mg_rpc_req *r); #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_EXTERNAL_FLASH 2 // OTA via an external flash #define MG_OTA_CUSTOM 100 // Custom implementation #ifndef MG_OTA @@ -1735,6 +1736,8 @@ MG_IRAM void mg_ota_boot(void); // Bootloader function #define MG_DEVICE_NONE 0 // Dummy system #define MG_DEVICE_STM32H5 1 // STM32 H5 #define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_RT1020_QUAD_NOR 3 // IMXRT1020 QUAD SPI NOR FLASH +#define MG_DEVICE_RT1060_QUAD_NOR 4 // IMXRT1060 QUAD SPI NOR FLASH #define MG_DEVICE_CUSTOM 100 // Custom implementation #ifndef MG_DEVICE diff --git a/src/device.h b/src/device.h index 99b1064bd8..0e86b24d52 100644 --- a/src/device.h +++ b/src/device.h @@ -8,6 +8,8 @@ #define MG_DEVICE_NONE 0 // Dummy system #define MG_DEVICE_STM32H5 1 // STM32 H5 #define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_RT1020_QUAD_NOR 3 // IMXRT1020 QUAD SPI NOR FLASH +#define MG_DEVICE_RT1060_QUAD_NOR 4 // IMXRT1060 QUAD SPI NOR FLASH #define MG_DEVICE_CUSTOM 100 // Custom implementation #ifndef MG_DEVICE diff --git a/src/device_rt1020_quad_nor.c b/src/device_rt1020_quad_nor.c new file mode 100644 index 0000000000..1ad37e9c14 --- /dev/null +++ b/src/device_rt1020_quad_nor.c @@ -0,0 +1,309 @@ +#include "device.h" +#include "log.h" + +#if MG_DEVICE == MG_DEVICE_RT1020_QUAD_NOR + +#define BIT(x) (1UL << (x)) + +#define FLEXSPI_LUT_OPERAND0(x) (((uint32_t)(((uint32_t)(x)))) & 0xFFU) +#define FLEXSPI_LUT_NUM_PADS0(x) (((uint32_t)(((uint32_t)(x)) << 8U)) & 0x300U) +#define FLEXSPI_LUT_OPCODE0(x) (((uint32_t)(((uint32_t)(x)) << 10U)) & 0xFC00U) +#define FLEXSPI_LUT_OPERAND1(x) (((uint32_t)(((uint32_t)(x)) << 16U)) & 0xFF0000U) +#define FLEXSPI_LUT_NUM_PADS1(x) (((uint32_t)(((uint32_t)(x)) << 24U)) & 0x3000000U) +#define FLEXSPI_LUT_OPCODE1(x) (((uint32_t)(((uint32_t)(x)) << 26U)) & 0xFC000000U) + +typedef struct _lut_sequence +{ + uint8_t seqNum; //!< Sequence Number, valid number: 1-16 + uint8_t seqId; //!< Sequence Index, valid number: 0-15 + uint16_t reserved; +} flexspi_lut_seq_t; + +typedef struct _FlexSPIConfig +{ + uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL + uint32_t version; //!< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix + uint32_t reserved0; //!< [0x008-0x00b] Reserved for future use + uint8_t readSampleClkSrc; //!< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 + uint8_t csHoldTime; //!< [0x00d-0x00d] CS hold time, default value: 3 + uint8_t csSetupTime; //!< [0x00e-0x00e] CS setup time, default value: 3 + uint8_t columnAddressWidth; //!< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For + //! Serial NAND, need to refer to datasheet + uint8_t deviceModeCfgEnable; //!< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable + uint8_t deviceModeType; //!< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch, + //! Generic configuration, etc. + uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for + //! DPI/QPI/OPI switch or reset command + flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + //! sequence number, [31:16] Reserved + uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration + uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable + uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe + flexspi_lut_seq_t + configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq + uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use + uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands + uint32_t reserved2; //!< [0x03c-0x03f] Reserved for future use + uint32_t controllerMiscOption; //!< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for more + //! details + uint8_t deviceType; //!< [0x044-0x044] Device Type: See Flash Type Definition for more details + uint8_t sflashPadType; //!< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal + uint8_t serialClkFreq; //!< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot + //! Chapter for more details + uint8_t lutCustomSeqEnable; //!< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot + //! be done using 1 LUT sequence, currently, only applicable to HyperFLASH + uint32_t reserved3[2]; //!< [0x048-0x04f] Reserved for future use + uint32_t sflashA1Size; //!< [0x050-0x053] Size of Flash connected to A1 + uint32_t sflashA2Size; //!< [0x054-0x057] Size of Flash connected to A2 + uint32_t sflashB1Size; //!< [0x058-0x05b] Size of Flash connected to B1 + uint32_t sflashB2Size; //!< [0x05c-0x05f] Size of Flash connected to B2 + uint32_t csPadSettingOverride; //!< [0x060-0x063] CS pad setting override value + uint32_t sclkPadSettingOverride; //!< [0x064-0x067] SCK pad setting override value + uint32_t dataPadSettingOverride; //!< [0x068-0x06b] data pad setting override value + uint32_t dqsPadSettingOverride; //!< [0x06c-0x06f] DQS pad setting override value + uint32_t timeoutInMs; //!< [0x070-0x073] Timeout threshold for read status command + uint32_t commandInterval; //!< [0x074-0x077] CS deselect interval between two commands + uint16_t dataValidTime[2]; //!< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B, in terms of 0.1ns + uint16_t busyOffset; //!< [0x07c-0x07d] Busy offset, valid value: 0-31 + uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - + //! busy flag is 0 when flash device is busy + uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences + flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use +} flexspi_mem_config_t; + +typedef struct _flexspi_nor_config +{ + flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI + uint32_t pageSize; //!< Page size of Serial NOR + uint32_t sectorSize; //!< Sector size of Serial NOR + uint8_t ipcmdSerialClkFreq; //!< Clock frequency for IP command + uint8_t isUniformBlockSize; //!< Sector/Block size is the same + uint8_t reserved0[2]; //!< Reserved for future use + uint8_t serialNorType; //!< Serial NOR Flash type: 0/1/2/3 + uint8_t needExitNoCmdMode; //!< Need to exit NoCmd mode before other IP command + uint8_t halfClkForNonReadCmd; //!< Half the Serial Clock for non-read command: true/false + uint8_t needRestoreNoCmdMode; //!< Need to Restore NoCmd mode after IP commmand execution + uint32_t blockSize; //!< Block size + uint32_t reserve2[11]; //!< Reserved for future use +} flexspi_nor_config_t; + +/* FLEXSPI memory config block related defintions */ +#define FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian +#define FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 + +#define FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ + (FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \ + FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1)) + +#define CMD_SDR 0x01 +#define CMD_DDR 0x21 +#define DUMMY_SDR 0x0C +#define DUMMY_DDR 0x2C +#define RADDR_SDR 0x02 +#define RADDR_DDR 0x22 +#define READ_SDR 0x09 +#define READ_DDR 0x29 +#define WRITE_SDR 0x08 +#define WRITE_DDR 0x28 +#define STOP 0 + +#define FLEXSPI_1PAD 0 +#define FLEXSPI_2PAD 1 +#define FLEXSPI_4PAD 2 +#define FLEXSPI_8PAD 3 + +#define __FLEXSPI_QSPI_LUT { \ + [0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18), \ + [1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x04),\ + [4 * 1 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x05, READ_SDR, FLEXSPI_1PAD, 0x04),\ + [4 * 3 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x06, STOP, FLEXSPI_1PAD, 0x0),\ + [4 * 5 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x20, RADDR_SDR, FLEXSPI_1PAD, 0x18),\ + [4 * 8 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xD8, RADDR_SDR, FLEXSPI_1PAD, 0x18),\ + [4 * 9 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x02, RADDR_SDR, FLEXSPI_1PAD, 0x18),\ + [4 * 9 + 1] = FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0x04, STOP, FLEXSPI_1PAD, 0x0),\ + [4 * 11 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x60, STOP, FLEXSPI_1PAD, 0x0),\ +} + +typedef struct _serial_nor_config_option +{ + union + { + struct + { + uint32_t max_freq : 4; //!< Maximum supported Frequency + uint32_t misc_mode : 4; //!< miscellaneous mode + uint32_t quad_mode_setting : 4; //!< Quad mode setting + uint32_t cmd_pads : 4; //!< Command pads + uint32_t query_pads : 4; //!< SFDP read pads + uint32_t device_type : 4; //!< Device type + uint32_t option_size : 4; //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4 + uint32_t tag : 4; //!< Tag, must be 0x0E + } B; + uint32_t U; + } option0; + union + { + struct + { + uint32_t dummy_cycles : 8; //!< Dummy cycles before read + uint32_t reserved0 : 8; //!< Reserved for future use + uint32_t pinmux_group : 4; //!< The pinmux group selection + uint32_t reserved1 : 8; //!< Reserved for future use + uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A + } B; + uint32_t U; + } option1; +} serial_nor_config_option_t; + +typedef int status_t; + +typedef enum _FlexSPIOperationType +{ + kFlexSpiOperation_Command, //!< FlexSPI operation: Only command, both TX and + //! RX buffer are ignored. + kFlexSpiOperation_Config, //!< FlexSPI operation: Configure device mode, the + //! TX FIFO size is fixed in LUT. + kFlexSpiOperation_Write, //!< FlexSPI operation: Write, only TX buffer is + //! effective + kFlexSpiOperation_Read, //!< FlexSPI operation: Read, only Rx Buffer is + kFlexSpiOperation_End = kFlexSpiOperation_Read, +} flexspi_operation_t; + +typedef struct _FlexSpiXfer +{ + flexspi_operation_t operation; //!< FlexSPI operation + uint32_t baseAddress; //!< FlexSPI operation base address + uint32_t seqId; //!< Sequence Id + uint32_t seqNum; //!< Sequence Number + bool isParallelModeEnable; //!< Is a parallel transfer + uint32_t *txBuffer; //!< Tx buffer + uint32_t txSize; //!< Tx size in bytes + uint32_t *rxBuffer; //!< Rx buffer + uint32_t rxSize; //!< Rx size in bytes +} flexspi_xfer_t; + +typedef struct +{ + uint32_t version; + status_t (*init)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t +dst_addr, const uint32_t *src); + uint32_t reserved; + status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, +uint32_t lengthInBytes); + uint32_t reserved2; + status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t +*lutBase, uint32_t seqNumber); +status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer); +void (*clear_cache)(uint32_t instance); +} flexspi_nor_driver_interface_t; + +typedef struct +{ + const uint32_t version; //!< Bootloader version number + const char *copyright; //!< Bootloader Copyright + void (*runBootloader)(void *arg); //!< Function to start the bootloader executing + const uint32_t *reserved0; //!< Reserved + const flexspi_nor_driver_interface_t *flexSpiNorDriver; //!< FlexSPI NOR Flash API + const uint32_t *reserved1[2]; //!< Reserved + /*const rtwdog_driver_interface_t *rtwdogDriver; + const wdog_driver_interface_t *wdogDriver; + const uint32_t *reserved2;*/ +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t**)(0x0020001c)) +#define flexspi_nor (*(flexspi_nor_driver_interface_t**) ((char*) bootloader + 4*sizeof(uint32_t))) + +static flexspi_nor_config_t s_config = { + .memConfig = {.tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad + .csHoldTime = 3, + .csSetupTime = 3, + .controllerMiscOption = MG_BIT(4), + .deviceType = 1, // serial NOR + .sflashPadType = 4, + .serialClkFreq = 7, // 133MHz + .sflashA1Size = 8 * 1024 * 1024, + .lookupTable = __FLEXSPI_QSPI_LUT}, + .pageSize = 256, + .sectorSize = 4 * 1024, + .ipcmdSerialClkFreq = 1, + .blockSize = 64 * 1024, + .isUniformBlockSize = false}; +static uint32_t s_instance; + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 256 * 1024; // 256k +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +uint32_t mg_flash_init() { + uint32_t status = flexspi_nor->init(s_instance, &s_config);; + return status; +} + +bool mg_flash_erase(void *addr) { + bool ok = false; + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + } else { + uint32_t status = flexspi_nor->erase(s_instance, &s_config, (uint32_t) addr, mg_flash_sector_size()); + if (status == 0) ok = true; + } + if (ok) { + MG_DEBUG(("Successfully erased sector starting at %p", addr)); + } else { + MG_DEBUG(("Failed to erase sector starting at %p", addr)); + } + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +bool mg_flash_write(void *addr, const void *buf, size_t len) { + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = flexspi_nor->program(s_instance, &s_config, (uint32_t) dst, src); + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) ok = false; + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, + ok ? "ok" : "fail")); + return ok; +} + +void mg_device_reset(void) { + MG_DEBUG(("Resetting device...")); + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} +#endif diff --git a/src/device_rt1060_quad_nor.c b/src/device_rt1060_quad_nor.c new file mode 100644 index 0000000000..8218b7c9ea --- /dev/null +++ b/src/device_rt1060_quad_nor.c @@ -0,0 +1,191 @@ +#include "device.h" +#include "log.h" + +#if MG_DEVICE == MG_DEVICE_RT1060_QUAD_NOR + +typedef struct { + char memCfg[448]; + uint32_t pageSize; + uint32_t sectorSize; + uint32_t ipCmdSerialClkFreq; + char reserved[55]; +} flexspi_nor_config_t; + +typedef struct _serial_nor_config_option +{ + union + { + struct + { + uint32_t max_freq : 4; //!< Maximum supported Frequency + uint32_t misc_mode : 4; //!< miscellaneous mode + uint32_t quad_mode_setting : 4; //!< Quad mode setting + uint32_t cmd_pads : 4; //!< Command pads + uint32_t query_pads : 4; //!< SFDP read pads + uint32_t device_type : 4; //!< Device type + uint32_t option_size : 4; //!< Option size, in terms of uint32_t, size = (option_size + 1) * 4 + uint32_t tag : 4; //!< Tag, must be 0x0E + } B; + uint32_t U; + } option0; + union + { + struct + { + uint32_t dummy_cycles : 8; //!< Dummy cycles before read + uint32_t reserved0 : 8; //!< Reserved for future use + uint32_t pinmux_group : 4; //!< The pinmux group selection + uint32_t reserved1 : 8; //!< Reserved for future use + uint32_t flash_connection : 4; //!< Flash connection option: 0 - Single Flash connected to port A + } B; + uint32_t U; + } option1; +} serial_nor_config_option_t; + +typedef int status_t; + +typedef enum _FlexSPIOperationType +{ + kFlexSpiOperation_Command, //!< FlexSPI operation: Only command, both TX and + //! RX buffer are ignored. + kFlexSpiOperation_Config, //!< FlexSPI operation: Configure device mode, the + //! TX FIFO size is fixed in LUT. + kFlexSpiOperation_Write, //!< FlexSPI operation: Write, only TX buffer is + //! effective + kFlexSpiOperation_Read, //!< FlexSPI operation: Read, only Rx Buffer is + kFlexSpiOperation_End = kFlexSpiOperation_Read, +} flexspi_operation_t; + +typedef struct _FlexSpiXfer +{ + flexspi_operation_t operation; //!< FlexSPI operation + uint32_t baseAddress; //!< FlexSPI operation base address + uint32_t seqId; //!< Sequence Id + uint32_t seqNum; //!< Sequence Number + bool isParallelModeEnable; //!< Is a parallel transfer + uint32_t *txBuffer; //!< Tx buffer + uint32_t txSize; //!< Tx size in bytes + uint32_t *rxBuffer; //!< Rx buffer + uint32_t rxSize; //!< Rx size in bytes +} flexspi_xfer_t; + +typedef struct +{ + uint32_t version; + status_t (*init)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*program)(uint32_t instance, flexspi_nor_config_t *config, uint32_t +dst_addr, const uint32_t *src); + status_t (*erase_all)(uint32_t instance, flexspi_nor_config_t *config); + status_t (*erase)(uint32_t instance, flexspi_nor_config_t *config, uint32_t start, +uint32_t lengthInBytes); + status_t (*read)( + uint32_t instance, flexspi_nor_config_t *config, uint32_t *dst, uint32_t addr, +uint32_t lengthInBytes); + void (*clear_cache)(uint32_t instance); + status_t (*xfer)(uint32_t instance, flexspi_xfer_t *xfer); + status_t (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t +*lutBase, uint32_t seqNumber); + status_t (*get_config)(uint32_t instance, flexspi_nor_config_t *config, +serial_nor_config_option_t *option); +} flexspi_nor_driver_interface_t; + +typedef struct +{ + const uint32_t version; //!< Bootloader version number + const char *copyright; //!< Bootloader Copyright + void (*runBootloader)(void *arg); //!< Function to start the bootloader executing + const uint32_t *reserved0; //!< Reserved + const flexspi_nor_driver_interface_t *flexSpiNorDriver; //!< FlexSPI NOR Flash API + const uint32_t *reserved1[2]; //!< Reserved + /*const rtwdog_driver_interface_t *rtwdogDriver; + const wdog_driver_interface_t *wdogDriver; + const uint32_t *reserved2;*/ +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t**)(0x0020001c)) +#define flexspi_nor (*(flexspi_nor_driver_interface_t**) ((char*) bootloader + 4*sizeof(uint32_t))) + +static flexspi_nor_config_t s_config; +static serial_nor_config_option_t s_option; +static uint32_t s_instance; + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 256 * 1024; // 256k +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +uint32_t mg_flash_init() { + uint32_t status = 0; + s_option.option0.U = 0xc0000008; + status = flexspi_nor->get_config(s_instance, &s_config, &s_option); + if (status != 0) { + MG_DEBUG(("Failed to get flexspi configuration")); + return status; + } + + status = flexspi_nor->init(s_instance, &s_config); + return status; +} + +bool mg_flash_erase(void *addr) { + bool ok = false; + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + } else { + uint32_t status = flexspi_nor->erase(s_instance, &s_config, (uint32_t) addr, mg_flash_sector_size()); + if (status == 0) ok = true; + } + if (ok) { + MG_DEBUG(("Successfully erased sector starting at %p", addr)); + } else { + MG_DEBUG(("Failed to erase sector starting at %p", addr)); + } + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +bool mg_flash_write(void *addr, const void *buf, size_t len) { + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = flexspi_nor->program(s_instance, &s_config, (uint32_t) dst, src); + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) ok = false; + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, + ok ? "ok" : "fail")); + return ok; +} + +void mg_device_reset(void) { + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} +#endif diff --git a/src/ota.h b/src/ota.h index 7eae2a496a..5cadd9e6b2 100644 --- a/src/ota.h +++ b/src/ota.h @@ -7,6 +7,7 @@ #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_EXTERNAL_FLASH 2 // OTA via an external flash #define MG_OTA_CUSTOM 100 // Custom implementation #ifndef MG_OTA diff --git a/src/ota_flash.c b/src/ota_flash.c index d5fe87b05b..4084e41f70 100644 --- a/src/ota_flash.c +++ b/src/ota_flash.c @@ -192,4 +192,105 @@ MG_IRAM void mg_ota_boot(void) { mg_device_reset(); } } + +#elif MG_OTA == MG_OTA_EXTERNAL_FLASH + +static char *s_addr; // Current address to write to +static size_t s_size; // Firmware size to flash. In-progress indicator +static uint32_t s_crc32; // Firmware checksum + +struct mg_otadata { + uint32_t crc32, size, timestamp, status; +}; + +bool mg_ota_begin(size_t new_firmware_size) { + bool ok = false; + if (s_size) { + MG_ERROR(("OTA already in progress. Call mg_ota_end()")); + } else { + if (new_firmware_size > mg_flash_size()) { + MG_ERROR(("Firmware exceeds flash size")); + return false; + } + s_size = new_firmware_size; + uint32_t flash_init_status = mg_flash_init(); + if (flash_init_status) { + MG_ERROR(("Error initialising the flash structure")); + return false; + } else { + ok = true; + } + } + return ok; +} + +bool mg_ota_write(const void *buf, size_t len) { + bool ok = false; + if (s_size == 0) { + MG_ERROR(("OTA is not started, call mg_ota_begin()")); + } else { + size_t align = mg_flash_write_align(); + size_t len_aligned_down = MG_ROUND_DOWN(len, align); + if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down); + if (len_aligned_down < len) { + size_t left = len - len_aligned_down; + char tmp[align]; + memset(tmp, 0xff, sizeof(tmp)); + memcpy(tmp, (char *) buf + len_aligned_down, left); + ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); + } + s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC + MG_DEBUG(("%#x %p %lu -> %d", s_addr, buf, len, ok)); + s_addr += len; + } + return ok; +} + +bool mg_ota_end(void) { + bool ok = true; + size_t size = (size_t) s_addr; + uint32_t crc32 = mg_crc32(0, (char *) 0x60000000, size); + MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, + size, s_crc32 == crc32 ? "ok" : "fail")); + s_addr = 0; + s_size = 0; + MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); + return ok; +} + +static struct mg_otadata mg_otadata(int fw) { + (void) fw; + struct mg_otadata od = {}; + return od; +} + +int mg_ota_status(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.status; +} + +uint32_t mg_ota_crc32(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.crc32; +} + +uint32_t mg_ota_timestamp(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.timestamp; +} + +size_t mg_ota_size(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.size; +} + +MG_IRAM bool mg_ota_commit(void) { + return false; +} + +bool mg_ota_rollback(void) { + MG_DEBUG(("Rolling firmware not supported yet")); + return false; +} + #endif