rt-thread/bsp/nuvoton/libraries/ma35/rtt_port/drv_sdio.c

1062 lines
28 KiB
C

/**************************************************************************//**
*
* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-11-19 Wayne First version
*
******************************************************************************/
#include "rtconfig.h"
#if defined(BSP_USING_SDH)
#include <rtdevice.h>
#include <drivers/dev_mmcsd_core.h>
#include <drivers/dev_sdio.h>
#include "NuMicro.h"
#include "drv_common.h"
#define LOG_TAG "drv.sdio"
#undef DBG_ENABLE
#define DBG_SECTION_NAME LOG_TAG
#define DBG_LEVEL LOG_LVL_INFO
#define DBG_COLOR
#include <rtdbg.h>
#define SDH_ALIGN_LEN 64
#define SDH_BUFF_SIZE (512*1024)
enum
{
SDH_START = -1,
#if defined(BSP_USING_SDH0)
SDH0_IDX,
#endif
#if defined(BSP_USING_SDH1)
SDH1_IDX,
#endif
SDH_CNT
};
struct nu_sdh
{
struct rt_mmcsd_host *host;
char *name;
SDH_T *base;
IRQn_Type irqn;
uint32_t rstidx;
uint32_t modid;
uint8_t *cachebuf;
struct rt_event event;
};
typedef struct nu_sdh *nu_sdh_t;
/* Private variables ------------------------------------------------------------*/
#if defined(BSP_USING_SDH0)
ALIGN(SDH_ALIGN_LEN)
static uint8_t g_au8CacheBuf_SDH0[SDH_BUFF_SIZE];
#endif
#if defined(BSP_USING_SDH1)
ALIGN(SDH_ALIGN_LEN)
static uint8_t g_au8CacheBuf_SDH1[SDH_BUFF_SIZE];
#endif
static struct nu_sdh nu_sdh_arr [] =
{
#if defined(BSP_USING_SDH0)
{
.name = "sdh0",
.base = SDH0,
.irqn = SDH0_IRQn,
.rstidx = SDH0_RST,
.modid = SDH0_MODULE,
.cachebuf = g_au8CacheBuf_SDH0,
},
#endif
#if defined(BSP_USING_SDH1)
{
.name = "sdh1",
.base = SDH1,
.irqn = SDH1_IRQn,
.rstidx = SDH1_RST,
.modid = SDH1_MODULE,
.cachebuf = g_au8CacheBuf_SDH1,
},
#endif
}; /* struct nu_sdh nu_sdh_arr [] */
static uint32_t nu_sdh_get_cmd_resptype(uint32_t rt_resp_type)
{
uint32_t nu_resptype = 0;
switch (rt_resp_type)
{
case RESP_NONE:
nu_resptype = MMC_RSP_NONE;
break;
case RESP_R1:
nu_resptype = MMC_RSP_R1;
break;
case RESP_R1B:
nu_resptype = MMC_RSP_R1b;
break;
case RESP_R2:
nu_resptype = MMC_RSP_R2;
break;
case RESP_R3:
nu_resptype = MMC_RSP_R3;
break;
case RESP_R4:
nu_resptype = MMC_RSP_R4;
break;
case RESP_R6:
nu_resptype = MMC_RSP_R6;
break;
case RESP_R7:
nu_resptype = MMC_RSP_R7;
break;
case RESP_R5:
nu_resptype = MMC_RSP_R5;
break;
default:
nu_resptype = 0xffffffff;
}
return nu_resptype ;
}
static void nu_sdh_send_commanddone(SDH_T *sdh, struct mmc_cmd *cmd)
{
if (cmd->resp_type & MMC_RSP_136)
{
/* CRC is stripped so we need to do some shifting. */
cmd->response[0] = (sdh->RESP67 << 8) | sdh->S_RESP45.B3;
cmd->response[1] = (sdh->RESP45 << 8) | sdh->S_RESP23.B3;
cmd->response[2] = (sdh->RESP23 << 8) | sdh->S_RESP01.B3;
cmd->response[3] = (sdh->RESP01 << 8);
}
else
{
cmd->response[0] = sdh->RESP01;
cmd->response[1] = cmd->response[2] = cmd->response[3] = 0;
}
}
static int nu_sdh_xfer_data(SDH_T *sdh, struct mmc_data *data)
{
uint32_t start_addr, timeout;
if (data->flags & DATA_DIR_READ)
{
start_addr = (uint32_t)data->dest;
}
else
{
start_addr = (uint32_t)data->src;
}
timeout = 1000000;
while (!sdh->S_NORMAL_INT_STAT.XFER_COMPLETE) /* SDHCI_INT_DATA_END? */
{
if (sdh->S_NORMAL_INT_STAT.ERR_INTERRUPT == 1)
return -1;
if (sdh->S_NORMAL_INT_STAT.DMA_INTERRUPT) /* SDHCI_INT_DMA_END */
{
sdh->S_NORMAL_INT_STAT.DMA_INTERRUPT = 1; /* Clear SDHCI_INT_DMA_END */
start_addr &= ~(SDH_BLOCK_SIZE * 1024 - 1);
start_addr += SDH_BLOCK_SIZE * 1024;
sdh->SDMASA = start_addr;
}
if (timeout-- > 0)
rt_hw_us_delay(10);
else
return -2;
}
return 0;
}
static void nu_sdh_list_errors(SDH_T *sdh)
{
if (sdh->S_NORMAL_INT_STAT.ERR_INTERRUPT)
{
LOG_D("Error List:");
if (sdh->S_ERROR_INT_STAT.CMD_TOUT_ERR)
LOG_D("\tCMD_TOUT_ERR.");
if (sdh->S_ERROR_INT_STAT.CMD_CRC_ERR)
LOG_D("\tCMD_CRC_ERR.");
if (sdh->S_ERROR_INT_STAT.CMD_END_BIT_ERR)
LOG_D("\tCMD_END_BIT_ERR.");
if (sdh->S_ERROR_INT_STAT.CMD_IDX_ERR)
LOG_D("\tCMD_IDX_ERR.");
if (sdh->S_ERROR_INT_STAT.DATA_TOUT_ERR)
LOG_D("\tDATA_TOUT_ERR.");
if (sdh->S_ERROR_INT_STAT.DATA_CRC_ERR)
LOG_D("\tDATA_CRC_ERR.");
if (sdh->S_ERROR_INT_STAT.DATA_END_BIT_ERR)
LOG_D("\tDATA_END_BIT_ERR.");
if (sdh->S_ERROR_INT_STAT.CUR_LMT_ERR)
LOG_D("\tCUR_LMT_ERR.");
if (sdh->S_ERROR_INT_STAT.AUTO_CMD_ERR)
LOG_D("\tAUTO_CMD_ERR.");
if (sdh->S_ERROR_INT_STAT.ADMA_ERR)
LOG_D("\tADMA_ERR.");
if (sdh->S_ERROR_INT_STAT.TUNING_ERR)
LOG_D("\tTUNING_ERR.");
if (sdh->S_ERROR_INT_STAT.RESP_ERR)
LOG_D("\tRESP_ERR.");
if (sdh->S_ERROR_INT_STAT.BOOT_ACK_ERR)
LOG_D("\tBOOT_ACK_ERR.");
if (sdh->S_ERROR_INT_STAT.VENDOR_ERR1)
LOG_D("\tVENDOR_ERR1.");
if (sdh->S_ERROR_INT_STAT.VENDOR_ERR2)
LOG_D("\tVENDOR_ERR2.");
if (sdh->S_ERROR_INT_STAT.VENDOR_ERR3)
LOG_D("\tVENDOR_ERR3.");
}
}
/**
* @brief This function send command.
* @param sdio rthw_sdio
* @param pkg sdio package
* @retval None
*/
static int nu_sdh_send_command(SDH_T *sdh, struct mmc_cmd *cmd, struct mmc_data *data)
{
int ret;
uint32_t mask, flags, mode;
volatile unsigned int time = 0;
volatile unsigned int cmd_timeout, stat;
LOG_D("[CMD:%d ARG:0x%08x] RESP_TYPE:0x%08x rw:%c addr:0x%08x len:%d blksize:%d",
cmd->cmdidx,
cmd->cmdarg,
cmd->resp_type,
data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
data ? data->src : 0,
data ? data->blocks * data->blocksize : 0,
data ? data->blocksize : 0);
mask = 0x3; /* SDH_CMD_INHIBIT | SDH_DATA_INHIBIT */
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
mask &= ~0x2; /* SDH_DATA_INHIBIT */
ret = SD_GetBusStatus(sdh, mask);
if (ret)
{
LOG_E("ERROR: Busy %d\n", ret);
ret = __LINE__;
goto exit_nu_sdh_send_command;
}
/* SDHCI_INT_ALL_MASK */
sdh->NORMAL_INT_STAT = 0xFFFF;
sdh->ERROR_INT_STAT = 0xFFFF;
mask = 0x1; /* SDHCI_INT_RESPONSE */
if (!(cmd->resp_type & MMC_RSP_PRESENT))
flags = SDH_CMD_RESP_NONE;
else if (cmd->resp_type & MMC_RSP_136)
flags = SDH_CMD_RESP_LONG;
else if (cmd->resp_type & MMC_RSP_BUSY)
{
flags = SDH_CMD_RESP_SHORT_BUSY;
if (data)
mask |= 0x2; /* SDHCI_INT_DATA_END */
}
else
flags = SDH_CMD_RESP_SHORT;
if (cmd->resp_type & MMC_RSP_CRC)
flags |= SDH_CMD_CRC;
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= SDH_CMD_INDEX;
/* Set Transfer mode regarding to data flag */
if (data)
{
flags |= SDH_CMD_DATA;
sdh->S_TOUT_CTRL.TOUT_CNT = 0xE;
mode = 0x2; /* SDHCI_TRNS_BLK_CNT_EN */
if (data->blocks > 1)
mode |= 0x20; /* SDHCI_TRNS_MULTI */
if (data->flags & DATA_DIR_READ)
{
mode |= 0x10; /* SDHCI_TRNS_READ */
sdh->SDMASA = (uint32_t)data->dest;
}
else
{
sdh->SDMASA = (uint32_t)data->src;
}
mode |= 0x1;
sdh->S_HOST_CTRL1.DMA_SEL = 0; //SDMA is selected
/* 512 Kbytes SDMA Buffer Boundary */
sdh->S_BLOCKSIZE.SDMA_BUF_BDARY = 0x7;
/* Set Block Size */
sdh->S_BLOCKSIZE.XFER_BLOCK_SIZE = data->blocksize;
/* Set Block count */
sdh->S_BLOCKCOUNT.BLOCK_CNT = data->blocks;
/* Set transfer mode */
sdh->XFER_MODE = mode;
}
else if (cmd->resp_type & MMC_RSP_BUSY)
{
sdh->S_TOUT_CTRL.TOUT_CNT = 0xE;
}
sdh->ARGUMENT = cmd->cmdarg;
sdh->CMD = ((cmd->cmdidx & 0xff) << 8) | (flags & 0xff);
cmd_timeout = 10000000;
time = 0;
do
{
stat = sdh->NORMAL_INT_STAT;
if (stat & 0x8000) /* SDHCI_INT_ERROR */
break;
if (time > cmd_timeout)
{
ret = __LINE__;
LOG_E("[%s %d] timeout stat=%04x, mask=%04x", __func__, __LINE__, stat, mask);
goto exit_nu_sdh_send_command;
}
time++;
}
while ((stat & mask) != mask);
if ((stat & (0x8000 | mask)) == mask)
{
//LOG_D("[%s %d] Done. cmdid=%d restore", __func__, __LINE__, cmd->cmdidx);
nu_sdh_send_commanddone(sdh, cmd);
nu_sdh_list_errors(sdh);
/* Send data */
if (data)
{
ret = nu_sdh_xfer_data(sdh, data);
}
stat = sdh->ERROR_INT_STAT;
sdh->NORMAL_INT_STAT = mask;
ret = 0;
}
else
{
//LOG_E("[%s %d] Error. cmdid=%d not restored %08x %08x", __func__, __LINE__, cmd->cmdidx, stat, mask);
ret = __LINE__;
nu_sdh_list_errors(sdh);
goto exit_nu_sdh_send_command;
}
/* SDHCI_INT_ALL_MASK */
sdh->NORMAL_INT_STAT = 0xFFFF;
sdh->ERROR_INT_STAT = 0xFFFF;
if (ret)
{
LOG_E("[%s %d] ret=%d cmd->cmdidx=%d, error=0x%x", __func__, __LINE__, ret, cmd->cmdidx, stat);
ret = __LINE__;
goto exit_nu_sdh_send_command;
}
return 0;
exit_nu_sdh_send_command:
SDH_Reset(sdh, SDH_RESET_CMD);
SDH_Reset(sdh, SDH_RESET_DATA);
//LOG_E("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmdidx, ret);
return ret;
}
/**
* @brief This function send sdio request.
* @param host rt_mmcsd_host
* @param req request
* @retval None
*/
static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
{
nu_sdh_t sdh = (nu_sdh_t)host->private_data;
RT_ASSERT(host);
RT_ASSERT(req);
if (host->card)
{
if (host->card->card_type == CARD_TYPE_MMC)
{
sdh->base->S_EMMC_CTRL.CARD_IS_EMMC = 1;
sdh->base->S_EMMC_CTRL.DISABLE_DATA_CRC_CHK = 1;
}
else
{
sdh->base->S_EMMC_CTRL.CARD_IS_EMMC = 0;
sdh->base->S_EMMC_CTRL.DISABLE_DATA_CRC_CHK = 0;
}
}
if (req->cmd != RT_NULL)
{
struct mmc_cmd cmd;
LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:0x%08x, 0x%08x",
(host->card == RT_NULL) ? "Unknown" : "",
(host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "",
(host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "",
req->cmd->cmd_code,
req->cmd->arg,
resp_type(req->cmd),
nu_sdh_get_cmd_resptype(resp_type(req->cmd)));
rt_memset(&cmd, 0, sizeof(struct mmc_cmd));
cmd.cmdidx = req->cmd->cmd_code;
cmd.cmdarg = req->cmd->arg;
cmd.resp_type = nu_sdh_get_cmd_resptype(resp_type(req->cmd));
if (req->data != RT_NULL)
{
struct mmc_data data;
rt_uint32_t size;
rt_int32_t IsNonaligned = 0;
LOG_D("[%s]REQ: BUF:%08x FLAGS:0x%08x BLKSIZE:%d, BLKCOUNT:%d",
sdh->name,
req->data->buf,
req->data->flags,
req->data->blksize,
req->data->blks);
rt_memset(&data, 0, sizeof(struct mmc_data));
data.dest = (char *)req->data->buf;
data.flags = req->data->flags;
data.blocksize = req->data->blksize;
data.blocks = req->data->blks;
size = data.blocksize * data.blocks;
RT_ASSERT(size <= SDH_BUFF_SIZE);
IsNonaligned = (((rt_uint32_t)data.dest & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0;
if (IsNonaligned)
{
data.dest = (char *)sdh->cachebuf;
if (data.flags & DATA_DIR_WRITE)
{
LOG_D("Un-aligned, prepare into cache buf(%d)", size);
rt_memcpy(data.dest, req->data->buf, size);
}
}
rt_hw_cpu_dcache_clean_and_invalidate((void *)data.dest, size);
req->cmd->err = nu_sdh_send_command(sdh->base, &cmd, &data);
rt_hw_cpu_dcache_invalidate((void *)data.dest, size);
if (!req->cmd->err && IsNonaligned)
{
if (data.flags & DATA_DIR_READ)
{
LOG_D("Un-aligned, restore from cache buf(%d)", size);
rt_memcpy(req->data->buf, data.dest, size);
}
}
LOG_HEX("data.dest", 16, (void *)data.dest, size);
}
else
{
req->cmd->err = nu_sdh_send_command(sdh->base, &cmd, NULL);
}
/* Report response words */
req->cmd->resp[3] = cmd.response[3];
req->cmd->resp[2] = cmd.response[2];
req->cmd->resp[1] = cmd.response[1];
req->cmd->resp[0] = cmd.response[0];
LOG_HEX("req->cmd->resp", 16, (void *)&req->cmd->resp[0], 16);
}
if (req->stop != RT_NULL)
{
struct mmc_cmd stop;
rt_memset(&stop, 0, sizeof(struct mmc_cmd));
stop.cmdidx = req->stop->cmd_code;
stop.cmdarg = req->stop->arg;
stop.resp_type = nu_sdh_get_cmd_resptype(resp_type(req->stop));
req->stop->err = nu_sdh_send_command(sdh->base, &stop, NULL);
/* Report response words */
req->stop->resp[3] = stop.response[3];
req->stop->resp[2] = stop.response[2];
req->stop->resp[1] = stop.response[1];
req->stop->resp[0] = stop.response[0];
LOG_HEX("req->stop->resp", 16, (void *)&req->stop->resp[0], 16);
}
mmcsd_req_complete(host);
}
/**
* @brief This function config sdio.
* @param host rt_mmcsd_host
* @param io_cfg rt_mmcsd_io_cfg
* @retval None
*/
static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
{
nu_sdh_t NuSdh;
rt_uint32_t clk = io_cfg->clock;
SDH_T *sdh;
RT_ASSERT(host);
RT_ASSERT(io_cfg);
NuSdh = (nu_sdh_t)host->private_data;
sdh = NuSdh->base;
LOG_D("[%s]clk:%d width:%s%s%s power:%s%s%s",
NuSdh->name,
clk,
io_cfg->bus_width == MMCSD_BUS_WIDTH_8 ? "8" : "",
io_cfg->bus_width == MMCSD_BUS_WIDTH_4 ? "4" : "",
io_cfg->bus_width == MMCSD_BUS_WIDTH_1 ? "1" : "",
io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : "");
/* Bus width */
switch (io_cfg->bus_width)
{
case MMCSD_BUS_WIDTH_1:
case MMCSD_BUS_WIDTH_4:
case MMCSD_BUS_WIDTH_8:
SDH_SetBusWidth(sdh, 1 << io_cfg->bus_width);
break;
default:
break;
}
/* Power */
switch (io_cfg->power_mode)
{
case MMCSD_POWER_UP:
case MMCSD_POWER_ON:
SDH_SetPower(sdh, 1);
break;
case MMCSD_POWER_OFF:
SDH_SetPower(sdh, 0);
break;
default:
break;
}
/* Clock */
if (clk > host->freq_max)
clk = host->freq_max;
if (clk < host->freq_min)
clk = host->freq_min;
if (clk)
{
uint32_t u32SrcFreqInHz = 0, u32ModRealFreqInHz;
uint32_t u32ModSrcIdx = CLK_GetModuleClockSource(NuSdh->modid);
switch (u32ModSrcIdx)
{
case 0: // From SYSPLL
u32SrcFreqInHz = CLK_GetPLLClockFreq(SYSPLL);
break;
default: // From APLL
u32SrcFreqInHz = CLK_GetPLLClockFreq(APLL);
break;
}
u32ModRealFreqInHz = SDH_SetClock(sdh, u32SrcFreqInHz, clk);
u32ModRealFreqInHz = u32ModRealFreqInHz; //Avoid warning
LOG_D("[%s] SrcClock: %d kHz, ExceptedFreq: %d kHz, RealFreq: %d kHz", NuSdh->name, u32SrcFreqInHz / 1000, clk / 1000, u32ModRealFreqInHz / 1000);
}
}
/**
* @brief This function detect sdcard.
* @param host rt_mmcsd_host
* @retval 0x01
*/
static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host)
{
LOG_D("try to detect device");
return 0x01;
}
/**
* @brief This function interrupt process function.
* @param host rt_mmcsd_host
* @retval None
*/
static void nu_sdh_isr(int vector, void *param)
{
nu_sdh_t sdh = (nu_sdh_t)param;
struct rt_mmcsd_host *host = sdh->host;
SDH_T *base = sdh->base;
volatile unsigned int isr = base->NORMAL_INT_STAT;
/* We just catch card detection here. */
if (isr & 0xc0)
{
/* ready to change */
mmcsd_change(host);
base->NORMAL_INT_STAT = 0xC0;
}
}
/**
* @brief This function update sdh interrupt.
* @param host rt_mmcsd_host
* @param enable
* @retval None
*/
void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable)
{
nu_sdh_t sdh = (nu_sdh_t)host->private_data;
SDH_T *sdh_base = sdh->base;
if (enable)
{
LOG_D("Enable %s irq", sdh->name);
/* Enable only interrupts served by the SD controller */
/* sdh_base->NORMAL_INT_STAT_EN = 0x00FB; */
sdh_base->S_NORMAL_INT_STAT_EN.CMD_COMPLETE_STAT_EN = 1;
sdh_base->S_NORMAL_INT_STAT_EN.XFER_COMPLETE_STAT_EN = 1;
sdh_base->S_NORMAL_INT_STAT_EN.DMA_INTERRUPT_STAT_EN = 1;
sdh_base->S_NORMAL_INT_STAT_EN.BUF_WR_READY_STAT_EN = 1;
sdh_base->S_NORMAL_INT_STAT_EN.BUF_RD_READY_STAT_EN = 1;
sdh_base->S_NORMAL_INT_STAT_EN.CARD_INSERTION_STAT_EN = 1;
sdh_base->S_NORMAL_INT_STAT_EN.CARD_REMOVAL_STAT_EN = 1;
/* sdh_base->ERROR_INT_STAT_EN = 0x0271; */
sdh_base->S_ERROR_INT_STAT_EN.CMD_TOUT_ERR_STAT_EN = 1;
sdh_base->S_ERROR_INT_STAT_EN.DATA_TOUT_ERR_STAT_EN = 1;
sdh_base->S_ERROR_INT_STAT_EN.DATA_CRC_ERR_STAT_EN = 1;
sdh_base->S_ERROR_INT_STAT_EN.DATA_END_BIT_ERR_STAT_EN = 1;
sdh_base->S_ERROR_INT_STAT_EN.ADMA_ERR_STAT_EN = 1;
/* Mask all interrupt sources */
/* sdh_base->NORMAL_INT_SIGNAL_EN = 0xC0; */
sdh_base->S_NORMAL_INT_SIGNAL_EN.CARD_INSERTION_SIGNAL_EN = 1;
sdh_base->S_NORMAL_INT_SIGNAL_EN.CARD_REMOVAL_SIGNAL_EN = 1;
sdh_base->ERROR_INT_SIGNAL_EN = 0;
//sdh_base->NORMAL_INT_STAT_EN = 0x7FFF;
//sdh_base->ERROR_INT_STAT_EN = 0xFFFF;
//sdh_base->NORMAL_INT_SIGNAL_EN=0x7FFF;
//sdh_base->ERROR_INT_SIGNAL_EN=0xFFFF;
}
else
{
LOG_D("Disable %s irq", sdh->name);
sdh_base->NORMAL_INT_STAT_EN = 0x0;
sdh_base->ERROR_INT_STAT_EN = 0x0;
sdh_base->NORMAL_INT_SIGNAL_EN = 0x0;
sdh_base->ERROR_INT_SIGNAL_EN = 0x0;
}
}
static const struct rt_mmcsd_host_ops ops =
{
nu_sdh_request,
nu_sdh_iocfg,
nu_sdh_card_detect,
nu_sdh_irq_update,
};
/**
* @brief This function create mmcsd host.
* @param sdh nu_sdh_t
* @retval nuvton
*/
void nu_sdh_host_initial(nu_sdh_t sdh)
{
struct rt_mmcsd_host *host;
rt_err_t ret = RT_EOK;
host = mmcsd_alloc_host();
RT_ASSERT(host != RT_NULL);
ret = rt_event_init(&sdh->event, "sdh_event", RT_IPC_FLAG_FIFO);
RT_ASSERT(ret == RT_EOK);
/* Reset sdh at first. */
SDH_Reset(sdh->base, SDH_RESET_ALL);
/* set host default attributes */
host->ops = &ops;
host->freq_min = 200 * 1000;
host->freq_max = 50 * 1000 * 1000;
host->valid_ocr = VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34; // | VDD_165_195;
host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ | MMCSD_SUP_HIGHSPEED;
host->max_seg_size = SDH_BUFF_SIZE;
host->max_dma_segs = 1;
host->max_blk_size = SDH_BLOCK_SIZE;
host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE);
/* link up host and sdio */
host->private_data = sdh;
sdh->host = host;
/* Set initial state: high speed */
sdh->base->S_HOST_CTRL1.HIGH_SPEED_EN = 1;
/* Set SDR50 mode */
sdh->base->S_HOST_CTRL2.UHS_MODE_SEL = 2;
/* Install ISR. */
rt_hw_interrupt_install(sdh->irqn, nu_sdh_isr, (void *)sdh, sdh->name);
rt_hw_interrupt_umask(sdh->irqn);
/* Enable interrupt. */
nu_sdh_irq_update(host, 1);
/* ready to change */
//mmcsd_change(host);
}
void nu_sd_attach(void)
{
int i;
/* ready to change */
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
if (nu_sdh_arr[i].host)
mmcsd_change(nu_sdh_arr[i].host);
}
}
MSH_CMD_EXPORT(nu_sd_attach, attach card);
void nu_sd_regdump(void)
{
int i;
/* ready to change */
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
if (nu_sdh_arr[i].host)
SDH_DumpReg(nu_sdh_arr[i].base);
}
}
MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers);
static int rt_hw_sdh_init(void)
{
int i;
for (i = (SDH_START + 1); i < SDH_CNT; i++)
{
CLK_EnableModuleClock(nu_sdh_arr[i].modid);
SYS_ResetModule(nu_sdh_arr[i].rstidx);
nu_sdh_host_initial(&nu_sdh_arr[i]);
}
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_sdh_init);
/* A simple MBR writer. */
struct mbr
{
uint8_t code[440];
union
{
uint32_t disk_signature;
struct
{
uint32_t disk_signature_B0: 8;
uint32_t disk_signature_B1: 8;
uint32_t disk_signature_B2: 8;
uint32_t disk_signature_B3: 8;
} s_disk_signature;
};
uint16_t unused;
struct mbr_partition
{
uint8_t status;
uint8_t first_cylinder;
uint8_t first_head;
uint8_t first_sector;
uint8_t partition_type;
uint8_t last_cylinder;
uint8_t last_head;
uint8_t last_sector;
union
{
uint32_t first_sector_lba;
struct
{
uint32_t first_sector_lba_B0: 8;
uint32_t first_sector_lba_B1: 8;
uint32_t first_sector_lba_B2: 8;
uint32_t first_sector_lba_B3: 8;
} s_first_sector_lba;
};
union
{
uint32_t sectors ;
struct
{
uint32_t sectors_B0: 8;
uint32_t sectors_B1: 8;
uint32_t sectors_B2: 8;
uint32_t sectors_B3: 8;
} s_sectors;
};
} partition[4];
uint16_t mbr_signature;
} __attribute__((packed));
#define MBR_SIGNATURE 0xAA55
#define MBR_STATUS_BOOTABLE 0x80
#define le2(a, o) ((a)[o] << 0 | (a)[(o)+1] << 8)
#define le4(a, o) (le2(a,o) | (a)[(o)+2] << 16 | (a)[(o)+3] << 24)
static int nu_sd_mbr_read(const char *devname)
{
rt_device_t mmc_device = RT_NULL;
rt_err_t ret;
struct rt_device_blk_geometry geo = {0};
struct mbr *psMbr = RT_NULL;
if ((mmc_device = rt_device_find(devname)) == RT_NULL)
{
LOG_E("find device %s failed!\n", devname);
goto fail_nu_sd_mbr_read;
}
if (rt_device_open(mmc_device, RT_DEVICE_OFLAG_RDWR) != RT_EOK)
{
LOG_E("open device %s failed!\n", devname);
mmc_device = RT_NULL;
goto fail_nu_sd_mbr_read;
}
if (rt_device_control(mmc_device, RT_DEVICE_CTRL_BLK_GETGEOME, &geo) != RT_EOK)
{
LOG_E("control device %s failed!\n", devname);
goto fail_nu_sd_mbr_read;
}
LOG_I("device information:\n");
LOG_I("sector size : %d byte\n", geo.bytes_per_sector);
LOG_I("sector count : %d \n", geo.sector_count);
LOG_I("block size : %d byte\n", geo.block_size);
LOG_I("MBR size : %d byte\n", sizeof(struct mbr));
psMbr = rt_malloc(sizeof(struct mbr));
if (psMbr == RT_NULL)
{
LOG_E("no memory for mbr buffer!\n");
goto fail_nu_sd_mbr_read;
}
rt_memset(psMbr, 0, sizeof(struct mbr));
ret = rt_device_read(mmc_device, 0, psMbr, 1);
if (ret != 1)
{
LOG_E("read device %s %d failed!\n", devname, ret);
goto fail_nu_sd_mbr_read;
}
LOG_I("disk_signature = %08x\n", psMbr->disk_signature);
LOG_I("unused = %02x\n", psMbr->unused);
for (int i = 0; i < 4; i++)
{
LOG_I("[%d] status = %02x\n", i, psMbr->partition[i].status);
LOG_I("[%d] first_cylinder = %d\n", i, psMbr->partition[i].first_cylinder);
LOG_I("[%d] first_head = %d\n", i, psMbr->partition[i].first_head);
LOG_I("[%d] first_sector = %d\n", i, psMbr->partition[i].first_sector);
LOG_I("[%d] partition_type = %02x\n", i, psMbr->partition[i].partition_type);
LOG_I("[%d] last_cylinder = %d\n", i, psMbr->partition[i].last_cylinder);
LOG_I("[%d] last_head = %d\n", i, psMbr->partition[i].last_head);
LOG_I("[%d] last_sector = %d\n", i, psMbr->partition[i].last_sector);
LOG_I("[%d] first_sector_lba = %u\n", i, psMbr->partition[i].first_sector_lba);
LOG_I("[%d] sectors = %u\n", i, psMbr->partition[i].sectors);
}
LOG_I("signature = %02x\n", psMbr->mbr_signature);
rt_free(psMbr);
rt_device_close(mmc_device);
return 0;
fail_nu_sd_mbr_read:
if (psMbr != RT_NULL)
rt_free(psMbr);
if (mmc_device != RT_NULL)
rt_device_close(mmc_device);
return -1;
}
int nu_sd_mbr_dump(int argc, char *argv[])
{
// argc=1: string, mmcblk device name.
if (argc != 2)
return -1;
return nu_sd_mbr_read(argv[1]);
}
MSH_CMD_EXPORT(nu_sd_mbr_dump, dump sd card device);
static int nu_sd_mbr_write(const char *devname, int32_t u32Sectors)
{
rt_device_t mmc_device = RT_NULL;
rt_err_t ret;
struct rt_device_blk_geometry geo;
struct mbr *psMbr = RT_NULL;
if ((mmc_device = rt_device_find(devname)) == RT_NULL)
{
LOG_E("find device %s failed!\n", devname);
goto fail_nu_sd_mbr_write;
}
if (rt_device_open(mmc_device, RT_DEVICE_OFLAG_RDWR) != RT_EOK)
{
mmc_device = RT_NULL;
LOG_E("open device %s failed!\n", devname);
goto fail_nu_sd_mbr_write;
}
rt_memset(&geo, 0, sizeof(geo));
ret = rt_device_control(mmc_device, RT_DEVICE_CTRL_BLK_GETGEOME, &geo);
if (ret != RT_EOK)
{
LOG_E("control device %s failed!\n", devname);
goto fail_nu_sd_mbr_write;
}
LOG_I("device information:\n");
LOG_I("sector size : %d byte\n", geo.bytes_per_sector);
LOG_I("sector count : %d \n", geo.sector_count);
LOG_I("block size : %d byte\n", geo.block_size);
if (u32Sectors >= geo.sector_count)
{
LOG_E("no enough sectors for reserved. %s failed!\n", devname);
goto fail_nu_sd_mbr_write;
}
psMbr = rt_malloc(sizeof(struct mbr));
if (psMbr == RT_NULL)
{
LOG_E("no memory for mbr buffer!\n");
goto fail_nu_sd_mbr_write;
}
rt_memset(psMbr, 0, sizeof(struct mbr));
psMbr->disk_signature = 0xa8e7d068;
psMbr->mbr_signature = MBR_SIGNATURE;
psMbr->partition[0].status = 0;
//psMbr->partition[0].first_cylinder = 0x00;
//psMbr->partition[0].first_head = 0x21;
//psMbr->partition[0].first_sector = 0x21;
psMbr->partition[0].partition_type = 0x0C;
//psMbr->partition[0].last_head = 0xFE;
//psMbr->partition[0].last_sector = 0x3F;
//psMbr->partition[0].last_cylinder = 0xFF;
psMbr->partition[0].first_sector_lba = u32Sectors;
psMbr->partition[0].sectors = geo.sector_count - u32Sectors;
ret = rt_device_write(mmc_device, 0, psMbr, 1);
if (ret != 1)
{
LOG_E("write device %s %d failed!\n", devname, ret);
goto fail_nu_sd_mbr_write;
}
fail_nu_sd_mbr_write:
if (psMbr != RT_NULL)
rt_free(psMbr);
if (mmc_device)
rt_device_close(mmc_device);
return -1;
}
int nu_sd_mbr_layout(int argc, char *argv[])
{
// argc=1: string, mmcblk device name.
// argc=2: Reserved sectors for bootable code and remains sectors are for elm mounting.
if (argc != 3)
return -1;
return nu_sd_mbr_write((const char *)argv[1], atoi(argv[2]));
}
MSH_CMD_EXPORT(nu_sd_mbr_layout, layout sd device);
#endif