summaryrefslogtreecommitdiff
path: root/bsp
diff options
context:
space:
mode:
authorzyh <[email protected]>2018-04-20 11:10:38 +0800
committerzyh <[email protected]>2018-04-20 11:15:07 +0800
commit7cbeb251e4754e7d0bc2bdc783d492b20ef8e5f9 (patch)
treec9dde00a3298ccd919e7d8f6b29d12651f65fd37 /bsp
parent75194461eae91e73a5c98f654fc15c8d6f0641d4 (diff)
[Bsp][Tina]Add SDIO Driver
Diffstat (limited to 'bsp')
-rw-r--r--bsp/allwinner_tina/drivers/Kconfig6
-rw-r--r--bsp/allwinner_tina/drivers/drv_sdio.c795
-rw-r--r--bsp/allwinner_tina/drivers/drv_sdio.h180
3 files changed, 981 insertions, 0 deletions
diff --git a/bsp/allwinner_tina/drivers/Kconfig b/bsp/allwinner_tina/drivers/Kconfig
index b7efb5de20..1fd2393cc9 100644
--- a/bsp/allwinner_tina/drivers/Kconfig
+++ b/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
+ \ No newline at end of file
diff --git a/bsp/allwinner_tina/drivers/drv_sdio.c b/bsp/allwinner_tina/drivers/drv_sdio.c
new file mode 100644
index 0000000000..72b43c0e22
--- /dev/null
+++ b/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
diff --git a/bsp/allwinner_tina/drivers/drv_sdio.h b/bsp/allwinner_tina/drivers/drv_sdio.h
new file mode 100644
index 0000000000..742d5362e1
--- /dev/null
+++ b/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