⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3cmci.c

📁 《ARM嵌入式Linux设备驱动实例开发》
💻 C
📖 第 1 页 / 共 2 页
字号:
	//if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;#ifdef CONFIG_MMC_DEBUG	dbg_dumpcmd(host, cmd, debug_as_failure);#endif	//Cleanup controller	writel(0, host->base + S3C2410_SDICMDARG);	writel(0, host->base + S3C2410_SDIDCON);	writel(0, host->base + S3C2410_SDICMDCON);	writel(0, host->base + host->sdiimsk);	if (cmd->data && cmd->error)		cmd->data->error = cmd->error;	if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {		host->cmd_is_stop = 1;		s3cmci_send_request(host->mmc);		return;	}	// If we have no data transfer we are finished here	if (!mrq->data)		goto request_done;	// Calulate the amout of bytes transfer, but only if there was	// no error	if (mrq->data->error == MMC_ERR_NONE) {		mrq->data->bytes_xfered =			(mrq->data->blocks * mrq->data->blksz);	} else {		mrq->data->bytes_xfered = 0;	}	// If we had an error while transfering data we flush the	// DMA channel and the fifo to clear out any garbage	if (mrq->data->error != MMC_ERR_NONE) {		if (host->dodma)			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);			//reset fifo			mci_con = readl(host->base + S3C2410_SDICON);			mci_con|= S3C2410_SDICON_FIFORESET;			writel(mci_con, host->base + S3C2410_SDICON);			}request_done:	host->complete_what = COMPLETION_NONE;	host->mrq = NULL;	mmc_request_done(host->mmc, mrq);}void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source){	static int setup_ok = 0;	static enum s3c2410_dmasrc last_source = -1;	if (last_source == source)		return;	last_source = source;	s3c2410_dma_devconfig(host->dma, source, 3,		host->mem->start + host->sdidata);	if (!setup_ok) {		s3c2410_dma_config(host->dma, 4,			(S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));		s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);		s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);		setup_ok = 1;	}}static void s3cmci_send_command(struct s3cmci_host *host,					struct mmc_command *cmd){	u32 ccon, imsk;	imsk  = S3C2410_SDIIMSK_CMDTIMEOUT |		S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |		S3C2410_SDIIMSK_RESPONSECRC;	enable_imask(host, imsk);	if (cmd->data) {		host->complete_what = COMPLETION_XFERFINISH_RSPFIN;	} else if (cmd->flags & MMC_RSP_PRESENT) {		host->complete_what = COMPLETION_RSPFIN;	} else {		host->complete_what = COMPLETION_CMDSENT;	}	writel(cmd->arg, host->base + S3C2410_SDICMDARG);	ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;	ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;	if (cmd->flags & MMC_RSP_PRESENT)		ccon |= S3C2410_SDICMDCON_WAITRSP;	if (cmd->flags & MMC_RSP_136)		ccon|= S3C2410_SDICMDCON_LONGRSP;	writel(ccon, host->base + S3C2410_SDICMDCON);}static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data){	u32 dcon, imsk, stoptries=3;	/* write DCON register */	if (!data) {		writel(0, host->base + S3C2410_SDIDCON);		return 0;	}	while(readl(host->base + S3C2410_SDIDSTA) &		(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {		dbg(host, dbg_err,			"mci_setup_data() transfer stillin progress.\n");		writel(0, host->base + S3C2410_SDIDCON);		s3cmci_reset(host);		if (0 == (stoptries--)) {#ifdef CONFIG_MMC_DEBUG			dbg_dumpregs(host, "DRF");#endif			return -EINVAL;		}	}	dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;	if (host->dodma) {		dcon |= S3C2410_SDIDCON_DMAEN;	}	if (host->bus_width == MMC_BUS_WIDTH_4) {		dcon |= S3C2410_SDIDCON_WIDEBUS;	}	if (!(data->flags & MMC_DATA_STREAM)) {		dcon |= S3C2410_SDIDCON_BLOCKMODE;	}	if (data->flags & MMC_DATA_WRITE) {		dcon |= S3C2410_SDIDCON_TXAFTERRESP;		dcon |= S3C2410_SDIDCON_XFER_TXSTART;	}	if (data->flags & MMC_DATA_READ) {		dcon |= S3C2410_SDIDCON_RXAFTERCMD;		dcon |= S3C2410_SDIDCON_XFER_RXSTART;	}	writel(dcon, host->base + S3C2410_SDIDCON);	/* write BSIZE register */	writel(data->blksz, host->base + S3C2410_SDIBSIZE);	/* add to IMASK register */	imsk =	S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |		S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;	enable_imask(host, imsk);	/* write TIMER register */		writel(0x0000FFFF, host->base + S3C2410_SDITIMER);		//FIX: set slow clock to prevent timeouts on read		if (data->flags & MMC_DATA_READ) {			writel(0xFF, host->base + S3C2410_SDIPRE);		}		//debug_dump_registers(host, "Data setup:");	return 0;}static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data){	int rw = (data->flags & MMC_DATA_WRITE)?1:0;	if (rw != ((data->flags & MMC_DATA_READ)?0:1))		return -EINVAL;	host->pio_sgptr = 0;	host->pio_words = 0;	host->pio_count = 0;	host->pio_active = rw?XFER_WRITE:XFER_READ;	if (rw) {		do_pio_write(host);		enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);	} else {		enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF			| S3C2410_SDIIMSK_RXFIFOLAST);	}	return 0;}static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data){	int dma_len, i;	int rw = (data->flags & MMC_DATA_WRITE)?1:0;	if (rw != ((data->flags & MMC_DATA_READ)?0:1))		return -EINVAL;	s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);	dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,				(rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);	if (dma_len == 0)		return -ENOMEM;	host->dma_complete = 0;	host->dmatogo = dma_len;	for (i = 0; i < dma_len; i++) {		int res;		dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,			sg_dma_address(&data->sg[i]),			sg_dma_len(&data->sg[i]));		res = s3c2410_dma_enqueue(host->dma, (void *) host,				sg_dma_address(&data->sg[i]),				sg_dma_len(&data->sg[i]));		if (res) {			s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);			return -EBUSY;		} 	}	s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);	return 0;}static void s3cmci_send_request(struct mmc_host *mmc){	struct s3cmci_host *host = mmc_priv(mmc);	struct mmc_request *mrq = host->mrq;	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;	host->ccnt++;	if (cmd->opcode == SD_SWITCH && cmd->arg == 0x80fffff1)	{ 		struct scatterlist *sg = cmd->data->sg; 		memset(page_address(sg->page) + sg->offset, 0, sg->length); 		cmd->error = MMC_ERR_NONE; 		cmd->data->error = MMC_ERR_NONE; 		dbg(host, dbg_conf, "suppress SD_SWITCH @%ukHz\n", mmc->f_max/1000); 		mmc_request_done(mmc, mrq); 		return; 	}#ifdef CONFIG_MMC_DEBUG	prepare_dbgmsg(host, cmd, host->cmd_is_stop);#endif	//Clear command, data and fifo status registers	//Fifo clear only necessary on 2440, but doesn't hurt on 2410	writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);	writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);	writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);	if (cmd->data) {		int res;		res = s3cmci_setup_data(host, cmd->data);		host->dcnt++;		if (res) {			cmd->error = MMC_ERR_DMA;			cmd->data->error = MMC_ERR_DMA;			mmc_request_done(mmc, mrq);			return;		}		if (host->dodma) {			res = s3cmci_prepare_dma(host, cmd->data);		} else {			res = s3cmci_prepare_pio(host, cmd->data);		}		if (res) {			cmd->error = MMC_ERR_DMA;			cmd->data->error = MMC_ERR_DMA;			mmc_request_done(mmc, mrq);			return;		}	}	s3cmci_send_command(host, cmd);	enable_irq(host->irq);}static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq){ 	struct s3cmci_host *host = mmc_priv(mmc);	host->cmd_is_stop = 0;	host->mrq = mrq;	s3cmci_send_request(mmc);}static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){	struct s3cmci_host *host = mmc_priv(mmc);	u32 mci_psc, mci_con;	//Set power	mci_con = readl(host->base + S3C2410_SDICON);	switch(ios->power_mode) {		case MMC_POWER_ON:		case MMC_POWER_UP:			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);			s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);			s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);			s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);			s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);			s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);			if (ios->clock == 0)				mci_con|=S3C2410_SDICON_FIFORESET;			break;		case MMC_POWER_OFF:		default:			s3c2410_gpio_setpin(S3C2410_GPE5, 0);			s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);			break;	}	//Set clock	for (mci_psc=0; mci_psc<255; mci_psc++) {		host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));		if (host->real_rate <= ios->clock)			break;	}	if(mci_psc > 255) mci_psc = 255;	host->prescaler = mci_psc;	writel(host->prescaler, host->base + S3C2410_SDIPRE);	//If requested clock is 0, real_rate will be 0, too	if (ios->clock == 0)		host->real_rate = 0;	//Set CLOCK_ENABLE	if (ios->clock)		mci_con |= S3C2410_SDICON_CLOCKTYPE;	else		mci_con &=~S3C2410_SDICON_CLOCKTYPE;	writel(mci_con, host->base + S3C2410_SDICON);	if ((ios->power_mode==MMC_POWER_ON)		|| (ios->power_mode==MMC_POWER_UP)) {		dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",			host->real_rate/1000, ios->clock/1000);	} else {		dbg(host, dbg_conf, "powered down.\n");	}	host->bus_width = ios->bus_width;}static void s3cmci_reset(struct s3cmci_host *host){	u32 con = readl(host->base + S3C2410_SDICON);	con |= S3C2440_SDICON_SDRESET;	writel(con, host->base + S3C2410_SDICON);}static struct mmc_host_ops s3cmci_ops = {	.request	= s3cmci_request,	.set_ios	= s3cmci_set_ios,};static int s3cmci_probe(struct platform_device *pdev){	struct mmc_host 	*mmc;	struct s3cmci_host 	*host;	int ret;	mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);	if (!mmc) {		ret = -ENOMEM;		goto probe_out;	}	host = mmc_priv(mmc);	host->mmc 	= mmc;	host->pdev	= pdev;	spin_lock_init(&host->complete_lock);	tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);	host->sdiimsk	= S3C2410_SDIIMSK;	host->sdidata	= S3C2410_SDIDATA;	host->clk_div	= 2;		host->dodma		= 0;	host->complete_what 	= COMPLETION_NONE;	host->pio_active 	= XFER_NONE;	host->dma		= S3CMCI_DMA;	host->irq_cd		= IRQ_EINT2;	host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);	if (!host->mem) {		dev_err(&pdev->dev,			"failed to get io memory region resouce.\n");		ret = -ENOENT;		goto probe_free_host;	}	host->mem = request_mem_region(host->mem->start,		RESSIZE(host->mem), pdev->name);	if (!host->mem) {		dev_err(&pdev->dev, "failed to request io memory region.\n");		ret = -ENOENT;		goto probe_free_host;	}	host->base = ioremap(host->mem->start, RESSIZE(host->mem));	if (host->base == 0) {		dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");		ret = -EINVAL;		goto probe_free_mem_region;	}	host->irq = platform_get_irq(pdev, 0);	if (host->irq == 0) {		dev_err(&pdev->dev, "failed to get interrupt resouce.\n");		ret = -EINVAL;		goto probe_iounmap;	}	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {		dev_err(&pdev->dev, "failed to request mci interrupt.\n");		ret = -ENOENT;		goto probe_iounmap;	}	disable_irq(host->irq);	s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);	if (request_irq(host->irq_cd, s3cmci_irq_cd, \		SA_TRIGGER_RISING | SA_TRIGGER_FALLING, DRIVER_NAME, host)) {		dev_err(&pdev->dev,			"failed to request card detect interrupt.\n");		ret = -ENOENT;		goto probe_free_irq;	}	if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {		dev_err(&pdev->dev, "unable to get DMA channel.\n");		ret = -EBUSY;		goto probe_free_irq_cd;	}	host->clk = clk_get(&pdev->dev, "sdi");	if (IS_ERR(host->clk)) {		dev_err(&pdev->dev, "failed to find clock source.\n");		ret = PTR_ERR(host->clk);		host->clk = NULL;		goto probe_free_dma;	}	if ((ret = clk_enable(host->clk))) {		dev_err(&pdev->dev, "failed to enable clock source.\n");		goto clk_free;	}	host->clk_rate = clk_get_rate(host->clk);	mmc->ops 	= &s3cmci_ops;	mmc->ocr_avail	= MMC_VDD_32_33;	mmc->caps	= MMC_CAP_4_BIT_DATA;	mmc->f_min 	= host->clk_rate / (host->clk_div * 256);	mmc->f_max 	= host->clk_rate / host->clk_div;	mmc->max_sectors	= 4095;	mmc->max_seg_size	= mmc->max_sectors << 9;	mmc->max_phys_segs	= 128;	mmc->max_hw_segs	= 128;	if ((ret = mmc_add_host(mmc))) {		dev_err(&pdev->dev, "failed to add mmc host.\n");		goto free_dmabuf;	}	platform_set_drvdata(pdev, mmc);	dev_info(&pdev->dev,"initialisation done.\n");	return 0; free_dmabuf:	clk_disable(host->clk); clk_free:	clk_put(host->clk); probe_free_dma: 	s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); probe_free_irq_cd: 	free_irq(host->irq_cd, host); probe_free_irq: 	free_irq(host->irq, host); probe_iounmap:	iounmap(host->base); probe_free_mem_region:	release_mem_region(host->mem->start, RESSIZE(host->mem)); probe_free_host:	mmc_free_host(mmc); probe_out:	return ret;}static int s3cmci_remove(struct platform_device *pdev){	struct mmc_host 	*mmc  = platform_get_drvdata(pdev);	struct s3cmci_host 	*host = mmc_priv(mmc);	mmc_remove_host(mmc);	clk_disable(host->clk);	clk_put(host->clk);	s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client); 	free_irq(host->irq_cd, host); 	free_irq(host->irq, host);	iounmap(host->base);	release_mem_region(host->mem->start, RESSIZE(host->mem));	mmc_free_host(mmc);	return 0;}#ifdef CONFIG_PMstatic int s3cmci_suspend(struct platform_device *dev, pm_message_t state){	struct mmc_host *mmc = platform_get_drvdata(dev);	return  mmc_suspend_host(mmc, state);}static int s3cmci_resume(struct platform_device *dev){	struct mmc_host *mmc = platform_get_drvdata(dev);	return mmc_resume_host(mmc);}#else /* CONFIG_PM */#define s3cmci_suspend NULL#define s3cmci_resume NULL#endif /* CONFIG_PM */static struct platform_driver s3cmci_driver_2410 ={	.driver.name	= "s3c2410-sdi",	.probe		= s3cmci_probe,	.remove		= s3cmci_remove,	.suspend	= s3cmci_suspend,	.resume		= s3cmci_resume,};static int __init s3cmci_init(void){	platform_driver_register(&s3cmci_driver_2410);	return 0;}static void __exit s3cmci_exit(void){	platform_driver_unregister(&s3cmci_driver_2410);}module_init(s3cmci_init);module_exit(s3cmci_exit);MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -