12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130 |
- /*
- * File : drv_mmc.c
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2013 - 2015, RT-Thread Development Team
- *
- * The license and distribution terms for this file may be
- * found in the file LICENSE in this distribution or at
- * http://www.rt-thread.org/license/LICENSE
- *
- * Change Logs:
- * Date Author Notes
- * 2013-03-09 aozima the first version
- * 2013-03-29 aozima support Jz4770.
- * 2013-04-01 aozima add interrupt support for Jz4770.
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #include <drivers/mmcsd_core.h>
- #include <drivers/sdio.h>
- #include "board.h"
- #include "drv_gpio.h"
- #include "drv_clock.h"
- #include "drv_mmc.h"
- #include <cache.h>
- #include <string.h>
- #define DMA_BUFFER
- #define DMA_ALIGN (32U)
- #define PIO_THRESHOLD 64 /* use pio mode if data length < PIO_THRESHOLD */
- #define DBG_TAG "SDIO"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- /*
- * Error status including CRC_READ_ERROR, CRC_WRITE_ERROR,
- * CRC_RES_ERR, TIME_OUT_RES, TIME_OUT_READ
- */
- #define ERROR_STAT 0x3f
- #define JZMMC_USE_PIO 2
- /* Register access macros */
- #define msc_readl(host,reg) \
- readl((host)->hw_base + reg)
- #define msc_writel(host,reg,value) \
- writel((value), (host)->hw_base + (reg))
- #define is_pio_mode(host) \
- (host->flags & (1 << JZMMC_USE_PIO))
- #define enable_pio_mode(host) \
- (host->flags |= (1 << JZMMC_USE_PIO))
- #define disable_pio_mode(host) \
- (host->flags &= ~(1 << JZMMC_USE_PIO))
- /*-------------------End structure and macro define------------------------*/
- #ifdef DMA_BUFFER
- ALIGN(32)
- uint8_t _dma_buffer_0[32 * 1024];
- ALIGN(32)
- uint8_t _dma_buffer_1[32 * 1024];
- #endif
- struct jzmmc_host *jz_host1 = RT_NULL;
- volatile static int stopping_clock = 0;
- volatile static int sdio_log = 0;
- /*
- * Functional functions.
- *
- * These small function will be called frequently.
- */
- rt_inline void enable_msc_irq(struct jzmmc_host *host, unsigned long bits)
- {
- unsigned long imsk;
- imsk = msc_readl(host, MSC_IMASK_OFFSET);
- imsk &= ~bits;
- msc_writel(host, MSC_IMASK_OFFSET, imsk);
- }
- rt_inline void clear_msc_irq(struct jzmmc_host *host, unsigned long bits)
- {
- msc_writel(host, MSC_IREG_OFFSET, bits);
- }
- rt_inline void disable_msc_irq(struct jzmmc_host *host, unsigned long bits)
- {
- unsigned long imsk;
- imsk = msc_readl(host, MSC_IMASK_OFFSET);
- imsk |= bits;
- msc_writel(host, MSC_IMASK_OFFSET, imsk);
- }
- static inline int check_error_status(struct jzmmc_host *host, unsigned int status)
- {
- if (status & ERROR_STAT)
- {
- LOG_D("Error status->0x%08X: cmd=%d", status, host->cmd->cmd_code);
- return -1;
- }
- return 0;
- }
- /* Stop the MMC clock and wait while it happens */
- rt_inline rt_err_t jzmmc_stop_clock(uint32_t hw_base)
- {
- uint16_t value;
- int timeout = 10000;
- stopping_clock = 1;
- value = readw(hw_base + MSC_CTRL_OFFSET);
- value &= ~MSC_CTRL_CLOCK_CONTROL_MASK;
- value |= MSC_CTRL_CLOCK_STOP;
- writew(value, hw_base + MSC_CTRL_OFFSET);
- while (timeout && (readl(hw_base + MSC_STAT_OFFSET) & MSC_STAT_CLK_EN))
- {
- timeout--;
- if (timeout == 0)
- {
- rt_kprintf("stop clock timeout!\n");
- stopping_clock = 0;
- return -RT_ETIMEOUT;
- }
- rt_thread_delay(1);
- }
- stopping_clock = 0;
- return RT_EOK;
- }
- /* Start the MMC clock and operation */
- rt_inline void jzmmc_start_clock(uint32_t hw_base)
- {
- uint16_t value;
- value = readw(hw_base + MSC_CTRL_OFFSET);
- value |= (MSC_CTRL_CLOCK_START | MSC_CTRL_START_OP);
- writew(value, hw_base + MSC_CTRL_OFFSET);
- }
- static int jzmmc_polling_status(struct jzmmc_host *host, unsigned int status)
- {
- unsigned int cnt = 100 * 1000 * 1000;
- while(!(msc_readl(host, MSC_STAT_OFFSET) & (status | ERROR_STAT)) \
- && (--cnt));
- if (!cnt)
- {
- LOG_D("polling status(0x%08X) time out, "
- "op=%d, status=0x%08X", status,
- host->cmd->cmd_code, msc_readl(host, MSC_STAT_OFFSET));
- return -1;
- }
- if (msc_readl(host, MSC_STAT_OFFSET) & ERROR_STAT)
- {
- LOG_D("polling status(0x%08X) error, "
- "op=%d, status=0x%08X", status,
- host->cmd->cmd_code, msc_readl(host, MSC_STAT_OFFSET));
- return -1;
- }
- return 0;
- }
- rt_inline void jzmmc_stop_dma(struct jzmmc_host *host)
- {
- /*
- * Theoretically, DMA can't be stopped when transfering, so we can only
- * diable it when it is out of DMA request.
- */
- msc_writel(host, MSC_DMAC_OFFSET, 0);
- }
- static void jzmmc_command_done(struct jzmmc_host *host, struct rt_mmcsd_cmd *cmd)
- {
- int i;
- unsigned long res;
- uint8_t buf[16];
- uint32_t data;
- memset(cmd->resp, 0x0, sizeof(cmd->resp));
- if ((host->cmdat & MSC_CMDAT_RESP_FORMAT_MASK) == MSC_CMDAT_RESPONSE_R2)
- {
- res = msc_readl(host, MSC_RES_OFFSET);
- for (i = 0 ; i < 4 ; i++) {
- cmd->resp[i] = res << 24;
- res = msc_readl(host, MSC_RES_OFFSET);
- cmd->resp[i] |= res << 8;
- res = msc_readl(host, MSC_RES_OFFSET);
- cmd->resp[i] |= res >> 8;
- }
- }
- else if ((host->cmdat & MSC_CMDAT_RESP_FORMAT_MASK) == MSC_CMDAT_RESPONSE_NONE)
- {
- }
- else
- {
- res = msc_readl(host, MSC_RES_OFFSET);
- cmd->resp[0] = res << 24;
- res = msc_readl(host, MSC_RES_OFFSET);
- cmd->resp[0] |= res << 8;
- res = msc_readl(host, MSC_RES_OFFSET);
- cmd->resp[0] |= res & 0xff;
- }
- LOG_D("error:%d cmd->resp [%08X, %08X, %08X, %08X]\r\n\r",
- cmd->err,
- cmd->resp[0],
- cmd->resp[1],
- cmd->resp[2],
- cmd->resp[3]
- );
- clear_msc_irq(host, IFLG_END_CMD_RES);
- }
- static void jzmmc_data_done(struct jzmmc_host *host)
- {
- struct rt_mmcsd_data *data = host->data;
- if (host->cmd->err == RT_EOK)
- {
- data->bytes_xfered = (data->blks * data->blksize);
- jzmmc_stop_dma(host);
- }
- else
- {
- jzmmc_stop_dma(host);
- data->bytes_xfered = 0;
- LOG_D("error when request done");
- }
- }
- #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
- static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
- {
- unsigned char *buf = (unsigned char*)ptr;
- int i, j;
- for (i=0; i<buflen; i+=16)
- {
- rt_kprintf("%08X: ", i);
- for (j=0; j<16; j++)
- if (i+j < buflen)
- rt_kprintf("%02X ", buf[i+j]);
- else
- rt_kprintf(" ");
- rt_kprintf(" ");
- for (j=0; j<16; j++)
- if (i+j < buflen)
- rt_kprintf("%c", __is_print(buf[i+j]) ? buf[i+j] : '.');
- rt_kprintf("\n");
- }
- }
- /*------------------------End functional functions-------------------------*/
- rt_inline void jzmmc_submit_dma(struct jzmmc_host *host, struct rt_mmcsd_data *data)
- {
- host->dma_desc.nda = 0;
- host->dma_desc.len = data->blks * data->blksize;
- host->dma_desc.da = virt_to_phys(data->buf);
- host->dma_desc.dcmd = DMACMD_ENDI; /* only one DMA descriptor */
- #ifdef DMA_BUFFER
- if ((uint32_t)(data->buf) & (DMA_ALIGN - 1))
- {
- /* not align */
- host->dma_desc.da = virt_to_phys(host->_dma_buffer);
- if (data->flags & DATA_DIR_WRITE)
- {
- LOG_D("%d ->", data->blks * data->blksize);
- memcpy(host->_dma_buffer, data->buf, data->blks * data->blksize);
- rt_hw_dcache_flush_range((rt_ubase_t)(host->_dma_buffer), data->blks * data->blksize);
- LOG_D("| 0x%08x", data->buf);
- }
- }
- else
- {
- if (data->flags & DATA_DIR_WRITE)
- {
- rt_hw_dcache_flush_range((rt_ubase_t)(data->buf), data->blks * data->blksize);
- }
- }
- #endif
-
- }
- // #define PERFORMANCE_DMA
- rt_inline void jzmmc_dma_start(struct jzmmc_host *host, struct rt_mmcsd_data *data)
- {
- volatile int i = 120;
- uint32_t dma_addr = virt_to_phys(data->buf);
- unsigned int dma_len = data->blks * data->blksize;
- unsigned int dmac;
- #ifdef PERFORMANCE_DMA
- dmac = (0x01 << DMAC_INCR_SHF) | DMAC_DMAEN | DMAC_MODE_SEL;
- #else
- dmac = (0x01 << DMAC_INCR_SHF) | DMAC_DMAEN;
- #endif
- #ifndef DMA_BUFFER
- if ((dma_addr & (DMA_ALIGN - 1)) || (dma_len & (DMA_ALIGN - 1)))
- {
- LOG_D("DMA align, addr=0x%08x", dma_addr);
- dmac |= DMAC_ALIGNEN;
- if (dma_addr & (DMA_ALIGN - 1))
- {
- dmac |= (dma_addr & (DMA_ALIGN - 1)) << DMAC_AOFST_SHF;
- }
- }
- #endif
- LOG_D("DMA start: nda 0x%08x, da 0x%08x, len 0x%04x, cmd 0x%08x", virt_to_phys(&(host->dma_desc)),
- host->dma_desc.da, host->dma_desc.len, host->dma_desc.dcmd);
-
- rt_hw_dcache_flush_range((rt_ubase_t)(&(host->dma_desc)), 32);
- while(i--); //TODO:短暂延时,不延时会发生错误
- msc_writel(host, MSC_DMANDA_OFFSET, virt_to_phys(&(host->dma_desc)));
- msc_writel(host, MSC_DMAC_OFFSET, dmac);
- }
- /*----------------------------End DMA handler------------------------------*/
- /*
- * PIO transfer mode.
- *
- * Functions of PIO read/write mode that can handle 1, 2 or 3 bytes transfer
- * even though the FIFO register is 32-bits width.
- * It's better just used for test.
- */
- static int wait_cmd_response(struct jzmmc_host *host)
- {
- if (!(msc_readl(host, MSC_IREG_OFFSET) & IFLG_END_CMD_RES))
- {
- rt_err_t ret;
- rt_completion_init(&host->completion);
- enable_msc_irq(host, IMASK_TIME_OUT_RES | IMASK_END_CMD_RES);
- rt_hw_interrupt_umask(host->irqno);
- ret = rt_completion_wait(&host->completion, RT_TICK_PER_SECOND);
- clear_msc_irq(host, IFLG_TIMEOUT_RES | IFLG_END_CMD_RES);
- disable_msc_irq(host, IFLG_TIMEOUT_RES | IFLG_END_CMD_RES);
- if(ret == RT_EOK)
- {
- LOG_D("wait response OK!\r");
- }
- else
- {
- uint32_t value;
- value = msc_readl(host, MSC_STAT_OFFSET);
- LOG_D("stat=0x%08x", value);
- value = msc_readl(host, MSC_IREG_OFFSET);
- LOG_D("iflag=0x%08x", value);
- host->cmd->err = ret;
- LOG_D("wait END_CMD_RES timeout[uncompletion]\r");
- return -1;
- }
- }
- msc_writel(host, MSC_IREG_OFFSET, IFLG_END_CMD_RES);
- return 0;
- }
- static void do_pio_read(struct jzmmc_host *host,
- unsigned int *addr, unsigned int cnt)
- {
- int i = 0;
- unsigned int status = 0;
- for (i = 0; i < cnt / 4; i++)
- {
- while (((status = msc_readl(host, MSC_STAT_OFFSET))
- & MSC_STAT_DATA_FIFO_EMPTY));
- if (check_error_status(host, status))
- {
- host->cmd->err = -RT_EIO;
- return;
- }
- *addr++ = msc_readl(host, MSC_RXFIFO_OFFSET);
- }
- /*
- * These codes handle the last 1, 2 or 3 bytes transfer.
- */
- if (cnt & 3)
- {
- uint32_t n = cnt & 3;
- uint32_t data = msc_readl(host, MSC_RXFIFO_OFFSET);
- uint8_t *p = (u8 *)addr;
- while (n--)
- {
- *p++ = data;
- data >>= 8;
- }
- }
- }
- static void do_pio_write(struct jzmmc_host *host,
- unsigned int *addr, unsigned int cnt)
- {
- int i = 0;
- unsigned int status = 0;
- for (i = 0; i < (cnt / 4); i++)
- {
- while (((status = msc_readl(host, MSC_STAT_OFFSET))
- & MSC_STAT_DATA_FIFO_FULL));
- if (check_error_status(host, status))
- {
- host->cmd->err = -RT_EIO;
- return;
- }
- msc_writel(host, MSC_TXFIFO_OFFSET, *addr++);
- }
- /*
- * These codes handle the last 1, 2 or 3 bytes transfer.
- */
- if (cnt & 3)
- {
- uint32_t data = 0;
- uint8_t *p = (uint8_t *)addr;
- for (i = 0; i < (cnt & 3); i++)
- data |= *p++ << (8 * i);
- msc_writel(host, MSC_TXFIFO_OFFSET, data);
- }
- }
- static inline void pio_trans_start(struct jzmmc_host *host, struct rt_mmcsd_data *data)
- {
- unsigned int *addr = (unsigned int *)data->buf;
- unsigned int cnt = data->blks * data->blksize;
- if (data->flags & DATA_DIR_WRITE)
- do_pio_write(host, addr, cnt);
- else
- do_pio_read(host, addr, cnt);
- }
- static void pio_trans_done(struct jzmmc_host *host, struct rt_mmcsd_data *data)
- {
- if (host->cmd->err == RT_EOK)
- data->bytes_xfered = data->blks * data->blksize;
- else
- data->bytes_xfered = 0;
- if (host->req->stop)
- {
- if (jzmmc_polling_status(host, MSC_STAT_AUTO_CMD_DONE) < 0)
- host->cmd->err = -RT_EIO;
- }
- if (data->flags & DATA_DIR_WRITE)
- {
- if (jzmmc_polling_status(host, MSC_STAT_PRG_DONE) < 0)
- {
- host->cmd->err = -RT_EIO;
- }
- clear_msc_irq(host, IFLG_PRG_DONE);
- }
- else
- {
- if (jzmmc_polling_status(host, MSC_STAT_DATA_TRAN_DONE) < 0)
- {
- host->cmd->err = -RT_EIO;
- }
- clear_msc_irq(host, IFLG_DATA_TRAN_DONE);
- }
- }
- /*-------------------------End PIO transfer mode---------------------------*/
- /*
- * Achieve mmc_request here.
- */
- static void jzmmc_data_pre(struct jzmmc_host *host, struct rt_mmcsd_data *data)
- {
- unsigned int nob = data->blks;
- unsigned long cmdat,imsk;
- msc_writel(host, MSC_RDTO_OFFSET, 0xffffff);
- msc_writel(host, MSC_NOB_OFFSET, nob);
- msc_writel(host, MSC_BLKLEN_OFFSET, data->blksize);
- cmdat = MSC_CMDAT_DATA_EN;
- msc_writel(host, MSC_CMDAT_OFFSET, MSC_CMDAT_DATA_EN);
- if (data->flags & DATA_DIR_WRITE)
- {
- cmdat |= MSC_CMDAT_WRITE;
- imsk = IMASK_WR_ALL_DONE | IMASK_CRC_WRITE_ERR;
- }
- else if (data->flags & DATA_DIR_READ)
- {
- cmdat &= ~MSC_CMDAT_WRITE;
- imsk = IMASK_DMA_DATA_DONE | IMASK_TIME_OUT_READ | IMASK_CRC_READ_ERR;
- }
- else
- {
- rt_kprintf("data direction confused\n");
- }
- host->cmdat |= cmdat;
- if (!is_pio_mode(host))
- {
- jzmmc_submit_dma(host, data);
- clear_msc_irq(host, IFLG_PRG_DONE);
- enable_msc_irq(host, imsk);
- }
- }
- static void jzmmc_data_start(struct jzmmc_host *host, struct rt_mmcsd_data *data)
- {
- if (is_pio_mode(host))
- {
- pio_trans_start(host, data);
- pio_trans_done(host, data);
- disable_pio_mode(host);
- }
- else
- {
- rt_err_t ret;
- rt_completion_init(&host->completion);
- /* start DMA */
- disable_msc_irq(host, IFLG_END_CMD_RES);
- jzmmc_dma_start(host, data);
- rt_hw_interrupt_umask(host->irqno);
- ret = rt_completion_wait(&host->completion, RT_TICK_PER_SECOND);
- if (ret != RT_EOK)
- {
- rt_kprintf("warning: msc dma timeout\n");
- }
- else
- {
- LOG_D("msc status: 0x%08x", msc_readl(host, MSC_STAT_OFFSET));
- clear_msc_irq(host, IFLG_DATA_TRAN_DONE | IFLG_DMAEND | IFLG_DMA_DATA_DONE | IFLG_TIMEOUT_RES);
- disable_msc_irq(host, IMASK_DMA_DATA_DONE | IMASK_CRC_READ_ERR);
- #ifdef DMA_BUFFER
- if ((data->flags & DATA_DIR_READ))
- {
- if((uint32_t)data->buf & (DMA_ALIGN - 1))
- {
- rt_hw_dcache_invalidate_range((rt_ubase_t)(host->_dma_buffer), data->blks * data->blksize);
- memcpy(data->buf, host->_dma_buffer, data->blks * data->blksize);
- LOG_D("0x%08x <-| %d", data->buf, data->blks * data->blksize);
- }
- else
- {
- rt_hw_dcache_invalidate_range((rt_ubase_t)(data->buf), data->blks * data->blksize);
- }
-
- }
- #endif
- }
- jzmmc_data_done(host);
- }
- }
- static void jzmmc_command_start(struct jzmmc_host *host, struct rt_mmcsd_cmd *cmd)
- {
- unsigned long cmdat = 0;
- unsigned long imsk;
- /* auto send stop */
- if (host->req->stop) cmdat |= MSC_CMDAT_SEND_AS_STOP;
- /* handle response type */
- switch (cmd->flags & RESP_MASK)
- {
- #define _CASE(S,D) case RESP_##S: cmdat |= MSC_CMDAT_RESPONSE_##D; break
- _CASE(R1, R1); /* r1 */
- _CASE(R2, R2);
- _CASE(R3, R3); /* r3 */
- _CASE(R4, R4); /* r4 */
- _CASE(R5, R5);
- _CASE(R6, R6);
- _CASE(R7, R7);
- default:
- break;
- #undef _CASE
- }
- if ((cmd->flags & RESP_MASK) == RESP_R1B) cmdat |= MSC_CMDAT_BUSY;
- host->cmdat |= cmdat;
- if (!is_pio_mode(host))
- {
- imsk = IMASK_TIME_OUT_RES | IMASK_END_CMD_RES;
- enable_msc_irq(host, imsk);
- }
- LOG_D("dat: 0x%08x", host->cmdat);
- LOG_D("resp type: %d", cmd->flags & RESP_MASK);
- writel(0xFF, host->hw_base + MSC_RESTO_OFFSET);
- writel(0xFFFFFFFF, host->hw_base + MSC_RDTO_OFFSET);
- msc_writel(host, MSC_CMD_OFFSET, cmd->cmd_code);
- msc_writel(host, MSC_ARG_OFFSET, cmd->arg);
- msc_writel(host, MSC_CMDAT_OFFSET, host->cmdat);
- msc_writel(host, MSC_CTRL_OFFSET, MSC_CTRL_START_OP);
- jzmmc_start_clock(host->hw_base);
- cmd->err = RT_EOK;
- if (is_pio_mode(host))
- {
- wait_cmd_response(host);
- jzmmc_command_done(host, host->cmd);
- }
- }
- static void jzmmc_sdio_request(struct rt_mmcsd_host *mmc, struct rt_mmcsd_req *req)
- {
- struct jzmmc_host *host = mmc->private_data;
- char direction = '\0';
- host->req = req;
- host->data = req->data;
- host->cmd = req->cmd;
- host->cmdat = 0;
- LOG_D("CMD: %d ARG: %08X", req->cmd->cmd_code, req->cmd->arg);
- if (host->data)
- {
- direction = (host->data->flags & DATA_DIR_WRITE)? 'w' : 'r';
- }
- jzmmc_stop_clock(host->hw_base);
- /* disable pio mode firstly */
- disable_pio_mode(host);
- /* clear status */
- writew(0xFFFF, host->hw_base + MSC_IREG_OFFSET);
- disable_msc_irq(host, 0xffffffff);
- if (host->flags & MSC_CMDAT_BUS_WIDTH_4BIT)
- {
- host->cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT;
- }
- if(req->cmd->cmd_code == GO_IDLE_STATE)
- {
- host->cmdat |= MSC_CMDAT_INIT;
- }
- if(host->data)
- {
- LOG_D("with data, datalen = %d", host->data->blksize * host->data->blks);
- if (host->data->blksize * host->data->blks < PIO_THRESHOLD)
- {
- LOG_D(" pio mode!");
- enable_pio_mode(host);
- }
- jzmmc_data_pre(host, host->data);
- }
- else
- {
- writew(0, host->hw_base + MSC_BLKLEN_OFFSET);
- writew(0, host->hw_base + MSC_NOB_OFFSET);
- enable_pio_mode(host);
- }
- jzmmc_command_start(host, host->cmd);
- if (host->data)
- {
- jzmmc_data_start(host, host->data);
- }
- mmcsd_req_complete(mmc);
- }
- static void jzmmc_isr(int irqno, void* param)
- {
- uint32_t pending;
- uint32_t pending_;
-
- struct jzmmc_host * host = (struct jzmmc_host *)param;
- pending_ = msc_readl(host, MSC_IREG_OFFSET);
- pending = msc_readl(host, MSC_IREG_OFFSET) & (~ msc_readl(host, MSC_IMASK_OFFSET));
- if(pending_ & IFLG_CRC_RES_ERR)
- {
- LOG_W("RES CRC err");
- }
- if(pending_ & IFLG_CRC_READ_ERR)
- {
- LOG_W("READ CRC err");
- }
- if(pending_ & IFLG_CRC_WRITE_ERR)
- {
- LOG_W("WRITE CRC err");
- }
-
-
- if (pending & IFLG_TIMEOUT_RES)
- {
- host->cmd->err = -RT_ETIMEOUT;
- LOG_D("TIMEOUT");
- }
- else if (pending & IFLG_CRC_READ_ERR)
- {
- host->cmd->err = -RT_EIO;
- LOG_W("CRC READ");
- }
- else if (pending & (IFLG_CRC_RES_ERR | IFLG_CRC_WRITE_ERR | IFLG_TIMEOUT_READ))
- {
- LOG_E("MSC ERROR, pending=0x%08x", pending);
- }
- if (pending & (IFLG_DMA_DATA_DONE | IFLG_WR_ALL_DONE))
- {
- LOG_D("msc DMA end!");
- /* disable interrupt */
- rt_hw_interrupt_mask(host->irqno);
- rt_completion_done(&host->completion);
- }
- else if (pending & (MSC_TIME_OUT_RES | MSC_END_CMD_RES))
- {
- /* disable interrupt */
- rt_hw_interrupt_mask(host->irqno);
- rt_completion_done(&host->completion);
- }
- }
- rt_inline void jzmmc_clk_autoctrl(struct jzmmc_host *host, unsigned int on)
- {
- if(on)
- {
- if(!clk_is_enabled(host->clock))
- clk_enable(host->clock);
- if(!clk_is_enabled(host->clock_gate))
- clk_enable(host->clock_gate);
- }
- else
- {
- if(clk_is_enabled(host->clock_gate))
- clk_disable(host->clock_gate);
- if(clk_is_enabled(host->clock))
- clk_disable(host->clock);
- }
- }
- static int jzmmc_hardware_init(struct jzmmc_host *jz_sdio)
- {
- uint32_t hw_base = jz_sdio->hw_base;
- uint32_t value;
- /* reset mmc/sd controller */
- value = readl(hw_base + MSC_CTRL_OFFSET);
- value |= MSC_CTRL_RESET;
- writel(value, hw_base + MSC_CTRL_OFFSET);
- rt_thread_delay(1);
- value &= ~MSC_CTRL_RESET;
- writel(value, hw_base + MSC_CTRL_OFFSET);
- while(readl(hw_base + MSC_STAT_OFFSET) & MSC_STAT_IS_RESETTING);
- /* mask all IRQs */
- writel(0xffffffff, hw_base + MSC_IMASK_OFFSET);
- writel(0xffffffff, hw_base + MSC_IREG_OFFSET);
- /* set timeout */
- writel(0x100, hw_base + MSC_RESTO_OFFSET);
- writel(0x1ffffff, hw_base + MSC_RDTO_OFFSET);
- /* stop MMC/SD clock */
- jzmmc_stop_clock(hw_base);
- return 0;
- }
- /* RT-Thread SDIO interface */
- static void jzmmc_sdio_set_iocfg(struct rt_mmcsd_host *host,
- struct rt_mmcsd_io_cfg *io_cfg)
- {
- struct jzmmc_host * jz_sdio = host->private_data;
- rt_uint32_t clkdiv;
- LOG_D("set_iocfg clock: %d", io_cfg->clock);
- if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4)
- {
- LOG_D("MMC: Setting controller bus width to 4");
- jz_sdio->flags |= MSC_CMDAT_BUS_WIDTH_4BIT;
- }
- else
- {
- jz_sdio->flags &= ~(MSC_CMDAT_BUS_WIDTH_4BIT);
- LOG_D("MMC: Setting controller bus width to 1");
- }
- if (io_cfg->clock)
- {
- unsigned int clk_set = 0, clkrt = 0;
- unsigned int clk_want = io_cfg->clock;
- unsigned int lpm = 0;
- if (io_cfg->clock > 1 * 1000 * 1000)
- {
- io_cfg->clock = 1000 * 1000;
- }
- jzmmc_clk_autoctrl(jz_sdio, 1);
- if (clk_want > 3000000)
- {
- clk_set_rate(jz_sdio->clock, io_cfg->clock);
- }
- else
- {
- clk_set_rate(jz_sdio->clock, 24000000);
- }
- clk_set = clk_get_rate(jz_sdio->clock);
- while (clk_want < clk_set)
- {
- clkrt++;
- clk_set >>= 1;
- }
- if (clkrt > 7)
- {
- LOG_E("invalid value of CLKRT: "
- "ios->clock=%d clk_want=%d "
- "clk_set=%d clkrt=%X,",
- io_cfg->clock, clk_want, clk_set, clkrt);
- return;
- }
- if (!clkrt)
- {
- LOG_D("clk_want: %u, clk_set: %luHz", io_cfg->clock, clk_get_rate(jz_sdio->clock));
- }
- writel(clkrt, jz_sdio->hw_base + MSC_CLKRT_OFFSET);
- if (clk_set > 25000000)
- {
- lpm = (0x2 << LPM_DRV_SEL_SHF) | LPM_SMP_SEL;
- }
- if(jz_sdio->sdio_clk)
- {
- writel(lpm, jz_sdio->hw_base + MSC_LPM_OFFSET);
- writel(MSC_CTRL_CLOCK_START, jz_sdio->hw_base + MSC_CTRL_OFFSET);
- }
- else
- {
- lpm |= LPM_LPM;
- writel(lpm, jz_sdio->hw_base + MSC_LPM_OFFSET);
- }
- }
- else
- {
- jzmmc_clk_autoctrl(jz_sdio, 0);
- }
- /* maybe switch power to the card */
- switch (io_cfg->power_mode)
- {
- case MMCSD_POWER_OFF:
- LOG_D("MMCSD_POWER_OFF\r");
- break;
- case MMCSD_POWER_UP:
- LOG_D("MMCSD_POWER_UP\r");
- break;
- case MMCSD_POWER_ON:
- LOG_D("MMCSD_POWER_ON\r");
- jzmmc_hardware_init(jz_sdio);
- // jz_mmc_set_clock(jz_sdio, io_cfg->clock);
- break;
- default:
- LOG_D("unknown power_mode %d", io_cfg->power_mode);
- break;
- }
- }
- static rt_int32_t jzmmc_sdio_detect(struct rt_mmcsd_host *host)
- {
- LOG_D("jz47xx_SD_Detect");
- return 0;
- }
- static void jzmmc_sdio_enable_sdio_irq(struct rt_mmcsd_host *host,
- rt_int32_t enable)
- {
- LOG_D("jz47xx_sdio_enable_sdio_irq, enable:%d", enable);
- }
- static const struct rt_mmcsd_host_ops ops =
- {
- jzmmc_sdio_request,
- jzmmc_sdio_set_iocfg,
- jzmmc_sdio_detect,
- jzmmc_sdio_enable_sdio_irq,
- };
- int jzmmc_sdio_init(void)
- {
- struct rt_mmcsd_host *host = RT_NULL;
- struct jzmmc_host *jz_host = RT_NULL;
- #ifdef RT_USING_MSC0
- host = mmcsd_alloc_host();
- jz_host = rt_malloc_align(sizeof(struct jzmmc_host), 32);
- if(!(host && jz_host))
- {
- goto err;
- }
-
- rt_memset(jz_host, 0, sizeof(struct jzmmc_host));
- /* set hardware base firstly */
- jz_host->hw_base = MSC0_BASE;
- jz_host->clock = clk_get("cgu_msc0");
- jz_host->clock_gate = clk_get("msc0");
- #ifdef DMA_BUFFER
- jz_host->_dma_buffer = _dma_buffer_0;
- #endif
- /* init GPIO (msc0 boot)
- * name pin fun
- * X1000 MSC0_D0: PA23 1
- * X1000 MSC0_D1: PA22 1
- * X1000 MSC0_D2: PA21 1
- * X1000 MSC0_D3: PA20 1
- * X1000 MSC0_CMD: PA25 1
- * X1000 MSC0_CLK: PA24 1
- */
- {
- gpio_set_func(GPIO_PORT_A, GPIO_Pin_20, GPIO_FUNC_1);
- gpio_set_func(GPIO_PORT_A, GPIO_Pin_21, GPIO_FUNC_1);
- gpio_set_func(GPIO_PORT_A, GPIO_Pin_22, GPIO_FUNC_1);
- gpio_set_func(GPIO_PORT_A, GPIO_Pin_23, GPIO_FUNC_1);
- gpio_set_func(GPIO_PORT_A, GPIO_Pin_24, GPIO_FUNC_1);
- gpio_set_func(GPIO_PORT_A, GPIO_Pin_25, GPIO_FUNC_1);
- }
- /* enable MSC0 clock gate. */
- clk_enable(jz_host->clock_gate);
- jz_host->msc_clock = 25UL * 1000 * 1000; /* 25Mhz */
- host->freq_min = 400 * 1000; /* min 400Khz. */
- host->freq_max = 25 * 1000 * 1000; /* max 25Mhz. */
-
- // jz_host->msc_clock = 400 * 1000; /* 25Mhz */
- // host->freq_min = 400 * 1000; /* min 400Khz. */
- // host->freq_max = 400 * 1000; /* max 25Mhz. */
- /* set clock */
- clk_set_rate(jz_host->clock, 50000000);
- host->ops = &ops;
- host->valid_ocr = 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->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ | MMCSD_SUP_HIGHSPEED;
- host->max_seg_size = 65535;
- host->max_dma_segs = 2;
- host->max_blk_size = 512;
- host->max_blk_count = 4096;
- host->private_data = jz_host;
- jz_host->host = host;
- jz_host->irqno = IRQ_MSC0;
- rt_hw_interrupt_install(jz_host->irqno, jzmmc_isr, jz_host, "msc0");
- rt_hw_interrupt_mask(jz_host->irqno);
- mmcsd_change(host);
- #endif // RT_USING_MSC0
- #ifdef RT_USING_MSC1
- host = mmcsd_alloc_host();
- jz_host = rt_malloc(sizeof(struct jzmmc_host));
- if(!(host && jz_host))
- {
- goto err;
- }
- jz_host1 = jz_host; // for debug
- rt_memset(jz_host, 0, sizeof(struct jzmmc_host));
- jz_host->hw_base = MSC1_BASE;
- jz_host->clock = clk_get("cgu_msc1");
- jz_host->clock_gate = clk_get("msc1");
- #ifdef DMA_BUFFER
- jz_host->_dma_buffer = _dma_buffer_1;
- #endif
- /* init GPIO (paladin msc1 SDIO wifi)
- * name pin fun
- * X1000 MSC1_D0: PC02 0
- * X1000 MSC1_D1: PC03 0
- * X1000 MSC1_D2: PC04 0
- * X1000 MSC1_D3: PC05 0
- * X1000 MSC1_CMD: PC01 0
- * X1000 MSC1_CLK: PC00 0
- *
- */
- {
- gpio_set_func(GPIO_PORT_C, GPIO_Pin_0, GPIO_FUNC_0);
- gpio_set_func(GPIO_PORT_C, GPIO_Pin_1, GPIO_FUNC_0);
- gpio_set_func(GPIO_PORT_C, GPIO_Pin_2, GPIO_FUNC_0);
- gpio_set_func(GPIO_PORT_C, GPIO_Pin_3, GPIO_FUNC_0);
- gpio_set_func(GPIO_PORT_C, GPIO_Pin_4, GPIO_FUNC_0);
- gpio_set_func(GPIO_PORT_C, GPIO_Pin_5, GPIO_FUNC_0);
- }
- /* enable MSC1 clock gate. */
- clk_enable(jz_host->clock_gate);
- jz_host->msc_clock = 25UL * 1000 * 1000; /* 25Mhz */
- host->freq_min = 400 * 1000; /* min 400Khz. */
- host->freq_max = 25 * 1000 * 1000; /* max 25Mhz. */
- /* set clock */
- clk_set_rate(jz_host->clock, BOARD_EXTAL_CLK);
- host->ops = &ops;
- host->valid_ocr = 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 = 65535;
- host->max_dma_segs = 2;
- host->max_blk_size = 512;
- host->max_blk_count = 4096;
- host->private_data = jz_host;
- jz_host->host = host;
- jz_host->irqno = IRQ_MSC1;
- rt_hw_interrupt_install(jz_host->irqno, jzmmc_isr, jz_host, "msc1");
- rt_hw_interrupt_mask(jz_host->irqno);
- mmcsd_change(host);
- #endif // RT_USING_MSC1
- return RT_EOK;
- err:
- if(host)
- {
- mmcsd_free_host(host);
- }
- if(jz_host)
- {
- rt_free(jz_host);
- }
- return -RT_ENOMEM;
- }
- INIT_DEVICE_EXPORT(jzmmc_sdio_init);
- #include <finsh.h>
- int msc_status(void)
- {
- uint32_t value;
- if (jz_host1)
- {
- value = msc_readl(jz_host1, MSC_STAT_OFFSET);
- rt_kprintf("status: 0x%08x\n", value);
- value = msc_readl(jz_host1, MSC_IMASK_OFFSET);
- rt_kprintf("mask : 0x%08x -> 0x%08x\n", value, ~value);
- value = msc_readl(jz_host1, MSC_IREG_OFFSET);
- rt_kprintf("iflag : 0x%08x\n", value);
- rt_kprintf("dma : nda 0x%08x, da 0x%08x, len 0x%04x, cmd 0x%08x\n", msc_readl(jz_host1, MSC_DMANDA_OFFSET),
- msc_readl(jz_host1, MSC_DMADA_OFFSET),
- msc_readl(jz_host1, MSC_DMALEN_OFFSET),
- msc_readl(jz_host1, MSC_DMACMD_OFFSET));
- rt_kprintf("clock : %s\n", (stopping_clock == 1)? "stopping" : "none stopping");
- }
- return 0;
- }
- MSH_CMD_EXPORT(msc_status, dump msc status);
- int msc_log(int argc, char** argv)
- {
- if (argc == 2)
- sdio_log = atoi(argv[1]);
- return 0;
- }
- MSH_CMD_EXPORT(msc_log, set msc log enable);
|