Flash access library for GR-Boards.

FlashAccess.cpp

Committer:
dkato
Date:
2017-11-28
Revision:
1:652a093cf264
Parent:
0:5a74eeaefb5d

File content as of revision 1:652a093cf264:

/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
*
* Copyright (C) 2017 Renesas Electronics Corporation. All rights reserved.
*******************************************************************************/

#include "mbed.h"
#include "FlashAccess.h"

/* ---- serial flash command ---- */
#define SFLASHCMD_SECTOR_ERASE       (0x20u)    /* SE     3-byte address(1bit)             */
#define SFLASHCMD_PAGE_PROGRAM       (0x02u)    /* PP     3-byte address(1bit), data(1bit) */
#define SFLASHCMD_READ               (0x03u)    /* READ   3-byte address(1bit), data(1bit) */
#define SFLASHCMD_READ_STATUS_REG    (0x05u)    /* RDSR                         data(1bit) */
#define SFLASHCMD_WRITE_ENABLE       (0x06u)    /* WREN                                    */
/* ---- serial flash register definitions ---- */
#define STREG_BUSY_BIT               (0x01u)    /* SR.[0]BUSY Erase/Write In Progress (RO) */

/* Definition of the base address for the MMU translation table */
#if defined(__CC_ARM) || defined(__GNUC__)
extern uint32_t Image$$TTB$$ZI$$Base;
#define TTB         ((uint32_t)&Image$$TTB$$ZI$$Base)   /* using linker symbol */
#elif defined(__ICCARM__)
#pragma section="TTB"
#define TTB         ((uint32_t)__section_begin("TTB"))
#endif

/** public **/

FlashAccess::FlashAccess() : SPIBSC(&SPIBSC0) {
}

bool FlashAccess::SectorErase(uint32_t addr) {
    bool ret;
#if defined (__ICCARM__)
    int was_masked = __disable_irq_iar();
#else
    int was_masked = __disable_irq();
#endif
    spi_mode();
    ret = _SectorErase(addr);
    ex_mode();
    if (0 == was_masked) {
        __enable_irq();
    }
    return ret;
}

bool FlashAccess::PageProgram(uint32_t addr, uint8_t * buf, int32_t size) {
    bool ret;
#if defined (__ICCARM__)
    int was_masked = __disable_irq_iar();
#else
    int was_masked = __disable_irq();
#endif
    spi_mode();
    ret = _PageProgram(addr, buf, size);
    ex_mode();
    if (0 == was_masked) {
        __enable_irq();
    }
    return ret;
}

bool FlashAccess::Read(uint32_t addr, uint8_t * buf, int32_t size) {
    bool ret;
#if defined (__ICCARM__)
    int was_masked = __disable_irq_iar();
#else
    int was_masked = __disable_irq();
#endif
    spi_mode();
    ret = _Read(addr, buf, size);
    ex_mode();
    if (0 == was_masked) {
        __enable_irq();
    }
    return ret;
}

/** protected **/

bool FlashAccess::_SectorErase(uint32_t addr) {
    bool ret;

    /* ---- Write enable   ---- */
    ret = _WriteEnable();      /* WREN Command */
    if (ret == false) {
        return ret;
    }

    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- command ---- */
    spimd_reg.cde    = SPIBSC_OUTPUT_ENABLE;
    spimd_reg.cdb    = SPIBSC_1BIT;
    spimd_reg.cmd    = SFLASHCMD_SECTOR_ERASE;

    /* ---- address ---- */
    spimd_reg.ade    = SPIBSC_OUTPUT_ADDR_24;
    spimd_reg.addre  = SPIBSC_SDR_TRANS;       /* SDR */
    spimd_reg.adb    = SPIBSC_1BIT;
    spimd_reg.addr   = addr;

    ret = spibsc_transfer(&spimd_reg);
    if (ret == false) {
        return ret;
    }

    ret = _busy_wait();

    return ret;
}

bool FlashAccess::_PageProgram(uint32_t addr, uint8_t * buf, int32_t size) {
    bool ret;

    /* ---- Write enable   ---- */
    ret = _WriteEnable();      /* WREN Command */
    if (ret == false) {
        return ret;
    }

    /* ----------- 1. Command, Address ---------------*/
    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- command ---- */
    spimd_reg.cde    = SPIBSC_OUTPUT_ENABLE;
    spimd_reg.cdb    = SPIBSC_1BIT;
    spimd_reg.cmd    = SFLASHCMD_PAGE_PROGRAM;

    /* ---- address ---- */
    spimd_reg.ade    = SPIBSC_OUTPUT_ADDR_24;
    spimd_reg.addre  = SPIBSC_SDR_TRANS;       /* SDR */
    spimd_reg.adb    = SPIBSC_1BIT;
    spimd_reg.addr   = addr;

    /* ---- Others ---- */
    spimd_reg.sslkp  = SPIBSC_SPISSL_KEEP;     /* SPBSSL level */

    ret = spibsc_transfer(&spimd_reg);         /* Command,Address */
    if (ret == false) {
        return ret;
    }

    /* ----------- 2. Data ---------------*/
    ret = data_send(SPIBSC_1BIT, SPIBSC_SPISSL_NEGATE, buf, size);
    if (ret == false) {
        return ret;
    }

    ret = _busy_wait();

    return ret;
}

bool FlashAccess::_Read(uint32_t addr, uint8_t * buf, int32_t size) {
    bool ret;

    /* ----------- 1. Command, Address ---------------*/
    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- command ---- */
    spimd_reg.cde    = SPIBSC_OUTPUT_ENABLE;
    spimd_reg.cdb    = SPIBSC_1BIT;
    spimd_reg.cmd    = SFLASHCMD_READ;

    /* ---- address ---- */
    spimd_reg.ade    = SPIBSC_OUTPUT_ADDR_24;
    spimd_reg.addre  = SPIBSC_SDR_TRANS;       /* SDR */
    spimd_reg.adb    = SPIBSC_1BIT;
    spimd_reg.addr   = addr;

    /* ---- Others ---- */
    spimd_reg.sslkp  = SPIBSC_SPISSL_KEEP;     /* SPBSSL level */

    ret = spibsc_transfer(&spimd_reg);         /* Command,Address */
    if (ret == false) {
        return ret;
    }

    /* ----------- 2. Data ---------------*/
    ret = data_recv(SPIBSC_1BIT, SPIBSC_SPISSL_NEGATE, buf, size);

    return ret;
}

bool FlashAccess::_WriteEnable(void) {
    bool ret;

    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- command ---- */
    spimd_reg.cde    = SPIBSC_OUTPUT_ENABLE;
    spimd_reg.cdb    = SPIBSC_1BIT;
    spimd_reg.cmd    = SFLASHCMD_WRITE_ENABLE;

    ret = spibsc_transfer(&spimd_reg);

    return ret;
}

bool FlashAccess::_busy_wait(void) {
    bool ret;
    uint8_t st_reg;

    while (1) {
        ret = _read_register(SFLASHCMD_READ_STATUS_REG, &st_reg);
        if (ret == false) {
            break;
        }
        if ((st_reg & STREG_BUSY_BIT) == 0) {
            break;
        }
    }

    return ret;
}

bool FlashAccess::_read_register(uint8_t cmd, uint8_t * status) {
    bool ret;

    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- command ---- */
    spimd_reg.cde    = SPIBSC_OUTPUT_ENABLE;
    spimd_reg.cdb    = SPIBSC_1BIT;
    spimd_reg.cmd    = cmd;

    /* ---- Others ---- */
    spimd_reg.sslkp  = SPIBSC_SPISSL_NEGATE;   /* SPBSSL level */
    spimd_reg.spire  = SPIBSC_SPIDATA_ENABLE;  /* read enable/disable */
    spimd_reg.spiwe  = SPIBSC_SPIDATA_ENABLE;  /* write enable/disable */

    /* ---- data ---- */
    spimd_reg.spide  = SPIBSC_OUTPUT_SPID_8;   /* Enable(8bit) */
    spimd_reg.spidre = SPIBSC_SDR_TRANS;       /* SDR */
    spimd_reg.spidb  = SPIBSC_1BIT;
    spimd_reg.smwdr[0] = 0x00;                 /* Output 0 in read status */
    spimd_reg.smwdr[1] = 0x00;                 /* Output 0 in read status */

    ret = spibsc_transfer(&spimd_reg);
    if (ret != false) {
        *status = (uint8_t)(spimd_reg.smrdr[0]);   /* Data[7:0]  */
    }

    return ret;
}

bool FlashAccess::data_send(uint32_t bit_width, uint32_t spbssl_level, uint8_t * buf, int32_t size) {
    bool ret = true;
    int32_t unit;
    uint8_t  *buf_b;
    uint16_t *buf_s;
    uint32_t *buf_l;

    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- Others ---- */
    spimd_reg.sslkp  = SPIBSC_SPISSL_KEEP;     /* SPBSSL level */
    spimd_reg.spiwe  = SPIBSC_SPIDATA_ENABLE;  /* write enable/disable */

    /* ---- data ---- */
    spimd_reg.spidb = bit_width;
    spimd_reg.spidre= SPIBSC_SDR_TRANS;        /* SDR */

    if (((uint32_t)size & 0x3)  == 0) {
        spimd_reg.spide = SPIBSC_OUTPUT_SPID_32;  /* Enable(32bit) */
        unit = 4;
    } else if (((uint32_t)size & 0x1) == 0) {
        spimd_reg.spide = SPIBSC_OUTPUT_SPID_16;  /* Enable(16bit) */
        unit = 2;
    } else {
        spimd_reg.spide = SPIBSC_OUTPUT_SPID_8;   /* Enable(8bit) */
        unit = 1;
    }

    while (size > 0) {
        if (unit == 1) {
            buf_b = (uint8_t *)buf;
            spimd_reg.smwdr[0] = (uint32_t)(((uint32_t)*buf_b) & 0x000000FF);
        } else if (unit == 2) {
            buf_s = (uint16_t *)buf;
            spimd_reg.smwdr[0] = (uint32_t)(((uint32_t)*buf_s) & 0x0000FFFF);
        } else if (unit == 4) {
            buf_l = (uint32_t *)buf;
            spimd_reg.smwdr[0] = (uint32_t)(((uint32_t)(*buf_l)) & 0xfffffffful);
        } else {
            /* Do Nothing */
        }

        buf  += unit;
        size -= unit;

        if (size <= 0) {
            spimd_reg.sslkp = spbssl_level;
        }

        ret = spibsc_transfer(&spimd_reg);    /* Data */
        if (ret == false) {
            return ret;
        }
    }

    return ret;
}

bool FlashAccess::data_recv(uint32_t bit_width, uint32_t spbssl_level, uint8_t * buf, int32_t size) {
    bool ret = true;
    int32_t unit;
    uint8_t  *buf_b;
    uint16_t *buf_s;
    uint32_t *buf_l;

    /* ---- spimd_reg init ---- */
    clear_spimd_reg(&spimd_reg);

    /* ---- Others ---- */
    spimd_reg.sslkp  = SPIBSC_SPISSL_KEEP;     /* SPBSSL level */
    spimd_reg.spire  = SPIBSC_SPIDATA_ENABLE;  /* read enable/disable */

    /* ---- data ---- */
    spimd_reg.spidb  = bit_width;
    spimd_reg.spidre = SPIBSC_SDR_TRANS;       /* SDR */

    if (((uint32_t)size & 0x3) == 0) {
        spimd_reg.spide = SPIBSC_OUTPUT_SPID_32;  /* Enable(32bit) */
        unit = 4;
    } else if (((uint32_t)size & 0x1) == 0) {
        spimd_reg.spide = SPIBSC_OUTPUT_SPID_16;  /* Enable(16bit) */
        unit = 2;
    } else {
        spimd_reg.spide = SPIBSC_OUTPUT_SPID_8;   /* Enable(8bit) */
        unit = 1;
    }

    while (size > 0) {
        if (unit >= size) {
            spimd_reg.sslkp = spbssl_level;
        }

        ret = spibsc_transfer(&spimd_reg);     /* Data */
        if (ret == false) {
            return ret;
        }

        if (unit == 1) {
            buf_b = (uint8_t *)buf;
            *buf_b = (uint8_t)((spimd_reg.smrdr[0]) & 0x000000fful);
        } else if (unit ==  2) {
            buf_s = (uint16_t *)buf;
            *buf_s = (uint16_t)((spimd_reg.smrdr[0]) & 0x0000fffful);
        } else if (unit == 4) {
            buf_l = (uint32_t *)buf;
            *buf_l = (uint32_t)((spimd_reg.smrdr[0]) & 0xfffffffful);
        } else {
            /* Do Nothing */
        }

        buf  += unit;
        size -= unit;
    }

    return ret;
}

void FlashAccess::spi_mode(void) {
    volatile uint32_t dummy_read_32;

    if (RegRead_32(&SPIBSC->CMNCR, SPIBSC_CMNCR_MD_SHIFT, SPIBSC_CMNCR_MD) != SPIBSC_CMNCR_MD_SPI) {
        /* ==== Change the MMU translation table SPI Multi-I/O bus space settings
                for use in SPI operating mode ==== */
        change_mmu_ttbl_spibsc(0);

        /* ==== Cleaning and invalidation of cache ==== */
        cache_control();

        /* ==== Switch to SPI operating mode ==== */
        spibsc_stop();

        dummy_read_32 = SPIBSC->CMNCR; /* dummy read */
        /* SPI Mode */
        RegWwrite_32(&SPIBSC->CMNCR, SPIBSC_CMNCR_MD_SPI, SPIBSC_CMNCR_MD_SHIFT, SPIBSC_CMNCR_MD);
        dummy_read_32 = SPIBSC->CMNCR; /* dummy read */

    }
    (void)dummy_read_32;
}

void FlashAccess::ex_mode(void) {
    volatile uint32_t dummy_read_32;

    if (RegRead_32(&SPIBSC->CMNCR, SPIBSC_CMNCR_MD_SHIFT, SPIBSC_CMNCR_MD) != SPIBSC_CMNCR_MD_EXTRD) {
        /* ==== Switch to external address space read mode and clear SPIBSC read cache ==== */
        spibsc_stop();

        /* Flush SPIBSC's read cache */
        RegWwrite_32(&SPIBSC->DRCR, SPIBSC_DRCR_RCF_EXE, SPIBSC_DRCR_RCF_SHIFT, SPIBSC_DRCR_RCF);
        dummy_read_32 = SPIBSC->DRCR;  /* dummy read */

        /* External address space read mode */
        RegWwrite_32(&SPIBSC->CMNCR, SPIBSC_CMNCR_MD_EXTRD, SPIBSC_CMNCR_MD_SHIFT, SPIBSC_CMNCR_MD);
        dummy_read_32 = SPIBSC->CMNCR; /* dummy read */

        /* ==== Change the MMU translation table SPI Multi-I/O bus space settings
                for use in external address space read mode ==== */
        change_mmu_ttbl_spibsc(1);

        /* ==== Cleaning and invalidation of cache ==== */
        cache_control();
    }
    (void)dummy_read_32;
}

void FlashAccess::clear_spimd_reg(st_spibsc_spimd_reg_t * regset) {
    /* ---- command ---- */
    regset->cde    = SPIBSC_OUTPUT_DISABLE;
    regset->cdb    = SPIBSC_1BIT;
    regset->cmd    = 0x00;

    /* ---- optional command ---- */
    regset->ocde   = SPIBSC_OUTPUT_DISABLE;
    regset->ocdb   = SPIBSC_1BIT;
    regset->ocmd   = 0x00;

    /* ---- address ---- */
    regset->ade    = SPIBSC_OUTPUT_DISABLE;
    regset->addre  = SPIBSC_SDR_TRANS;       /* SDR */
    regset->adb    = SPIBSC_1BIT;
    regset->addr   = 0x00000000;

    /* ---- option data ---- */
    regset->opde   = SPIBSC_OUTPUT_DISABLE;
    regset->opdre  = SPIBSC_SDR_TRANS;       /* SDR */
    regset->opdb   = SPIBSC_1BIT;
    regset->opd[0] = 0x00;    /* OPD3 */
    regset->opd[1] = 0x00;    /* OPD2 */
    regset->opd[2] = 0x00;    /* OPD1 */
    regset->opd[3] = 0x00;    /* OPD0 */

    /* ---- dummy cycle ---- */
    regset->dme    = SPIBSC_DUMMY_CYC_DISABLE;
    regset->dmdb   = SPIBSC_1BIT;
    regset->dmcyc  = SPIBSC_DUMMY_1CYC;

    /* ---- data ---- */
    regset->spide  = SPIBSC_OUTPUT_DISABLE;
    regset->spidre = SPIBSC_SDR_TRANS;       /* SDR */
    regset->spidb  = SPIBSC_1BIT;

    /* ---- Others ---- */
    regset->sslkp  = SPIBSC_SPISSL_NEGATE;   /* SPBSSL level */
    regset->spire  = SPIBSC_SPIDATA_DISABLE; /* read enable/disable */
    regset->spiwe  = SPIBSC_SPIDATA_DISABLE; /* write enable/disable */
}

bool FlashAccess::spibsc_transfer(st_spibsc_spimd_reg_t * regset) {
    if (RegRead_32(&SPIBSC->CMNCR, SPIBSC_CMNCR_MD_SHIFT, SPIBSC_CMNCR_MD) != SPIBSC_CMNCR_MD_SPI) {
        if (RegRead_32(&SPIBSC->CMNSR, SPIBSC_CMNSR_SSLF_SHIFT, SPIBSC_CMNSR_SSLF) != SPIBSC_SSL_NEGATE) {
            return false;
        }
        /* SPI Mode */
        RegWwrite_32(&SPIBSC->CMNCR, SPIBSC_CMNCR_MD_SPI, SPIBSC_CMNCR_MD_SHIFT, SPIBSC_CMNCR_MD);
    }

    if (RegRead_32(&SPIBSC->CMNSR, SPIBSC_CMNSR_TEND_SHIFT, SPIBSC_CMNSR_TEND) != SPIBSC_TRANS_END) {
        return false;
    }

    /* ---- Command ---- */
    /* Enable/Disable */
    RegWwrite_32(&SPIBSC->SMENR, regset->cde, SPIBSC_SMENR_CDE_SHIFT, SPIBSC_SMENR_CDE);
    if (regset->cde != SPIBSC_OUTPUT_DISABLE) {
        /* Command */
        RegWwrite_32(&SPIBSC->SMCMR, regset->cmd, SPIBSC_SMCMR_CMD_SHIFT, SPIBSC_SMCMR_CMD);
        /* Single/Dual/Quad */
        RegWwrite_32(&SPIBSC->SMENR, regset->cdb, SPIBSC_SMENR_CDB_SHIFT, SPIBSC_SMENR_CDB);
    }

    /* ---- Option Command ---- */
    /* Enable/Disable */
    RegWwrite_32(&SPIBSC->SMENR, regset->ocde, SPIBSC_SMENR_OCDE_SHIFT, SPIBSC_SMENR_OCDE);
    if (regset->ocde != SPIBSC_OUTPUT_DISABLE) {
        /* Option Command */
        RegWwrite_32(&SPIBSC->SMCMR, regset->ocmd, SPIBSC_SMCMR_OCMD_SHIFT, SPIBSC_SMCMR_OCMD);
        /* Single/Dual/Quad */
        RegWwrite_32(&SPIBSC->SMENR, regset->ocdb, SPIBSC_SMENR_OCDB_SHIFT, SPIBSC_SMENR_OCDB);
    }

    /* ---- Address ---- */
    /* Enable/Disable */
    RegWwrite_32(&SPIBSC->SMENR, regset->ade, SPIBSC_SMENR_ADE_SHIFT, SPIBSC_SMENR_ADE);
    if (regset->ade != SPIBSC_OUTPUT_DISABLE) {
        /* Address */
        RegWwrite_32(&SPIBSC->SMADR, regset->addr, SPIBSC_SMADR_ADR_SHIFT, SPIBSC_SMADR_ADR);
        /* Single/Dual/Quad */
        RegWwrite_32(&SPIBSC->SMENR, regset->adb, SPIBSC_SMENR_ADB_SHIFT, SPIBSC_SMENR_ADB);
    }

    /* ---- Option Data ---- */
    /* Enable/Disable */
    RegWwrite_32(&SPIBSC->SMENR, regset->opde, SPIBSC_SMENR_OPDE_SHIFT, SPIBSC_SMENR_OPDE);
    if (regset->opde != SPIBSC_OUTPUT_DISABLE) {
        /* Option Data */
        RegWwrite_32(&SPIBSC->SMOPR, regset->opd[0], SPIBSC_SMOPR_OPD3_SHIFT, SPIBSC_SMOPR_OPD3);
        RegWwrite_32(&SPIBSC->SMOPR, regset->opd[1], SPIBSC_SMOPR_OPD2_SHIFT, SPIBSC_SMOPR_OPD2);
        RegWwrite_32(&SPIBSC->SMOPR, regset->opd[2], SPIBSC_SMOPR_OPD1_SHIFT, SPIBSC_SMOPR_OPD1);
        RegWwrite_32(&SPIBSC->SMOPR, regset->opd[3], SPIBSC_SMOPR_OPD0_SHIFT, SPIBSC_SMOPR_OPD0);
        /* Single/Dual/Quad */
        RegWwrite_32(&SPIBSC->SMENR, regset->opdb, SPIBSC_SMENR_OPDB_SHIFT, SPIBSC_SMENR_OPDB);
    }

    /* ---- Dummy ---- */
     /* Enable/Disable */
     RegWwrite_32(&SPIBSC->SMENR, regset->dme, SPIBSC_SMENR_DME_SHIFT, SPIBSC_SMENR_DME);
     if (regset->dme != SPIBSC_DUMMY_CYC_DISABLE) {
         RegWwrite_32(&SPIBSC->SMDMCR, regset->dmdb, SPIBSC_SMDMCR_DMDB_SHIFT, SPIBSC_SMDMCR_DMDB);
         /* Dummy Cycle */
         RegWwrite_32(&SPIBSC->SMDMCR, regset->dmcyc, SPIBSC_SMDMCR_DMCYC_SHIFT, SPIBSC_SMDMCR_DMCYC);
     }

    /* ---- Data ---- */
    /* Enable/Disable */
    RegWwrite_32(&SPIBSC->SMENR, regset->spide, SPIBSC_SMENR_SPIDE_SHIFT, SPIBSC_SMENR_SPIDE);
    if (regset->spide != SPIBSC_OUTPUT_DISABLE) {
        if (SPIBSC_OUTPUT_SPID_8 == regset->spide) {
            if (RegRead_32(&SPIBSC0.CMNCR, SPIBSC_CMNCR_BSZ_SHIFT, SPIBSC_CMNCR_BSZ) == SPIBSC_CMNCR_BSZ_SINGLE) {
                SPIBSC->SMWDR0.UINT8[0] = (uint8_t)(regset->smwdr[0]);
            } else {
                SPIBSC->SMWDR0.UINT16[0] = (uint16_t)(regset->smwdr[0]);
            }
        } else if (regset->spide == SPIBSC_OUTPUT_SPID_16) {
            if (RegRead_32(&SPIBSC0.CMNCR, SPIBSC_CMNCR_BSZ_SHIFT, SPIBSC_CMNCR_BSZ) == SPIBSC_CMNCR_BSZ_SINGLE) {
                SPIBSC->SMWDR0.UINT16[0] = (uint16_t)(regset->smwdr[0]);
            } else {
                SPIBSC->SMWDR0.UINT32 = regset->smwdr[0];
            }
        } else if (regset->spide == SPIBSC_OUTPUT_SPID_32) {
            if (RegRead_32(&SPIBSC0.CMNCR, SPIBSC_CMNCR_BSZ_SHIFT, SPIBSC_CMNCR_BSZ) == SPIBSC_CMNCR_BSZ_SINGLE) {
                SPIBSC->SMWDR0.UINT32 = (uint32_t)(regset->smwdr[0]);
            } else {
                SPIBSC->SMWDR0.UINT32 = (uint32_t)(regset->smwdr[0]);
                SPIBSC->SMWDR1.UINT32 = (uint32_t)(regset->smwdr[1]);  /* valid in two serial-flash */
            }
        } else {
            /* none */
        }

        /* Single/Dual/Quad */
        RegWwrite_32(&SPIBSC->SMENR, regset->spidb, SPIBSC_SMENR_SPIDB_SHIFT, SPIBSC_SMENR_SPIDB);
    }

    RegWwrite_32(&SPIBSC->SMCR, regset->sslkp, SPIBSC_SMCR_SSLKP_SHIFT, SPIBSC_SMCR_SSLKP);

    if ((regset->spidb != SPIBSC_1BIT) && (regset->spide != SPIBSC_OUTPUT_DISABLE)) {
        if ((regset->spire == SPIBSC_SPIDATA_ENABLE) && (regset->spiwe == SPIBSC_SPIDATA_ENABLE)) {
            /* not set in same time */
            return false;
        }
    }

    RegWwrite_32(&SPIBSC->SMCR, regset->spire, SPIBSC_SMCR_SPIRE_SHIFT, SPIBSC_SMCR_SPIRE);
    RegWwrite_32(&SPIBSC->SMCR, regset->spiwe, SPIBSC_SMCR_SPIWE_SHIFT, SPIBSC_SMCR_SPIWE);

    /* SDR Transmission/DDR Transmission Setting */
    RegWwrite_32(&SPIBSC->SMDRENR, regset->addre, SPIBSC_SMDRENR_ADDRE_SHIFT, SPIBSC_SMDRENR_ADDRE);
    RegWwrite_32(&SPIBSC->SMDRENR, regset->opdre, SPIBSC_SMDRENR_OPDRE_SHIFT, SPIBSC_SMDRENR_OPDRE);
    RegWwrite_32(&SPIBSC->SMDRENR, regset->spidre, SPIBSC_SMDRENR_SPIDRE_SHIFT, SPIBSC_SMDRENR_SPIDRE);

    /* execute after setting SPNDL bit */
    RegWwrite_32(&SPIBSC->SMCR, SPIBSC_SPI_ENABLE, SPIBSC_SMCR_SPIE_SHIFT, SPIBSC_SMCR_SPIE);

    /* wait for transfer-start */
    while (RegRead_32(&SPIBSC->CMNSR, SPIBSC_CMNSR_TEND_SHIFT, SPIBSC_CMNSR_TEND) != SPIBSC_TRANS_END) {
        /* wait for transfer-end */
    }

    if (SPIBSC_OUTPUT_SPID_8 == regset->spide) {
        if (RegRead_32(&SPIBSC0.CMNCR, SPIBSC_CMNCR_BSZ_SHIFT, SPIBSC_CMNCR_BSZ) == SPIBSC_CMNCR_BSZ_SINGLE) {
            regset->smrdr[0] = SPIBSC->SMRDR0.UINT8[0];
        } else {
            regset->smrdr[0] = SPIBSC->SMRDR0.UINT16[0];        /* valid in two serial-flash  */
        }
    } else if (regset->spide == SPIBSC_OUTPUT_SPID_16) {
        if (RegRead_32(&SPIBSC0.CMNCR, SPIBSC_CMNCR_BSZ_SHIFT, SPIBSC_CMNCR_BSZ) == SPIBSC_CMNCR_BSZ_SINGLE) {
            regset->smrdr[0] = SPIBSC->SMRDR0.UINT16[0];
        } else {
            regset->smrdr[0] = SPIBSC->SMRDR0.UINT32;           /* valid in two serial-flash  */
        }
    } else if (regset->spide == SPIBSC_OUTPUT_SPID_32) {
        if (RegRead_32(&SPIBSC0.CMNCR, SPIBSC_CMNCR_BSZ_SHIFT, SPIBSC_CMNCR_BSZ) == SPIBSC_CMNCR_BSZ_SINGLE) {
            regset->smrdr[0] = SPIBSC->SMRDR0.UINT32;
        } else {
            regset->smrdr[0] = SPIBSC->SMRDR0.UINT32;           /* valid in two serial-flash  */
            regset->smrdr[1] = SPIBSC->SMRDR1.UINT32;
        }
    } else {
        /* none */
    }

    return true;
}

uint32_t FlashAccess::RegRead_32(volatile uint32_t * ioreg, uint32_t shift, uint32_t mask) {
    uint32_t reg_value;

    reg_value = *ioreg;                        /* Read from register            */
    reg_value = (reg_value & mask) >> shift;   /* Clear other bit and Bit shift */

    return reg_value;
}

void FlashAccess::RegWwrite_32(volatile uint32_t * ioreg, uint32_t write_value, uint32_t shift, uint32_t mask) {
    uint32_t reg_value;

    reg_value = *ioreg;                                         /* Read from register */
    reg_value = (reg_value & (~mask)) | (write_value << shift); /* Modify value       */
    *ioreg    = reg_value;                                      /* Write to register  */
}

/** private **/

void FlashAccess::change_mmu_ttbl_spibsc(uint32_t type) {
    uint32_t index;               /* Loop variable: table index */
    mmu_ttbl_desc_section_t desc; /* Loop variable: descriptor */
    mmu_ttbl_desc_section_t * table = (mmu_ttbl_desc_section_t *)TTB;

    /* ==== Modify SPI Multi-I/O bus space settings in the MMU translation table ==== */
    for (index = (SPIBSC_ADDR_START >> 20); index <= (SPIBSC_ADDR_END >> 20); index++) {
        /* Modify memory attribute descriptor */
        if (type == 0) {         /* Spi */
            desc = table[index];
            desc_tbl[index - (SPIBSC_ADDR_START >> 20)] = desc;
            desc.AP1_0 = 0x0u;   /* AP[2:0] = b'000 (No access) */
            desc.AP2   = 0x0u;
            desc.XN    = 0x1u;   /* XN = 1 (Execute never) */
        } else {                 /* Xip */
            desc = desc_tbl[index - (SPIBSC_ADDR_START >> 20)];
        }
        /* Write descriptor back to translation table */
        table[index] = desc;
    }
}

void FlashAccess::spibsc_stop(void) {
    if (((SPIBSC->DRCR & SPIBSC_DRCR_RBE)  != 0) &&
        ((SPIBSC->DRCR & SPIBSC_DRCR_SSLE) != 0)) {
        RegWwrite_32(&SPIBSC->DRCR, 1, SPIBSC_DRCR_SSLN_SHIFT, SPIBSC_DRCR_SSLN);
    }

    while (RegRead_32(&SPIBSC->CMNSR, SPIBSC_CMNSR_SSLF_SHIFT, SPIBSC_CMNSR_SSLF) != SPIBSC_SSL_NEGATE) {
        ;
    }

    while (RegRead_32(&SPIBSC->CMNSR, SPIBSC_CMNSR_TEND_SHIFT, SPIBSC_CMNSR_TEND) != SPIBSC_TRANS_END) {
        ;
    }
}

#ifndef __STATIC_FORCEINLINE
  #if   defined ( __CC_ARM )
    #define __STATIC_FORCEINLINE  static __forceinline
  #elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
    #define __STATIC_FORCEINLINE  __attribute__((always_inline)) static __inline
  #elif defined ( __GNUC__ )
    #define __STATIC_FORCEINLINE  __attribute__((always_inline)) static __inline
  #elif defined ( __ICCARM__ )
    #define __STATIC_FORCEINLINE  _Pragma("inline=forced") static inline
  #endif
#endif

#if   defined ( __CC_ARM )
__STATIC_FORCEINLINE __ASM void L1C_CleanInvalidateCache_sforce(uint32_t op) {
        ARM

        PUSH    {R4-R11}

        MRC     p15, 1, R6, c0, c0, 1      // Read CLIDR
        ANDS    R3, R6, #0x07000000        // Extract coherency level
        MOV     R3, R3, LSR #23            // Total cache levels << 1
        BEQ     Finished                   // If 0, no need to clean

        MOV     R10, #0                    // R10 holds current cache level << 1
Loop1   ADD     R2, R10, R10, LSR #1       // R2 holds cache "Set" position
        MOV     R1, R6, LSR R2             // Bottom 3 bits are the Cache-type for this level
        AND     R1, R1, #7                 // Isolate those lower 3 bits
        CMP     R1, #2
        BLT     Skip                       // No cache or only instruction cache at this level

        MCR     p15, 2, R10, c0, c0, 0     // Write the Cache Size selection register
        ISB                                // ISB to sync the change to the CacheSizeID reg
        MRC     p15, 1, R1, c0, c0, 0      // Reads current Cache Size ID register
        AND     R2, R1, #7                 // Extract the line length field
        ADD     R2, R2, #4                 // Add 4 for the line length offset (log2 16 bytes)
        LDR     R4, =0x3FF
        ANDS    R4, R4, R1, LSR #3         // R4 is the max number on the way size (right aligned)
        CLZ     R5, R4                     // R5 is the bit position of the way size increment
        LDR     R7, =0x7FFF
        ANDS    R7, R7, R1, LSR #13        // R7 is the max number of the index size (right aligned)

Loop2   MOV     R9, R4                     // R9 working copy of the max way size (right aligned)

Loop3   ORR     R11, R10, R9, LSL R5       // Factor in the Way number and cache number into R11
        ORR     R11, R11, R7, LSL R2       // Factor in the Set number
        CMP     R0, #0
        BNE     Dccsw
        MCR     p15, 0, R11, c7, c6, 2     // DCISW. Invalidate by Set/Way
        B       cont
Dccsw   CMP     R0, #1
        BNE     Dccisw
        MCR     p15, 0, R11, c7, c10, 2    // DCCSW. Clean by Set/Way
        B       cont
Dccisw  MCR     p15, 0, R11, c7, c14, 2    // DCCISW. Clean and Invalidate by Set/Way
cont    SUBS    R9, R9, #1                 // Decrement the Way number
        BGE     Loop3
        SUBS    R7, R7, #1                 // Decrement the Set number
        BGE     Loop2
Skip    ADD     R10, R10, #2               // Increment the cache number
        CMP     R3, R10
        BGT     Loop1

Finished
        DSB
        POP    {R4-R11}
        BX     lr
}

#elif (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) || defined ( __GNUC__ )
__STATIC_FORCEINLINE void L1C_CleanInvalidateCache_sforce(uint32_t op) {
  __ASM volatile(
    "        PUSH    {R4-R11}                   \n"

    "        MRC     p15, 1, R6, c0, c0, 1      \n" // Read CLIDR
    "        ANDS    R3, R6, #0x07000000        \n" // Extract coherency level
    "        MOV     R3, R3, LSR #23            \n" // Total cache levels << 1
    "        BEQ     Finished                   \n" // If 0, no need to clean

    "        MOV     R10, #0                    \n" // R10 holds current cache level << 1
    "Loop1:  ADD     R2, R10, R10, LSR #1       \n" // R2 holds cache "Set" position
    "        MOV     R1, R6, LSR R2             \n" // Bottom 3 bits are the Cache-type for this level
    "        AND     R1, R1, #7                 \n" // Isolate those lower 3 bits
    "        CMP     R1, #2                     \n"
    "        BLT     Skip                       \n" // No cache or only instruction cache at this level

    "        MCR     p15, 2, R10, c0, c0, 0     \n" // Write the Cache Size selection register
    "        ISB                                \n" // ISB to sync the change to the CacheSizeID reg
    "        MRC     p15, 1, R1, c0, c0, 0      \n" // Reads current Cache Size ID register
    "        AND     R2, R1, #7                 \n" // Extract the line length field
    "        ADD     R2, R2, #4                 \n" // Add 4 for the line length offset (log2 16 bytes)
    "        LDR     R4, =0x3FF                 \n"
    "        ANDS    R4, R4, R1, LSR #3         \n" // R4 is the max number on the way size (right aligned)
    "        CLZ     R5, R4                     \n" // R5 is the bit position of the way size increment
    "        LDR     R7, =0x7FFF                \n"
    "        ANDS    R7, R7, R1, LSR #13        \n" // R7 is the max number of the index size (right aligned)

    "Loop2:  MOV     R9, R4                     \n" // R9 working copy of the max way size (right aligned)

    "Loop3:  ORR     R11, R10, R9, LSL R5       \n" // Factor in the Way number and cache number into R11
    "        ORR     R11, R11, R7, LSL R2       \n" // Factor in the Set number
    "        CMP     R0, #0                     \n"
    "        BNE     Dccsw                      \n"
    "        MCR     p15, 0, R11, c7, c6, 2     \n" // DCISW. Invalidate by Set/Way
    "        B       cont                       \n"
    "Dccsw:  CMP     R0, #1                     \n"
    "        BNE     Dccisw                     \n"
    "        MCR     p15, 0, R11, c7, c10, 2    \n" // DCCSW. Clean by Set/Way
    "        B       cont                       \n"
    "Dccisw: MCR     p15, 0, R11, c7, c14, 2    \n" // DCCISW. Clean and Invalidate by Set/Way
    "cont:   SUBS    R9, R9, #1                 \n" // Decrement the Way number
    "        BGE     Loop3                      \n"
    "        SUBS    R7, R7, #1                 \n" // Decrement the Set number
    "        BGE     Loop2                      \n"
    "Skip:   ADD     R10, R10, #2               \n" // Increment the cache number
    "        CMP     R3, R10                    \n"
    "        BGT     Loop1                      \n"

    "Finished:                                  \n"
    "        DSB                                \n"
    "        POP    {R4-R11}                      "
  );
}

#else
__STATIC_FORCEINLINE void __L1C_MaintainDCacheSetWay_sforce(uint32_t level, uint32_t maint) {
    register volatile uint32_t Dummy;
    register volatile uint32_t ccsidr;
    uint32_t num_sets;
    uint32_t num_ways;
    uint32_t shift_way;
    uint32_t log2_linesize;
    uint32_t log2_num_ways;

    Dummy = level << 1;
    /* set csselr, select ccsidr register */
    __set_CCSIDR(Dummy);
    /* get current ccsidr register */
    ccsidr = __get_CCSIDR();
    num_sets = ((ccsidr & 0x0FFFE000) >> 13) + 1;
    num_ways = ((ccsidr & 0x00001FF8) >> 3) + 1;
    log2_linesize = (ccsidr & 0x00000007) + 2 + 2;
    log2_num_ways = log2_up(num_ways);
    shift_way = 32 - log2_num_ways;
    for (int way = num_ways-1; way >= 0; way--) {
        for (int set = num_sets-1; set >= 0; set--) {
            Dummy = (level << 1) | (set << log2_linesize) | (way << shift_way);
            switch (maint) {
                case 0:
                    // DCISW. Invalidate by Set/Way
                    __ASM volatile("MCR p15, 0, %0, c7, c6, 2" : : "r"(Dummy) : "memory");
                    break;
                case 1:
                    // DCCSW. Clean by Set/Way
                    __ASM volatile("MCR p15, 0, %0, c7, c10, 2" : : "r"(Dummy) : "memory");
                    break;
                default:
                    // DCCISW. Clean and Invalidate by Set/Way
                    __ASM volatile("MCR p15, 0, %0, c7, c14, 2" : : "r"(Dummy) : "memory");
                    break;
            }
        }
    }
    __DMB();
}

__STATIC_FORCEINLINE void L1C_CleanInvalidateCache_sforce(uint32_t op) {
    register volatile uint32_t clidr;
    uint32_t cache_type;
    clidr =  __get_CLIDR();
    for (uint32_t i = 0; i<7; i++) {
        cache_type = (clidr >> i*3) & 0x7UL;
        if ((cache_type >= 2) && (cache_type <= 4)) {
            __L1C_MaintainDCacheSetWay_sforce(i, op);
        }
    }
}
#endif

#ifdef MBED_VERSION

void FlashAccess::cache_control(void) {
    unsigned int assoc;

    /* ==== Cleaning and invalidation of the L1 data cache ==== */
    L1C_CleanInvalidateCache_sforce(2);
    __DSB();

    /* ==== Cleaning and invalidation of the L2 cache ==== */
    if (PL310->AUX_CNT & (1<<16)) {
        assoc = 16;
    } else {
        assoc =  8;
    }
    PL310->INV_WAY = (1 << assoc) - 1;
    while(PL310->INV_WAY & ((1 << assoc) - 1)); // poll invalidate
    PL310->CACHE_SYNC = 0x0;

    /* ==== Invalidate all TLB entries ==== */
    __ca9u_inv_tlb_all();

    /* ==== Invalidate the L1 instruction cache ==== */
    __v7_inv_icache_all();
    __DSB();
    __ISB();
}

#else  // mbed-os 5.6.4

void FlashAccess::cache_control(void) {
    unsigned int assoc;

    /* ==== Cleaning and invalidation of the L1 data cache ==== */
    L1C_CleanInvalidateCache_sforce(2);
    __DSB();

    /* ==== Cleaning and invalidation of the L2 cache ==== */
    if (L2C_310->AUX_CNT & (1U << 16U)) {
        assoc = 16U;
    } else {
        assoc =  8U;
    }
    L2C_310->CLEAN_INV_WAY = (1U << assoc) - 1U;
    while (L2C_310->CLEAN_INV_WAY & ((1U << assoc) - 1U)); // poll invalidate
    L2C_310->CACHE_SYNC = 0x0;

    /* ==== Invalidate all TLB entries ==== */
    __set_TLBIALL(0);
    __DSB();     // ensure completion of the invalidation
    __ISB();     // ensure instruction fetch path sees new state

    /* ==== Invalidate the L1 instruction cache ==== */
    __set_ICIALLU(0);
    __DSB();     // ensure completion of the invalidation
    __ISB();     // ensure instruction fetch path sees new I cache state
}
#endif