123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /*
- * Copyright (c) 2006-2018, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2020-04-15 Jonne first version for s3c2440 mmc controller
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #include <drivers/mmcsd_core.h>
- #include <s3c24x0.h>
- #define S3C_PCLK 50000000
- static void s3c_mmc_set_clk(struct rt_mmcsd_host *host, rt_uint32_t clock)
- {
- rt_uint32_t prescale;
- rt_uint32_t realClk;
- for(prescale = 0; prescale < 256; ++prescale)
- {
- realClk = S3C_PCLK / (1 + prescale);
- if(realClk <= clock)
- {
- break;
- }
- }
- SDIPRE = prescale;
- host->io_cfg.clock = realClk;
- }
- static rt_uint32_t s3c_mmc_send_cmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd)
- {
- rt_uint32_t ccon;
- rt_uint32_t cmdSta;
- SDICARG = cmd->arg;
- ccon = cmd->cmd_code & 0x3f;
- ccon |= (0 << 7) | (1 << 6); /* two start bits*/
- ccon |= (1 << 8);/* command start*/
- if(cmd->flags & 0xF)
- {
- // Need response
- ccon |= (1 << 9);
- }
- if((cmd->flags & 0xF) == RESP_R2)
- {
- // R2 need 136bit response
- ccon |= (1 << 10);
- }
- SDICCON = ccon; /* start cmd */
- if(cmd->flags & 0xF)
- {
- cmdSta = SDICSTA;
- while((cmdSta & 0x200) != 0x200 && (cmdSta & 0x400) != 0x400)
- {
- cmdSta = SDICSTA;
- }
- if((cmdSta & 0x1000) == 0x1000 && (cmd->flags & 0xF) != RESP_R3 && (cmd->flags & 0xF) != RESP_R4)
- {
- // crc error, but R3 R4 ignore it
- SDICSTA = cmdSta;
- return -RT_ERROR;
- }
- if((cmdSta & 0xF00) != 0xa00)
- {
- SDICSTA = cmdSta;
- return -RT_ERROR;
- }
- cmd->resp[0] = SDIRSP0;
- if((cmd->flags & 0xF) == RESP_R2)
- {
- cmd->resp[1] = SDIRSP1;
- cmd->resp[2] = SDIRSP2;
- cmd->resp[3] = SDIRSP3;
- }
- }
- else
- {
- cmdSta = SDICSTA;
- while((cmdSta & 0x800) != 0x800)
- {
- cmdSta = SDICSTA;
- }
- }
- SDICSTA = cmdSta; // clear current status
- return RT_EOK;
- }
- static rt_uint32_t s3c_mmc_xfer_data(struct rt_mmcsd_data *data)
- {
- rt_uint32_t status;
- rt_uint32_t xfer_size;
- rt_uint32_t handled_size = 0;
- rt_uint32_t *pBuf = RT_NULL;
- if(data == RT_NULL)
- {
- return -RT_ERROR;
- }
- xfer_size = data->blks * data->blksize;
- pBuf = data->buf;
- if(data->flags & DATA_DIR_READ)
- {
- while(handled_size < xfer_size)
- {
- if ((SDIDSTA & 0x20) == 0x20)
- {
- SDIDSTA = (0x1 << 0x5);
- break;
- }
- status = SDIFSTA;
- if ((status & 0x1000) == 0x1000)
- {
- *pBuf++ = SDIDAT;
- handled_size += 4;
- }
- }
- }
- else
- {
- while(handled_size < xfer_size)
- {
- status = SDIFSTA;
- if ((status & 0x2000) == 0x2000)
- {
- SDIDAT = *pBuf++;
- handled_size += 4;
- }
- }
- }
- // wait for end
- status = SDIDSTA;
- while((status & 0x30) == 0)
- {
- status = SDIDSTA;
- }
- SDIDSTA = status;
- if ((status & 0xfc) != 0x10)
- {
- return -RT_ERROR;
- }
- SDIDCON = SDIDCON & ~(7<<12);
- SDIFSTA = SDIFSTA & 0x200;
- SDIDSTA = 0x10;
- return RT_EOK;
- }
- static void mmc_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
- {
- rt_uint32_t ret;
- struct rt_mmcsd_cmd *cmd;
- struct rt_mmcsd_data *data;
- rt_uint32_t val;
- rt_uint32_t tryCnt = 0;
- if(req->cmd == RT_NULL)
- {
- goto out;
- }
- cmd = req->cmd;
- /* prepare for data transfer*/
- if(req->data != RT_NULL)
- {
- SDIFSTA = SDIFSTA | (1<<16); // reset fifo
- while(SDIDSTA & 0x03)
- {
- if(tryCnt++ > 500)
- {
- break;
- SDIDSTA = SDIDSTA;
- }
- }
- data = req->data;
- if((data->blksize & 0x3) != 0)
- {
- goto out;
- }
- val = (2 << 22) //word transfer
- | (1 << 20) // transmet after response
- | (1 << 19) // reciveve after command sent
- | (1 << 17) // block data transfer
- | (1 << 14); // data start
- if(host->io_cfg.bus_width == MMCSD_BUS_WIDTH_4)
- {
- val |= (1 << 16); // wide bus mode(4bit data)
- }
- if(data->flags & DATA_DIR_READ)
- {
- // for data read
- val |= (2 << 12);
- }
- else
- {
- val |= (3 << 12);
- }
- val |= (data->blks & 0xFFF);
- SDIDCON = val;
- SDIBSIZE = data->blksize;
- SDIDTIMER = 0x7fffff;
- }
- ret = s3c_mmc_send_cmd(host,req->cmd);
- if(ret != RT_EOK) {
- cmd->err = ret;
- goto out;
- }
- if(req->data != RT_NULL)
- {
- /*do transfer data*/
- ret = s3c_mmc_xfer_data(data);
- if(ret != RT_EOK)
- {
- data->err = ret;
- goto out;
- }
- }
- out:
- mmcsd_req_complete(host);
- }
- static void mmc_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
- {
- switch (io_cfg->power_mode) {
- case MMCSD_POWER_ON:
- case MMCSD_POWER_UP:
- /* Enable PCLK into SDI Block */
- CLKCON |= 1 << 9;
-
- /* Setup GPIO as SD and SDCMD, SDDAT[3:0] Pull up En */
- GPEUP = GPEUP & (~(0x3f << 5)) | (0x01 << 5);
- GPECON = GPECON & (~(0xfff << 10)) | (0xaaa << 10);
- break;
- case MMCSD_POWER_OFF:
- default:
- break;
- }
- s3c_mmc_set_clk(host, io_cfg->clock);
- SDICON = 1;
- }
- static rt_int32_t mmc_get_card_status(struct rt_mmcsd_host *host)
- {
- return RT_EOK;
- }
- static void mmc_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t en)
- {
- }
- static const struct rt_mmcsd_host_ops ops =
- {
- mmc_request,
- mmc_set_iocfg,
- mmc_get_card_status,
- mmc_enable_sdio_irq
- };
- int s3c_sdio_init(void)
- {
- struct rt_mmcsd_host * host = RT_NULL;
- host = mmcsd_alloc_host();
- if (!host)
- {
- goto err;
- }
- host->ops = &ops;
- host->freq_min = 300000;
- host->freq_max = 50000000;
- host->valid_ocr = VDD_32_33 | VDD_33_34;
- host->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED | MMCSD_SUP_SDIO_IRQ | MMCSD_BUSWIDTH_4;
- host->max_seg_size = 2048;
- host->max_dma_segs = 10;
- host->max_blk_size = 512;
- host->max_blk_count = 4096;
- mmcsd_change(host);
- return RT_EOK;
- err:
- if(host) rt_free(host);
- return -RT_EIO;
- }
- INIT_DEVICE_EXPORT(s3c_sdio_init);
|