diff --git a/bsp/allwinner_tina/drivers/Kconfig b/bsp/allwinner_tina/drivers/Kconfig index b7efb5de200ada4f35795a26ce403265d7d3e8d2..1fd2393cc98b8c4fa69b17d802a7eb490e5c9fe7 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 0000000000000000000000000000000000000000..72b43c0e22e804035d14de240d6d3e428e486550 --- /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 +#include +#include +#include + +#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 + +#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 0000000000000000000000000000000000000000..742d5362e1747494128f0cabd7f3d4e0e610151f --- /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