328 lines
6.7 KiB
C
328 lines
6.7 KiB
C
/*
|
|
* 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/dev_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);
|