Browse Source

Merge pull request #1372 from uestczyh222/master

[Bsp][Tina]Add SDIO Driver
ZYH 7 years ago
parent
commit
da704e695c

+ 6 - 0
bsp/allwinner_tina/drivers/Kconfig

@@ -14,3 +14,9 @@ config TINA_USING_UART2
     bool "Using UART2"
     select RT_USING_SERIAL
     default y
+
+config TINA_USING_SDIO0
+    bool "Using SDIO0"
+    select RT_USING_SDIO
+    default y
+    

+ 795 - 0
bsp/allwinner_tina/drivers/drv_sdio.c

@@ -0,0 +1,795 @@
+/*
+ * File      : drv_sdio.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2017, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-02-08     RT-Thread    the first version
+ */
+#include <rtthread.h>
+#include <rthw.h>
+#include <rtdevice.h>
+#include <string.h>
+
+#include "drv_sdio.h"
+#include "interrupt.h"
+#include "mmu.h"
+#include "drv_gpio.h"
+#include "drv_clock.h"
+
+#define DBG_ENABLE
+#define DBG_SECTION_NAME  "[MMC]"
+// #define DBG_LEVEL DBG_LOG    
+// #define DBG_LEVEL DBG_INFO   
+#define DBG_LEVEL DBG_WARNING
+// #define DBG_LEVEL DBG_ERROR  
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#ifdef RT_USING_SDIO
+#define CONFIG_MMC_USE_DMA
+#define DMA_ALIGN       (32U)
+
+struct mmc_xfe_des
+{
+    rt_uint32_t size;    /* block size  */
+	rt_uint32_t num;     /* block num   */
+	rt_uint8_t *buff;    /* buff addr   */
+	rt_uint32_t flag;    /* write or read or stream */
+#define MMC_DATA_WRITE	(1 << 0)
+#define MMC_DATA_READ	(1 << 1)
+#define MMC_DATA_STREAM	(1 << 2)
+};
+
+struct mmc_flag
+{
+    volatile rt_uint32_t risr;
+    volatile rt_uint32_t idst;
+};
+
+struct sdio_drv
+{
+    struct rt_mmcsd_host *host;
+    struct rt_mmcsd_req  *req;
+    struct rt_semaphore rt_sem;
+    struct mmc_xfe_des xfe;
+    struct mmc_flag flag;
+    tina_mmc_t mmc_des;
+    rt_uint8_t *mmc_buf;
+    rt_uint8_t usedma;
+    
+};
+
+#ifdef CONFIG_MMC_USE_DMA
+#ifdef TINA_USING_SDIO0
+ALIGN(32) static rt_uint8_t dma_buffer[64 * 1024];
+#endif
+#endif
+
+static void mmc_request_end(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req);
+
+static void mmc_delay_us(int us)
+{
+    volatile unsigned int temp;
+
+    while (us--)
+    {
+        temp = 0x2f;
+        while (temp--)
+        {
+            temp = temp;
+        };
+    }
+}
+
+static void mmc_dump_errinfo(unsigned int err)
+{
+    dbg_log(DBG_ERROR, "[err]:0x%08x, %s%s%s%s%s%s%s%s%s%s%s\n",
+               err,
+               err & SDXC_RespErr     ? " RE"     : "",
+               err & SDXC_RespCRCErr  ? " RCE"    : "",
+               err & SDXC_DataCRCErr  ? " DCE"    : "",
+               err & SDXC_RespTimeout ? " RTO"    : "",
+               err & SDXC_DataTimeout ? " DTO"    : "",
+               err & SDXC_DataStarve  ? " DS"     : "",
+               err & SDXC_FIFORunErr  ? " FE"     : "",
+               err & SDXC_HardWLocked ? " HL"     : "",
+               err & SDXC_StartBitErr ? " SBE"    : "",
+               err & SDXC_EndBitErr   ? " EBE"    : "",
+               err == 0  ? " STO"    : ""
+              );
+}
+
+static int mmc_update_clk(tina_mmc_t mmc)
+{
+    rt_uint32_t cmd;
+    rt_uint32_t timeout = 2000000;
+
+    /* cmd load */
+    cmd = SDXC_LOAD_CMD | SDXC_UPDATE_CLOCK_CMD | SDXC_WAIT_OVER_CMD;
+    mmc->cmdr_reg = cmd;
+    /* while load success */
+    while ((mmc->cmdr_reg & SDXC_LOAD_CMD) && (--timeout))
+    {
+        mmc_delay_us(1);
+    }
+    if (!timeout)
+    {
+        dbg_log(DBG_ERROR, "mmc update clk failed\n");
+        return -RT_ERROR;
+    }
+    /* clean interrupt */
+    mmc->risr_reg = mmc->risr_reg;
+    return RT_EOK;
+}
+    
+static rt_err_t mmc_trans_data_by_dma(tina_mmc_t mmc, struct mmc_xfe_des *xfe)
+{
+    ALIGN(32) static struct mmc_des_v4p1 pdes[128];  // mast ALIGN(32)
+    unsigned i, rval;
+    unsigned des_idx;
+    unsigned length = xfe->size * xfe->num;
+    unsigned buff_frag_num = length >> SDXC_DES_NUM_SHIFT;
+    unsigned remain = length & (SDXC_DES_BUFFER_MAX_LEN - 1);
+    
+    if (remain)
+    {
+        buff_frag_num ++;
+    }
+    else
+    {
+        remain = SDXC_DES_BUFFER_MAX_LEN;
+    }
+    memset(pdes, 0, sizeof(pdes));
+    mmu_clean_dcache((rt_uint32_t)(xfe->buff), length);
+    for (i = 0, des_idx = 0; i < buff_frag_num; i++, des_idx++) 
+    {
+        // memset((void*)&pdes[des_idx], 0, sizeof(struct mmc_v4p1));
+        pdes[des_idx].des_chain = 1;
+        pdes[des_idx].own = 1;
+        pdes[des_idx].dic = 1;
+        if ((buff_frag_num > 1) && (i != buff_frag_num - 1))
+        {
+            pdes[des_idx].data_buf1_sz = SDXC_DES_BUFFER_MAX_LEN;
+        }
+        else
+        {
+            pdes[des_idx].data_buf1_sz = remain;
+        }
+        pdes[des_idx].buf_addr_ptr1 = (unsigned long)(xfe->buff) + i * SDXC_DES_BUFFER_MAX_LEN;
+        if (i == 0)
+        {
+            pdes[des_idx].first_des = 1;
+        }
+
+        if (i == (buff_frag_num - 1))
+        {
+            pdes[des_idx].dic = 0;
+            pdes[des_idx].last_des = 1;
+            pdes[des_idx].end_of_ring = 1;
+            pdes[des_idx].buf_addr_ptr2 = 0;
+        } 
+        else 
+        {
+            pdes[des_idx].buf_addr_ptr2 = (unsigned long)&pdes[des_idx+1];
+        }
+
+        dbg_log(DBG_LOG, "frag %d, remain %d, des[%d](%08x): " \
+            "[0] = %08x, [1] = %08x, [2] = %08x, [3] = %08x\n", \
+            i, remain, des_idx, (unsigned int)&pdes[des_idx],
+            (unsigned int)((unsigned int*)&pdes[des_idx])[0], (unsigned int)((unsigned int*)&pdes[des_idx])[1],
+            (unsigned int)((unsigned int*)&pdes[des_idx])[2], (unsigned int)((unsigned int*)&pdes[des_idx])[3]);
+    }
+    mmu_clean_dcache((rt_uint32_t)pdes, sizeof(struct mmc_des_v4p1) * (des_idx + 1));
+
+    /*
+     * GCTRLREG
+     * GCTRL[2] : DMA reset
+     * GCTRL[5] : DMA enable
+     *
+     * IDMACREG
+     * IDMAC[0] : IDMA soft reset
+     * IDMAC[1] : IDMA fix burst flag
+     * IDMAC[7] : IDMA on
+     *
+     * IDIECREG
+     * IDIE[0]  : IDMA transmit interrupt flag
+     * IDIE[1]  : IDMA receive interrupt flag
+     */
+    rval = mmc->gctl_reg;
+    mmc->gctl_reg = rval | (1 << 5) | (1 << 2); /* dma enable           */
+    mmc->dmac_reg = (1 << 0);                   /* idma reset           */
+    while(mmc->dmac_reg & 0x1) {};              /* wait idma reset done */
+    mmc->dmac_reg = (1 << 1) | (1 << 7);        /* idma on              */
+    rval = mmc->idie_reg & (~3);
+    if (xfe->flag == MMC_DATA_WRITE)
+        rval |= (1 << 0);        
+    else
+        rval |= (1 << 1);
+    mmc->idie_reg = rval;
+    mmc->dlba_reg = (unsigned long)pdes;
+    mmc->fwlr_reg = (2U << 28) | (7U << 16) | 8;
+
+    return 0;
+}
+
+static rt_err_t mmc_trans_data_by_cpu(tina_mmc_t mmc, struct mmc_xfe_des *xfe)
+{
+    unsigned i;
+    unsigned byte_cnt = xfe->size * xfe->num;
+    unsigned *buff = (unsigned *)(xfe->buff);
+    volatile unsigned timeout = 2000000;
+
+    if (xfe->flag == MMC_DATA_WRITE)
+    {
+        for (i = 0; i < (byte_cnt >> 2); i++) 
+        {
+            while(--timeout && (mmc->star_reg & (1 << 3)));
+
+            if (timeout <= 0)
+            {
+                dbg_log(DBG_ERROR, "write data by cpu failed status:0x%08x\n", mmc->star_reg);
+                return -RT_ERROR;
+            }
+            mmc->fifo_reg = buff[i];
+            timeout = 2000000;
+        }
+    }
+    else
+    {
+        for (i = 0; i < (byte_cnt >> 2); i++) 
+        {
+            while(--timeout && (mmc->star_reg & (1 << 2)));
+
+            if (timeout <= 0)
+            {
+                dbg_log(DBG_ERROR, "read data by cpu failed status:0x%08x\n", mmc->star_reg);
+                return -RT_ERROR;
+            }
+            buff[i] = mmc->fifo_reg;
+            timeout = 2000000;
+        }
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t mmc_config_clock(tina_mmc_t mmc, int clk)
+{
+    rt_uint32_t rval = 0;
+
+    /* disable card clock */
+    rval = mmc->ckcr_reg;
+    rval &= ~(1 << 16);
+    mmc->ckcr_reg = rval;
+    if (mmc_update_clk(mmc) != RT_EOK)
+    {
+        dbg_log(DBG_ERROR, "clk update fail line:%d\n", __LINE__);
+        return -RT_ERROR;
+    }
+
+    if (mmc == MMC0)
+    {
+        mmc_set_clk(SDMMC0, clk);
+    }
+    else
+    {
+        mmc_set_clk(SDMMC1, clk);
+    }
+    
+    /* Re-enable card clock */
+    rval = mmc->ckcr_reg;
+    rval |=  (0x1 << 16); //(3 << 16);
+    mmc->ckcr_reg = rval;
+    if(mmc_update_clk(mmc) != RT_EOK)
+    {
+        dbg_log(DBG_ERROR, "clk update fail line:%d\n", __LINE__);
+        return -RT_ERROR;
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t mmc_set_ios(tina_mmc_t mmc, int clk, int bus_width)
+{
+    dbg_log(DBG_LOG, "mmc set io bus width:%d clock:%d\n", \
+        (bus_width == MMCSD_BUS_WIDTH_8 ? 8 : (bus_width == MMCSD_BUS_WIDTH_4 ? 4 : 1)), clk);
+    /* change clock */
+    if (clk && (mmc_config_clock(mmc, clk) != RT_EOK))
+    {
+        dbg_log(DBG_ERROR, "update clock failed\n");
+        return -RT_ERROR;
+    }
+
+    /* Change bus width */
+    if (bus_width == MMCSD_BUS_WIDTH_8)
+    {
+        mmc->bwdr_reg = 2;
+    }
+    else if (bus_width == MMCSD_BUS_WIDTH_4)
+    {
+        mmc->bwdr_reg = 1;
+    }
+    else
+    {
+        mmc->bwdr_reg = 0;
+    }
+
+    return RT_EOK;
+}
+
+static int mmc_send_cmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd)
+{
+
+    unsigned int cmdval  = 0x80000000;
+    signed int timeout   = 0;
+    int err              = 0;
+    unsigned int status  = 0;
+    struct rt_mmcsd_data *data = cmd->data;
+    unsigned int bytecnt = 0;
+    struct sdio_drv *sdio_des = (struct sdio_drv *)host->private_data;
+    tina_mmc_t mmc = sdio_des->mmc_des;
+
+    timeout = 5000 * 1000;
+    status = mmc->star_reg;
+    while (status & (1 << 9))
+    {
+        dbg_log(DBG_LOG, "note: check card busy\n");
+
+        status = mmc->star_reg;
+        if (!timeout--)
+        {
+            err = -1;
+            dbg_log(DBG_ERROR, "mmc cmd12 busy timeout data:0x%08x\n", status);
+            return err;
+        }
+        mmc_delay_us(1);
+    }
+    /*
+     * CMDREG
+     * CMD[5:0] : Command index
+     * CMD[6]   : Has response
+     * CMD[7]   : Long response
+     * CMD[8]   : Check response CRC
+     * CMD[9]   : Has data
+     * CMD[10]  : Write
+     * CMD[11]  : Steam mode
+     * CMD[12]  : Auto stop
+     * CMD[13]  : Wait previous over
+     * CMD[14]  : About cmd
+     * CMD[15]  : Send initialization
+     * CMD[21]  : Update clock
+     * CMD[31]  : Load cmd
+     */
+    if (!cmd->cmd_code)
+        cmdval |= (1 << 15);
+    if (resp_type(cmd) != RESP_NONE)
+        cmdval |= (1 << 6);
+    if (resp_type(cmd) == RESP_R2)
+        cmdval |= (1 << 7);
+    if ((resp_type(cmd) != RESP_R3) && (resp_type(cmd) != RESP_R4))
+        cmdval |= (1 << 8);
+    
+    if (data)
+    {
+        cmdval |= (1 << 9) | (1 << 13);
+        if (data->flags & DATA_DIR_WRITE)
+            cmdval |= (1 << 10);
+        if (data->blks > 1)
+            cmdval |= (1 << 12);
+        mmc->bksr_reg = data->blksize;
+        bytecnt = data->blksize * data->blks;
+        mmc->bycr_reg = bytecnt;
+    }
+
+    dbg_log(DBG_LOG, "cmd %d(0x%08x), arg 0x%08x\n", cmd->cmd_code, cmdval | cmd->cmd_code, cmd->arg);
+    mmc->cagr_reg = cmd->arg;
+    if (!data)
+    {
+        mmc->cmdr_reg = cmdval | cmd->cmd_code;
+        mmc->imkr_reg |= 0x1 << 2;
+    }
+
+    /*
+     * transfer data and check status
+     * STATREG[2] : FIFO empty
+     * STATREG[3] : FIFO full
+     */
+    if (data)
+    {
+        dbg_log(DBG_LOG, "mmc trans data %d bytes addr:0x%08x\n", bytecnt, data);
+#ifdef CONFIG_MMC_USE_DMA
+        if (bytecnt > 64)
+        {
+#else
+        if (0)
+        {
+#endif
+            sdio_des->usedma = 1;
+            mmc->gctl_reg = mmc->gctl_reg & (~0x80000000);
+            mmc_trans_data_by_dma(mmc, &sdio_des->xfe);
+            mmc->cmdr_reg = cmdval | cmd->cmd_code;
+        }
+        else
+        {
+            sdio_des->usedma = 0;
+            mmc->gctl_reg = mmc->gctl_reg | 0x80000000;
+            mmc->cmdr_reg = cmdval | cmd->cmd_code;
+            mmc_trans_data_by_cpu(mmc, &sdio_des->xfe);
+        }
+
+        if (data->blks > 1)
+        {
+            mmc->imkr_reg |= (0x1 << 14);
+        }
+        else
+        {
+            mmc->imkr_reg |= (0x1 << 3);
+        }
+    }
+
+    mmc->imkr_reg |= 0xbfc2;
+
+    if (data)
+    {
+        //TODO:2 * bytecnt * 4?
+        timeout = sdio_des->usedma ? (2 * bytecnt * 4) : 100; //0.04us(25M)*2(4bit width)*25()
+        if (timeout < 10)
+        {
+            timeout = 10;
+        }
+    }
+    else
+    {
+        timeout = 200;
+    }
+
+    if (rt_sem_take(&sdio_des->rt_sem, timeout) != RT_EOK)
+    {
+        err = (mmc->risr_reg | sdio_des->flag.risr) & 0xbfc2;
+        goto out;
+    }
+
+    err = (mmc->risr_reg | sdio_des->flag.risr) & 0xbfc2;
+    if (err)
+    {
+        cmd->err = -RT_ETIMEOUT;
+        goto out;
+    }
+
+    if (resp_type(cmd) == RESP_R2)
+    {
+        cmd->resp[3] = mmc->resp0_reg;
+        cmd->resp[2] = mmc->resp1_reg;
+        cmd->resp[1] = mmc->resp2_reg;
+        cmd->resp[0] = mmc->resp3_reg;
+        dbg_log(DBG_LOG, "mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                  cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+    }
+    else
+    {
+        cmd->resp[0] = mmc->resp0_reg;
+        dbg_log(DBG_LOG, "mmc resp 0x%08x\n", cmd->resp[0]);
+    }
+
+out:
+    if (err)
+    {
+        mmc_dump_errinfo(err & 0xbfc2);
+    }
+    if (data && sdio_des->usedma)
+    {
+        /* IDMASTAREG
+         * IDST[0] : idma tx int
+         * IDST[1] : idma rx int
+         * IDST[2] : idma fatal bus error
+         * IDST[4] : idma descriptor invalid
+         * IDST[5] : idma error summary
+         * IDST[8] : idma normal interrupt sumary
+         * IDST[9] : idma abnormal interrupt sumary
+         */
+        status = mmc->idst_reg;
+        mmc->idst_reg = status;
+        mmc->idie_reg = 0;
+        mmc->dmac_reg = 0;
+        mmc->gctl_reg = mmc->gctl_reg & (~(1 << 5));
+    }
+    if (err)
+    {
+        if (data && (data->flags & DATA_DIR_READ) && (bytecnt == 512))
+        {
+            mmc->gctl_reg = mmc->gctl_reg | 0x80000000;
+            mmc->dbgc_reg = 0xdeb;
+            timeout = 1000;
+            dbg_log(DBG_LOG, "Read remain data\n");
+            while (mmc->bbcr_reg < 512)
+            {
+                unsigned int tmp = mmc->fifo_reg;
+                tmp = tmp;
+                dbg_log(DBG_LOG, "Read data 0x%08x, bbcr 0x%04x\n", tmp, mmc->bbcr_reg);
+                mmc_delay_us(1);
+                if (!(timeout--))
+                {
+                    dbg_log(DBG_ERROR, "Read remain data timeout\n");
+                    break;
+                }
+            }
+        }
+
+        mmc->gctl_reg = 0x7;
+        while (mmc->gctl_reg & 0x7) { };
+
+        mmc_update_clk(mmc);
+        cmd->err = -RT_ETIMEOUT;
+        dbg_log(DBG_ERROR, "mmc cmd %d err\n", cmd->cmd_code);
+    }
+
+    mmc->gctl_reg &= ~(0x1 << 4);
+    mmc->imkr_reg &= ~0xffff;
+    mmc->risr_reg = 0xffffffff;
+    mmc->gctl_reg |= 0x1 << 4;
+    while (!rt_sem_take(&sdio_des->rt_sem, 0)) {}
+    mmc_request_end(sdio_des->host, sdio_des->req);
+
+    return err;
+}
+
+static void mmc_request_end(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
+{
+    struct rt_mmcsd_data *data;
+    unsigned byte_cnt;
+    struct sdio_drv *sdio = (struct sdio_drv *)host->private_data;
+
+#ifdef CONFIG_MMC_USE_DMA
+    data = req->cmd->data;
+    if (data)
+    {
+        byte_cnt = data->blksize * data->blks;
+        if ((byte_cnt > 64) && (data->flags & DATA_DIR_READ))
+        {
+            mmu_invalidate_dcache((rt_uint32_t)sdio->xfe.buff, (rt_uint32_t)byte_cnt);
+
+            if (((rt_uint32_t)data->buf) & (DMA_ALIGN - 1))
+            {
+                memcpy(data->buf, sdio->xfe.buff, byte_cnt);
+            }
+        }
+    }
+#endif
+    mmcsd_req_complete(host);
+}
+
+static void sdio_request_send(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
+{
+    struct rt_mmcsd_data *data;
+    int byte_cnt;
+    struct sdio_drv *sdio;
+
+    sdio = (struct sdio_drv *)host->private_data;
+    sdio->req = req;
+    data = req->cmd->data;
+
+    if (data)
+    {
+        sdio->xfe.size = data->blksize;
+        sdio->xfe.num  = data->blks;
+        sdio->xfe.buff = (rt_uint8_t *)data->buf;
+        sdio->xfe.flag = (data->flags & DATA_DIR_WRITE) ? \
+            MMC_DATA_WRITE : MMC_DATA_READ;
+#ifdef CONFIG_MMC_USE_DMA
+        byte_cnt = data->blksize * data->blks;
+        if ((byte_cnt > 64) && (((rt_uint32_t)data->buf) & (DMA_ALIGN - 1)))
+        {
+            sdio->xfe.buff = (rt_uint8_t *)sdio->mmc_buf;
+            if (data->flags & DATA_DIR_WRITE)
+            {
+                memcpy(sdio->mmc_buf, data->buf, byte_cnt);
+                mmu_clean_dcache((rt_uint32_t)sdio->mmc_buf, (rt_uint32_t)byte_cnt);
+            }
+        }
+#endif
+    }
+
+    memset(&sdio->flag, 0, sizeof(struct mmc_flag));
+    mmc_send_cmd(host, req->cmd);
+    
+    return;
+}
+
+static void sdio_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
+{
+    int clk = io_cfg->clock;
+    int width = io_cfg->bus_width;
+    struct sdio_drv *sdio_des = (struct sdio_drv *)host->private_data;
+    tina_mmc_t mmc = sdio_des->mmc_des;
+
+    mmc_set_ios(mmc, clk, width);
+}
+
+static const struct rt_mmcsd_host_ops ops =
+{
+    sdio_request_send,
+    sdio_set_iocfg,
+    RT_NULL,
+    RT_NULL,
+};
+
+static void sdio_interrupt_handle(int irqno, void *param)
+{
+    rt_uint32_t risr, idst;
+    rt_uint32_t status;
+    struct sdio_drv *sdio_des = (struct sdio_drv *)param;
+    struct rt_mmcsd_data *data = sdio_des->req->cmd->data;
+    tina_mmc_t mmc = sdio_des->mmc_des;
+
+    risr = mmc->risr_reg;
+    idst = mmc->idst_reg;
+
+    mmc->risr_reg = risr & mmc->imkr_reg;
+    mmc->idst_reg = idst & mmc->idie_reg;
+
+    sdio_des->flag.risr |= risr;
+    sdio_des->flag.idst |= idst;
+
+    if (data)
+    {
+        int done = 0;
+
+        status = sdio_des->flag.risr | mmc->risr_reg;
+        if (data->blks > 1)//not wait auto stop when MMC_CMD_MANUAL is set
+        {
+            if (sdio_des->usedma)
+                done = ((status & (1 << 14)) && (sdio_des->flag.idst & 0x3)) ? 1 : 0;
+            else
+                done = status & (1 << 14);
+        }
+        else
+        {
+            if (sdio_des->usedma)
+                done = ((status & (1 << 3)) && (sdio_des->flag.idst & 0x3)) ? 1 : 0;
+            else
+                done = status & (1 << 3);
+        }
+
+        if (done)
+        {
+            rt_sem_release(&sdio_des->rt_sem);
+        }
+    }
+    else
+    {
+        rt_sem_release(&sdio_des->rt_sem);
+    }
+}
+
+static void sdio_gpio_init(struct sdio_drv *sdio_des)
+{
+    int pin;
+
+    if ((rt_uint32_t)sdio_des->mmc_des == MMC0_BASE_ADDR)
+    {
+        /* SDC0: PF0-PF5 */
+        for (pin = GPIO_PIN_0; pin <= GPIO_PIN_5; pin++)
+        {
+            gpio_set_func(GPIO_PORT_F, pin, IO_FUN_1);
+            gpio_set_pull_mode(GPIO_PORT_F, pin, PULL_UP);
+            gpio_set_drive_level(GPIO_PORT_F, pin, DRV_LEVEL_2);
+        }
+    }
+    else if ((rt_uint32_t)sdio_des->mmc_des == MMC1_BASE_ADDR)
+    {
+        //todo: config gpio port
+        RT_ASSERT(0);
+    }
+
+}
+
+static void sdio_clk_io_on(struct sdio_drv *sdio_des)
+{
+    if ((rt_uint32_t)sdio_des->mmc_des == MMC0_BASE_ADDR)
+    {
+        CCU->bus_clk_gating0 |= 0x1 << 8;
+        CCU->bus_soft_rst0 |= 0x1 << 8;
+    }
+    else if ((rt_uint32_t)sdio_des->mmc_des == MMC1_BASE_ADDR)
+    {
+        CCU->bus_clk_gating0 |= 0x1 << 9;
+        CCU->bus_soft_rst0 |= 0x1 << 9;
+    }
+
+    mmc_set_clk(SDMMC0, 24000000);
+}
+
+static void sdio_irq_init(void *param)
+{
+    struct sdio_drv *sdio_des = (struct sdio_drv *)param;
+
+    if ((rt_uint32_t)sdio_des->mmc_des == MMC0_BASE_ADDR)
+    {
+        rt_hw_interrupt_install(SDC0_INTERRUPT, sdio_interrupt_handle, param, "mmc0_irq");
+        rt_hw_interrupt_umask(SDC0_INTERRUPT);
+    }
+    else if ((rt_uint32_t)sdio_des->mmc_des == MMC1_BASE_ADDR)
+    {
+        rt_hw_interrupt_install(SDC1_INTERRUPT, sdio_interrupt_handle, param, "mmc1_irq");
+        rt_hw_interrupt_umask(SDC1_INTERRUPT);
+    }
+
+    sdio_des->mmc_des->gctl_reg |= (0x1 << 4);
+}
+
+int tina_sdio_init(void)
+{
+    struct rt_mmcsd_host *host;
+
+#ifdef TINA_USING_SDIO0
+    {
+        static struct sdio_drv _sdio_drv;
+
+        host = mmcsd_alloc_host();
+        if (!host)
+        {
+            dbg_log(DBG_ERROR, "alloc host failed\n");
+            goto err;
+        }
+
+        if (rt_sem_init(&_sdio_drv.rt_sem, "sdio_sem", RT_NULL, RT_IPC_FLAG_FIFO))
+        {
+            dbg_log(DBG_ERROR, "sem init failed\n");
+            goto err;
+        }
+        _sdio_drv.mmc_des = (tina_mmc_t)MMC0_BASE_ADDR;
+        _sdio_drv.mmc_buf = dma_buffer;
+        //init gpio pin
+        sdio_gpio_init(&_sdio_drv);
+        //clk is on
+        sdio_clk_io_on(&_sdio_drv);
+        //irq init
+        sdio_irq_init(&_sdio_drv);
+
+        host->ops = &ops;
+        host->freq_min = 400 * 1000;
+        host->freq_max = 50 * 1000 * 1000;
+        host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 |
+        VDD_32_33 | VDD_33_34 | VDD_34_35 | VDD_35_36;
+        host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ | MMCSD_SUP_HIGHSPEED;
+        host->max_seg_size = 2048;
+        host->max_dma_segs = 10;
+        host->max_blk_size = 512;
+        host->max_blk_count = 4096;
+
+        host->private_data = &_sdio_drv;
+
+        _sdio_drv.host = host;
+
+        mmcsd_change(host);
+    }
+#endif
+
+    return RT_EOK;
+
+err:
+    if (host)
+    {
+        rt_free(host);
+    }
+
+    return RT_ERROR;
+}
+INIT_APP_EXPORT(tina_sdio_init);
+#endif

+ 180 - 0
bsp/allwinner_tina/drivers/drv_sdio.h

@@ -0,0 +1,180 @@
+/*
+ * File      : drv_sdio.h
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2017, RT-Thread Development Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-02-08     RT-Thread    the first version
+ */
+#ifndef __DRV_SDIO_H__
+#define __DRV_SDIO_H__
+
+
+#define MMC0_BASE_ADDR     0x01C0F000
+#define MMC1_BASE_ADDR     0x01C10000
+
+struct tina_mmc
+{
+    volatile rt_uint32_t gctl_reg;            /* (0x000) */
+    volatile rt_uint32_t ckcr_reg;            /* (0x004) */
+    volatile rt_uint32_t tmor_reg;            /* (0x008) */
+    volatile rt_uint32_t bwdr_reg;            /* (0x00C) */
+    volatile rt_uint32_t bksr_reg;            /* (0x010) */
+    volatile rt_uint32_t bycr_reg;            /* (0x014) */
+    volatile rt_uint32_t cmdr_reg;            /* (0x018) */
+    volatile rt_uint32_t cagr_reg;            /* (0x01C) */
+    volatile rt_uint32_t resp0_reg;           /* (0x020) */
+    volatile rt_uint32_t resp1_reg;           /* (0x024) */
+    volatile rt_uint32_t resp2_reg;           /* (0x028) */
+    volatile rt_uint32_t resp3_reg;           /* (0x02C) */
+    volatile rt_uint32_t imkr_reg;            /* (0x030) */
+    volatile rt_uint32_t misr_reg;            /* (0x034) */
+    volatile rt_uint32_t risr_reg;            /* (0x038) */
+    volatile rt_uint32_t star_reg;            /* (0x03C) */
+    volatile rt_uint32_t fwlr_reg;            /* (0x040) */
+    volatile rt_uint32_t funs_reg;            /* (0x044) */
+    volatile rt_uint32_t cbcr_reg;            /* (0x048) */
+    volatile rt_uint32_t bbcr_reg;            /* (0x04C) */
+    volatile rt_uint32_t dbgc_reg;            /* (0x050) */
+    volatile rt_uint32_t reserved0;
+    volatile rt_uint32_t a12a_reg;            /* (0x058) */
+    volatile rt_uint32_t reserved1[7];
+    volatile rt_uint32_t hwrst_reg;           /* (0x078) */
+    volatile rt_uint32_t reserved2;
+    volatile rt_uint32_t dmac_reg;            /* (0x080) */
+    volatile rt_uint32_t dlba_reg;            /* (0x084) */
+    volatile rt_uint32_t idst_reg;            /* (0x088) */
+    volatile rt_uint32_t idie_reg;            /* (0x08C) */
+    volatile rt_uint32_t chda_reg;            /* (0x090) */
+    volatile rt_uint32_t cbda_reg;            /* (0x094) */
+    volatile rt_uint32_t reserved3[26];
+    volatile rt_uint32_t card_thldc_reg;      /* (0x100) */
+    volatile rt_uint32_t reserved4[2];
+    volatile rt_uint32_t emmc_dsbd_reg;       /* (0x10c) */
+    volatile rt_uint32_t reserved5[12];
+    volatile rt_uint32_t reserved6[48];
+    volatile rt_uint32_t fifo_reg;            /* (0x200) */
+};
+
+typedef struct tina_mmc *tina_mmc_t;
+
+#define MMC0    ((tina_mmc_t)MMC0_BASE_ADDR)
+#define MMC1    ((tina_mmc_t)MMC1_BASE_ADDR)
+
+
+#define BIT(x)              (1<<(x))
+/* Struct for Intrrrupt Information */
+#define SDXC_RespErr        BIT(1)  //0x2
+#define SDXC_CmdDone        BIT(2)  //0x4
+#define SDXC_DataOver       BIT(3)  //0x8
+#define SDXC_TxDataReq      BIT(4)  //0x10
+#define SDXC_RxDataReq      BIT(5)  //0x20
+#define SDXC_RespCRCErr     BIT(6)  //0x40
+#define SDXC_DataCRCErr     BIT(7)  //0x80
+#define SDXC_RespTimeout    BIT(8)  //0x100
+#define SDXC_ACKRcv         BIT(8)  //0x100
+#define SDXC_DataTimeout    BIT(9)  //0x200
+#define SDXC_BootStart      BIT(9)  //0x200
+#define SDXC_DataStarve     BIT(10) //0x400
+#define SDXC_VolChgDone     BIT(10) //0x400
+#define SDXC_FIFORunErr     BIT(11) //0x800
+#define SDXC_HardWLocked    BIT(12) //0x1000
+#define SDXC_StartBitErr    BIT(13) //0x2000
+#define SDXC_AutoCMDDone    BIT(14) //0x4000
+#define SDXC_EndBitErr      BIT(15) //0x8000
+#define SDXC_SDIOInt        BIT(16) //0x10000
+#define SDXC_CardInsert     BIT(30) //0x40000000
+#define SDXC_CardRemove     BIT(31) //0x80000000
+#define SDXC_IntErrBit      (SDXC_RespErr | SDXC_RespCRCErr | SDXC_DataCRCErr \
+                            | SDXC_RespTimeout | SDXC_DataTimeout | SDXC_FIFORunErr \
+                            | SDXC_HardWLocked | SDXC_StartBitErr | SDXC_EndBitErr)  //0xbfc2
+
+/*
+    SD CMD reg
+REG[0-5] : Cmd ID
+REG[6]   : Has response
+REG[7]   : Long response
+REG[8]   : Check response CRC
+REG[9]   : Has data
+REG[10]  : Write
+REG[11]  : Steam mode
+REG[12]  : Auto stop
+REG[13]  : Wait previous over
+REG[14]  : About cmd
+REG[15]  : Send initialization
+REG[21]  : Update clock
+REG[31]  : Load cmd
+*/
+#define SDXC_RESPONSE_CMD        BIT(6)
+#define SDXC_LONG_RESPONSE_CMD   BIT(7)
+#define SDXC_CHECK_CRC_CMD       BIT(8)
+#define SDXC_HAS_DATA_CMD        BIT(9)
+#define SDXC_WRITE_CMD           BIT(10)
+#define SDXC_STEAM_CMD           BIT(11)
+#define SDXC_AUTO_STOP_CMD       BIT(12)
+#define SDXC_WAIT_OVER_CMD       BIT(13)
+#define SDXC_ABOUT_CMD           BIT(14)
+#define SDXC_SEND_INIT_CMD       BIT(15)
+#define SDXC_UPDATE_CLOCK_CMD    BIT(21)
+#define SDXC_LOAD_CMD            BIT(31)
+
+/* 
+    SD status reg 
+REG[0]   : FIFO_RX_LEVEL
+REG[1]   : FIFO_TX_LEVEL
+REG[2]   : FIFO_EMPTY
+REG[3]   : FIFO_FULL
+REG[4-7] : FSM_STA
+REG[8]   : CARD_PRESENT
+REG[9]   : CARD_BUSY
+REG[10]  : FSM_BUSY
+REG[11-16]: RESP_IDX 
+REG[17-21]: FIFO_LEVEL
+REG[31]   : DMA_REQ
+*/
+
+#define SDXC_FIFO_RX_LEVEL    BIT(0)
+#define SDXC_FIFO_TX_LEVEL    BIT(1)
+#define SDXC_FIFO_EMPTY       BIT(2)
+#define SDXC_FIFO_FULL        BIT(3)
+#define SDXC_CARD_PRESENT     BIT(8)
+#define SDXC_CARD_BUSY        BIT(9)
+#define SDXC_FSM_BUSY         BIT(10)
+#define SDXC_DMA_REQ          BIT(31)
+
+struct mmc_des_v4p1
+{
+    rt_uint32_t                         : 1,
+                    dic                 : 1, /* disable interrupt on completion                                    */
+                    last_des            : 1, /* 1-this data buffer is the last buffer                              */
+                    first_des           : 1, /* 1-data buffer is the first buffer,0-data buffer contained in the next descriptor is 1st buffer        */
+                    des_chain           : 1, /* 1-the 2nd address in the descriptor is the next descriptor address */
+                    end_of_ring         : 1, /* 1-last descriptor flag when using dual data buffer in descriptor   */
+                                        : 24,
+                    card_err_sum        : 1, /* transfer error flag                                                */
+                    own                 : 1; /* des owner:1-idma owns it, 0-host owns it                           */
+
+#define SDXC_DES_NUM_SHIFT 12  /* smhc2!! */
+#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT)
+    rt_uint32_t data_buf1_sz  : 16,
+                data_buf2_sz     : 16;
+    rt_uint32_t buf_addr_ptr1;
+    rt_uint32_t buf_addr_ptr2;
+};
+
+#endif