Modify the file main.cpp for M487

Dependencies:   BufferedSerial

NuMaker-mbed-SD-driver/NuSDBlockDevice.cpp

Committer:
shliu1
Date:
2017-09-29
Revision:
0:c89ccc69a48b

File content as of revision 0:c89ccc69a48b:

/* mbed Microcontroller Library
 * Copyright (c) 2015-2016 Nuvoton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Nuvoton mbed enabled targets which support SD card of SD bus mode */
#if defined(TARGET_NUMAKER_PFM_NUC472) || defined(TARGET_NUMAKER_PFM_M487)

#include "NuSDBlockDevice.h"
#include "PeripheralPins.h"
#include "mbed_debug.h"
#include "nu_modutil.h"

#if defined(TARGET_NUMAKER_PFM_NUC472)
#define NU_SDH_DAT0         PF_5
#define NU_SDH_DAT1         PF_4
#define NU_SDH_DAT2         PF_3
#define NU_SDH_DAT3         PF_2
#define NU_SDH_CMD          PF_7
#define NU_SDH_CLK          PF_8
#define NU_SDH_CDn          PF_6

#elif defined(TARGET_NUMAKER_PFM_M487)
#define NU_SDH_DAT0         PE_2
#define NU_SDH_DAT1         PE_3
#define NU_SDH_DAT2         PE_4
#define NU_SDH_DAT3         PE_5
#define NU_SDH_CMD          PE_7
#define NU_SDH_CLK          PE_6
#define NU_SDH_CDn          PD_13

#endif

#if defined(TARGET_NUMAKER_PFM_NUC472)
extern DISK_DATA_T SD_DiskInfo0;
extern DISK_DATA_T SD_DiskInfo1;
extern SD_INFO_T SD0,SD1;
extern int sd0_ok,sd1_ok;

#elif defined(TARGET_NUMAKER_PFM_M487)
extern int SDH_ok;
extern SDH_INFO_T SD0, SD1;

#endif


static const struct nu_modinit_s sdh_modinit_tab[] = {
#if defined(TARGET_NUMAKER_PFM_NUC472)
    {SD_0_0, SDH_MODULE, CLK_CLKSEL0_SDHSEL_PLL, CLK_CLKDIV0_SDH(2), SDH_RST, SD_IRQn, NULL},
    {SD_0_1, SDH_MODULE, CLK_CLKSEL0_SDHSEL_PLL, CLK_CLKDIV0_SDH(2), SDH_RST, SD_IRQn, NULL},
#elif defined(TARGET_NUMAKER_PFM_M487)
    {SD_0, SDH0_MODULE, CLK_CLKSEL0_SDH0SEL_HCLK, CLK_CLKDIV0_SDH0(2), SDH0_RST, SDH0_IRQn, NULL},
    {SD_1, SDH1_MODULE, CLK_CLKSEL0_SDH1SEL_HCLK, CLK_CLKDIV3_SDH1(2), SDH1_RST, SDH1_IRQn, NULL},
#endif

    {NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL}
};



#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK        -5001	/*!< operation would block */
#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED        -5002	/*!< unsupported operation */
#define SD_BLOCK_DEVICE_ERROR_PARAMETER          -5003	/*!< invalid parameter */
#define SD_BLOCK_DEVICE_ERROR_NO_INIT            -5004	/*!< uninitialized */
#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE          -5005	/*!< device is missing or not connected */
#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED    -5006	/*!< write protected */


NuSDBlockDevice::NuSDBlockDevice() :
    _sectors(0),
    _is_initialized(false),
    _dbg(false),
    _sdh_modinit(NULL),
    _sdh((SDName) NC),
    _sdh_base(NULL),
#if defined(TARGET_NUMAKER_PFM_NUC472)
    _sdh_port((uint32_t) -1),
#endif
    _sdh_irq_thunk(this, &NuSDBlockDevice::_sdh_irq),
    _sd_dat0(NU_SDH_DAT0),
    _sd_dat1(NU_SDH_DAT1),
    _sd_dat2(NU_SDH_DAT2),
    _sd_dat3(NU_SDH_DAT3),
    _sd_cmd(NU_SDH_CMD),
    _sd_clk(NU_SDH_CLK),
    _sd_cdn(NU_SDH_CDn)
{
}

NuSDBlockDevice::NuSDBlockDevice(PinName sd_dat0, PinName sd_dat1, PinName sd_dat2, PinName sd_dat3,
    PinName sd_cmd, PinName sd_clk, PinName sd_cdn) :
    _sectors(0),
    _is_initialized(false),
    _dbg(false),
    _sdh_modinit(NULL),
    _sdh((SDName) NC),
    _sdh_base(NULL),
#if defined(TARGET_NUMAKER_PFM_NUC472)
    _sdh_port((uint32_t) -1),
#endif
    _sdh_irq_thunk(this, &NuSDBlockDevice::_sdh_irq)
{
    _sd_dat0 = sd_dat0;
    _sd_dat1 = sd_dat1;
    _sd_dat2 = sd_dat2;
    _sd_dat3 = sd_dat3;
    _sd_cmd = sd_cmd;
    _sd_clk = sd_clk;
    _sd_cdn = sd_cdn;
}

NuSDBlockDevice::~NuSDBlockDevice()
{
    if (_is_initialized) {
        deinit();
    }
}

int NuSDBlockDevice::init()
{
    _lock.lock();
    int err = BD_ERROR_OK;
    
    do {
        err = _init_sdh();
        if (err != BD_ERROR_OK) {
            break;
        }
        
#if defined(TARGET_NUMAKER_PFM_NUC472)
        SD_Open(_sdh_port | CardDetect_From_GPIO);
        SD_Probe(_sdh_port);

        switch (_sdh_port) {
        case SD_PORT0:
            _is_initialized = sd0_ok && (SD0.CardType != SD_TYPE_UNKNOWN);
            break;
    
        case SD_PORT1:
            _is_initialized = sd1_ok && (SD1.CardType != SD_TYPE_UNKNOWN);
            break;
        }
    
#elif defined(TARGET_NUMAKER_PFM_M487)
        MBED_ASSERT(_sdh_modinit != NULL);
        
        NVIC_SetVector(_sdh_modinit->irq_n, _sdh_irq_thunk.entry());
        NVIC_EnableIRQ(_sdh_modinit->irq_n);

        SDH_Open(_sdh_base, CardDetect_From_GPIO);
        SDH_Probe(_sdh_base);
    
        switch (NU_MODINDEX(_sdh)) {
        case 0:
            _is_initialized = SDH_ok && (SD0.CardType != SDH_TYPE_UNKNOWN);
            break;
    
        case 1:
            _is_initialized = SDH_ok && (SD1.CardType != SDH_TYPE_UNKNOWN);
            break;
        }
#endif

        if (!_is_initialized) {
            debug_if(_dbg, "Fail to initialize card\n");
            err = BD_ERROR_DEVICE_ERROR;
        }
        debug_if(_dbg, "init card = %d\n", _is_initialized);
        _sectors = _sd_sectors();
    
    } while (0);

    _lock.unlock();
    
    return err;
}

int NuSDBlockDevice::deinit()
{
    _lock.lock();
   
    if (_sdh_modinit) {
        CLK_DisableModuleClock(_sdh_modinit->clkidx);
    }
    
#if defined(TARGET_NUMAKER_PFM_NUC472)
    // TODO
#elif defined(TARGET_NUMAKER_PFM_M487)
    // TODO
#endif

    _is_initialized = false;
    
    _lock.unlock();
    
    return BD_ERROR_OK;
}

int NuSDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
{
    if (! is_valid_program(addr, size)) {
        return SD_BLOCK_DEVICE_ERROR_PARAMETER;
    }

    _lock.lock();
    int err = BD_ERROR_OK;
    
    do {
        if (! _is_initialized) {
            err = SD_BLOCK_DEVICE_ERROR_NO_INIT;
        }

#if defined(TARGET_NUMAKER_PFM_NUC472)
        if (SD_Write(_sdh_port, (uint8_t*)b, addr / 512, size / 512) != 0) {
#elif defined(TARGET_NUMAKER_PFM_M487)
        if (SDH_Write(_sdh_base, (uint8_t*)b, addr / 512, size / 512) != 0) {
#endif
            err = BD_ERROR_DEVICE_ERROR;
        }
        
    } while (0);
    
    _lock.unlock();
    
    return err;
}

int NuSDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
{
    if (! is_valid_read(addr, size)) {
        return SD_BLOCK_DEVICE_ERROR_PARAMETER;
    }

    _lock.lock();
    int err = BD_ERROR_OK;
    
    do {
        if (! _is_initialized) {
            err = SD_BLOCK_DEVICE_ERROR_NO_INIT;
        }
        
#if defined(TARGET_NUMAKER_PFM_NUC472)
        if (SD_Read(_sdh_port, (uint8_t*)b, addr / 512, size / 512) != 0) {
#elif defined(TARGET_NUMAKER_PFM_M487)
        if (SDH_Read(_sdh_base, (uint8_t*)b, addr / 512, size / 512) != 0) {
#endif
            err = BD_ERROR_DEVICE_ERROR;
        }
        
    } while (0);
    
    _lock.unlock();
    
    return err;
}

int NuSDBlockDevice::erase(bd_addr_t addr, bd_size_t size)
{
    return BD_ERROR_OK;
}

bd_size_t NuSDBlockDevice::get_read_size() const
{
    return 512;
}

bd_size_t NuSDBlockDevice::get_program_size() const
{
    return 512;
}

bd_size_t NuSDBlockDevice::get_erase_size() const
{
    return 512;
}

bd_size_t NuSDBlockDevice::size() const
{
    return 512 * _sectors;
}

void NuSDBlockDevice::debug(bool dbg)
{
    _dbg = dbg;
}

int NuSDBlockDevice::_init_sdh()
{
    debug_if(_dbg, "SD MPF Setting & Enable SD IP Clock\n");
        
    // Check if all pins belong to the same SD module
    // Merge SD DAT0/1/2/3
    uint32_t sd_dat0_mod = pinmap_peripheral(_sd_dat0, PinMap_SD_DAT0);
    uint32_t sd_dat1_mod = pinmap_peripheral(_sd_dat1, PinMap_SD_DAT1);
    uint32_t sd_dat2_mod = pinmap_peripheral(_sd_dat2, PinMap_SD_DAT2);
    uint32_t sd_dat3_mod = pinmap_peripheral(_sd_dat3, PinMap_SD_DAT3);
    uint32_t sd_dat01_mod = (SDName) pinmap_merge(sd_dat0_mod, sd_dat1_mod);
    uint32_t sd_dat23_mod = (SDName) pinmap_merge(sd_dat2_mod, sd_dat3_mod);
    uint32_t sd_dat0123_mod = (SDName) pinmap_merge(sd_dat01_mod, sd_dat23_mod);
    // Merge SD CMD/CLK/CDn
    uint32_t sd_cmd_mod = pinmap_peripheral(_sd_cmd, PinMap_SD_CMD);
    uint32_t sd_clk_mod = pinmap_peripheral(_sd_clk, PinMap_SD_CLK);
    uint32_t sd_cdn_mod = pinmap_peripheral(_sd_cdn, PinMap_SD_CD);
    uint32_t sd_cmdclk_mod = (SDName) pinmap_merge(sd_cmd_mod, sd_clk_mod);
    uint32_t sd_cmdclkcdn_mod = (SDName) pinmap_merge(sd_cmdclk_mod, sd_cdn_mod);
    // Merge SD DAT0/1/2/3 and SD CMD/CLK/CDn
    uint32_t sd_mod = (SDName) pinmap_merge(sd_dat0123_mod, sd_cmdclkcdn_mod);
    
    if (sd_mod == (uint32_t) NC) {
        debug("SD pinmap error\n");
        return BD_ERROR_DEVICE_ERROR;
    }
    
    _sdh_modinit = get_modinit(sd_mod, sdh_modinit_tab);
    MBED_ASSERT(_sdh_modinit != NULL);
    MBED_ASSERT(_sdh_modinit->modname == sd_mod);
    
    
    // Configure SD multi-function pins
    pinmap_pinout(_sd_dat0, PinMap_SD_DAT0);
    pinmap_pinout(_sd_dat1, PinMap_SD_DAT1);
    pinmap_pinout(_sd_dat2, PinMap_SD_DAT2);
    pinmap_pinout(_sd_dat3, PinMap_SD_DAT3);
    pinmap_pinout(_sd_cmd, PinMap_SD_CMD);
    pinmap_pinout(_sd_clk, PinMap_SD_CLK);
    pinmap_pinout(_sd_cdn, PinMap_SD_CD);
    
    // Configure SD IP clock 
    SYS_UnlockReg();
    
    // Determine SDH port dependent on passed-in pins
    _sdh = (SDName) sd_mod;
    _sdh_base = (SDH_T *) NU_MODBASE(_sdh);
#if defined(TARGET_NUMAKER_PFM_NUC472)
    switch (NU_MODSUBINDEX(_sdh)) {
    case 0:
        _sdh_port = SD_PORT0;
        break;
        
    case 1:
        _sdh_port = SD_PORT1;
        break;
    }
#endif

    SYS_ResetModule(_sdh_modinit->rsetidx);
    CLK_SetModuleClock(_sdh_modinit->clkidx, _sdh_modinit->clksrc, _sdh_modinit->clkdiv);
    CLK_EnableModuleClock(_sdh_modinit->clkidx);
    
    SYS_LockReg();

    return BD_ERROR_OK;
}

uint32_t NuSDBlockDevice::_sd_sectors()
{
    _lock.lock();
   
#if defined(TARGET_NUMAKER_PFM_NUC472)
    switch (_sdh_port) {
    case SD_PORT0:
        _sectors = SD_DiskInfo0.totalSectorN;
        break;
    case SD_PORT1:
        _sectors = SD_DiskInfo1.totalSectorN;
        break;
    }
    
#elif defined(TARGET_NUMAKER_PFM_M487)
    switch (NU_MODINDEX(_sdh)) {
    case 0:
        _sectors = SD0.totalSectorN;
        break;
    case 1:
        _sectors = SD1.totalSectorN;
        break;
    }
    
#endif

    _lock.unlock();
    
    return _sectors;
}

void NuSDBlockDevice::_sdh_irq()
{
#if defined(TARGET_NUMAKER_PFM_NUC472)
    // TODO: Support IRQ
    
#elif defined(TARGET_NUMAKER_PFM_M487)
    // FMI data abort interrupt
    if (_sdh_base->GINTSTS & SDH_GINTSTS_DTAIF_Msk) {
        _sdh_base->GINTSTS = SDH_GINTSTS_DTAIF_Msk;
        /* ResetAllEngine() */
        _sdh_base->GCTL |= SDH_GCTL_GCTLRST_Msk;
    }

    //----- SD interrupt status
    if (_sdh_base->INTSTS & SDH_INTSTS_BLKDIF_Msk) {
        // block down
        extern uint8_t volatile _SDH_SDDataReady;
        _SDH_SDDataReady = TRUE;
        _sdh_base->INTSTS = SDH_INTSTS_BLKDIF_Msk;
    }
    
    // NOTE: On M487, there are two SDH instances which each support port 0 and don't support port 1.
    //       Port 0 (support): INTEN.CDIEN0, INTEN.CDSRC0, INTSTS.CDIF0, INTSTS.CDSTS0
    //       Port 1 (no support): INTEN.CDIEN1, INTEN.CDSRC1, INTSTS.CDIF1, INTSTS.CDSTS1
    if (_sdh_base->INTSTS & SDH_INTSTS_CDIF_Msk) { // port 0 card detect
        _sdh_base->INTSTS = SDH_INTSTS_CDIF_Msk;
        // TBD: Support PnP
    }

    // CRC error interrupt
    if (_sdh_base->INTSTS & SDH_INTSTS_CRCIF_Msk) {
        _sdh_base->INTSTS = SDH_INTSTS_CRCIF_Msk;      // clear interrupt flag
    }

    if (_sdh_base->INTSTS & SDH_INTSTS_DITOIF_Msk) {
        _sdh_base->INTSTS = SDH_INTSTS_DITOIF_Msk;
    }

    // Response in timeout interrupt
    if (_sdh_base->INTSTS & SDH_INTSTS_RTOIF_Msk) {
        _sdh_base->INTSTS |= SDH_INTSTS_RTOIF_Msk;
    }
#endif
}


#endif  //#if defined(TARGET_NUMAKER_PFM_NUC472) || defined(TARGET_NUMAKER_PFM_M487)