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

📄 imxmmc.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 2 页
字号:
	/* This is unfortunately required */	dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n",		stat);	udelay(20);	/* required for clocks < 8MHz*/	if(host->dma_dir == DMA_FROM_DEVICE) {		imxmci_busy_wait_for_status(host, &stat,				STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE,				50, "imxmci_cpu_driven_data read");		while((stat & (STATUS_APPL_BUFF_FF |  STATUS_DATA_TRANS_DONE)) &&		      (host->data_cnt < 512)) {			udelay(20);	/* required for clocks < 8MHz*/			for(i = burst_len; i>=2 ; i-=2) {				u16 data;				data = MMC_BUFFER_ACCESS;				udelay(10);	/* required for clocks < 8MHz*/				if(host->data_cnt+2 <= host->dma_size) {					*(host->data_ptr++) = data;				} else {					if(host->data_cnt < host->dma_size)						*(u8*)(host->data_ptr) = data;				}				host->data_cnt += 2;			}			stat = MMC_STATUS;			dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",				host->data_cnt, burst_len, stat);		}		if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))			trans_done = 1;		if(host->dma_size & 0x1ff)			stat &= ~STATUS_CRC_READ_ERR;	} else {		imxmci_busy_wait_for_status(host, &stat,				STATUS_APPL_BUFF_FE,				20, "imxmci_cpu_driven_data write");		while((stat & STATUS_APPL_BUFF_FE) &&		      (host->data_cnt < host->dma_size)) {			if(burst_len >= host->dma_size - host->data_cnt) {				burst_len = host->dma_size - host->data_cnt;				host->data_cnt = host->dma_size;				trans_done = 1;			} else {				host->data_cnt += burst_len;			}			for(i = burst_len; i>0 ; i-=2)				MMC_BUFFER_ACCESS = *(host->data_ptr++);			stat = MMC_STATUS;			dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",				burst_len, stat);		}	}	*pstat = stat;	return trans_done;}static void imxmci_dma_irq(int dma, void *devid){	struct imxmci_host *host = devid;	uint32_t stat = MMC_STATUS;	atomic_set(&host->stuck_timeout, 0);	host->status_reg = stat;	set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);	tasklet_schedule(&host->tasklet);}static irqreturn_t imxmci_irq(int irq, void *devid){	struct imxmci_host *host = devid;	uint32_t stat = MMC_STATUS;	int handled = 1;	MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT;	atomic_set(&host->stuck_timeout, 0);	host->status_reg = stat;	set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);	set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);	tasklet_schedule(&host->tasklet);	return IRQ_RETVAL(handled);;}static void imxmci_tasklet_fnc(unsigned long data){	struct imxmci_host *host = (struct imxmci_host *)data;	u32 stat;	unsigned int data_dir_mask = 0;	/* STATUS_WR_CRC_ERROR_CODE_MASK */	int timeout = 0;	if(atomic_read(&host->stuck_timeout) > 4) {		char *what;		timeout = 1;		stat = MMC_STATUS;		host->status_reg = stat;		if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))			if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))				what = "RESP+DMA";			else				what = "RESP";		else			if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))				if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))					what = "DATA";				else					what = "DMA";			else				what = "???";		dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",		       what, stat, MMC_INT_MASK);		dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",		       MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma));		dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",		       host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<<host->actual_bus_width, host->dma_size);	}	if(!host->present || timeout)		host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |				    STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;	if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {		clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);		stat = MMC_STATUS;		/*		 * This is not required in theory, but there is chance to miss some flag		 * which clears automatically by mask write, FreeScale original code keeps		 * stat from IRQ time so do I		 */		stat |= host->status_reg;		if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {			imxmci_busy_wait_for_status(host, &stat,					STATUS_END_CMD_RESP | STATUS_ERR_MASK,					20, "imxmci_tasklet_fnc resp (ERRATUM #4)");		}		if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {			if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))				imxmci_cmd_done(host, stat);			if(host->data && (stat & STATUS_ERR_MASK))				imxmci_data_done(host, stat);		}		if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {			stat |= MMC_STATUS;			if(imxmci_cpu_driven_data(host, &stat)){				if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))					imxmci_cmd_done(host, stat);				atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,							&host->pending_events);				imxmci_data_done(host, stat);			}		}	}	if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&	   !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {		stat = MMC_STATUS;		/* Same as above */		stat |= host->status_reg;		if(host->dma_dir == DMA_TO_DEVICE) {			data_dir_mask = STATUS_WRITE_OP_DONE;		} else {			data_dir_mask = STATUS_DATA_TRANS_DONE;		}		if(stat & data_dir_mask) {			clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);			imxmci_data_done(host, stat);		}	}	if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {		if(host->cmd)			imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);		if(host->data)			imxmci_data_done(host, STATUS_TIME_OUT_READ |					 STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);		if(host->req)			imxmci_finish_request(host, host->req);		mmc_detect_change(host->mmc, msecs_to_jiffies(100));	}}static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req){	struct imxmci_host *host = mmc_priv(mmc);	unsigned int cmdat;	WARN_ON(host->req != NULL);	host->req = req;	cmdat = 0;	if (req->data) {		imxmci_setup_data(host, req->data);		cmdat |= CMD_DAT_CONT_DATA_ENABLE;		if (req->data->flags & MMC_DATA_WRITE)			cmdat |= CMD_DAT_CONT_WRITE;		if (req->data->flags & MMC_DATA_STREAM) {			cmdat |= CMD_DAT_CONT_STREAM_BLOCK;		}	}	imxmci_start_cmd(host, req->cmd, cmdat);}#define CLK_RATE 19200000static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){	struct imxmci_host *host = mmc_priv(mmc);	int prescaler;	if( ios->bus_width==MMC_BUS_WIDTH_4 ) {		host->actual_bus_width = MMC_BUS_WIDTH_4;		imx_gpio_mode(PB11_PF_SD_DAT3);	}else{		host->actual_bus_width = MMC_BUS_WIDTH_1;		imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);	}	if ( host->power_mode != ios->power_mode ) {		switch (ios->power_mode) {		case MMC_POWER_OFF:        		break;		case MMC_POWER_UP:			set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);        		break;		case MMC_POWER_ON:        		break;		}		host->power_mode = ios->power_mode;	}	if ( ios->clock ) {		unsigned int clk;		/* The prescaler is 5 for PERCLK2 equal to 96MHz		 * then 96MHz / 5 = 19.2 MHz		 */		clk=imx_get_perclk2();		prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;		switch(prescaler) {		case 0:		case 1:	prescaler = 0;			break;		case 2:	prescaler = 1;			break;		case 3:	prescaler = 2;			break;		case 4:	prescaler = 4;			break;		default:		case 5:	prescaler = 5;			break;		}		dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",			clk, prescaler);		for(clk=0; clk<8; clk++) {			int x;			x = CLK_RATE / (1<<clk);			if( x <= ios->clock)				break;		}		MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */		imxmci_stop_clock(host);		MMC_CLK_RATE = (prescaler<<3) | clk;		/*		 * Under my understanding, clock should not be started there, because it would		 * initiate SDHC sequencer and send last or random command into card		 */		/*imxmci_start_clock(host);*/		dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);	} else {		imxmci_stop_clock(host);	}}static const struct mmc_host_ops imxmci_ops = {	.request	= imxmci_request,	.set_ios	= imxmci_set_ios,};static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr){	int i;	for (i = 0; i < dev->num_resources; i++)		if (dev->resource[i].flags == mask && nr-- == 0)			return &dev->resource[i];	return NULL;}static int platform_device_irq(struct platform_device *dev, int nr){	int i;	for (i = 0; i < dev->num_resources; i++)		if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)			return dev->resource[i].start;	return NO_IRQ;}static void imxmci_check_status(unsigned long data){	struct imxmci_host *host = (struct imxmci_host *)data;	if( host->pdata->card_present() != host->present ) {		host->present ^= 1;		dev_info(mmc_dev(host->mmc), "card %s\n",		      host->present ? "inserted" : "removed");		set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events);		tasklet_schedule(&host->tasklet);	}	if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||	   test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {		atomic_inc(&host->stuck_timeout);		if(atomic_read(&host->stuck_timeout) > 4)			tasklet_schedule(&host->tasklet);	} else {		atomic_set(&host->stuck_timeout, 0);	}	mod_timer(&host->timer, jiffies + (HZ>>1));}static int imxmci_probe(struct platform_device *pdev){	struct mmc_host *mmc;	struct imxmci_host *host = NULL;	struct resource *r;	int ret = 0, irq;	printk(KERN_INFO "i.MX mmc driver\n");	r = platform_device_resource(pdev, IORESOURCE_MEM, 0);	irq = platform_device_irq(pdev, 0);	if (!r || irq == NO_IRQ)		return -ENXIO;	r = request_mem_region(r->start, 0x100, "IMXMCI");	if (!r)		return -EBUSY;	mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);	if (!mmc) {		ret = -ENOMEM;		goto out;	}	mmc->ops = &imxmci_ops;	mmc->f_min = 150000;	mmc->f_max = CLK_RATE/2;	mmc->ocr_avail = MMC_VDD_32_33;	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK;	/* MMC core transfer sizes tunable parameters */	mmc->max_hw_segs = 64;	mmc->max_phys_segs = 64;	mmc->max_sectors = 64;		/* default 1 << (PAGE_CACHE_SHIFT - 9) */	mmc->max_seg_size = 64*512;	/* default PAGE_CACHE_SIZE */	host = mmc_priv(mmc);	host->mmc = mmc;	host->dma_allocated = 0;	host->pdata = pdev->dev.platform_data;	spin_lock_init(&host->lock);	host->res = r;	host->irq = irq;	imx_gpio_mode(PB8_PF_SD_DAT0);	imx_gpio_mode(PB9_PF_SD_DAT1);	imx_gpio_mode(PB10_PF_SD_DAT2);	/* Configured as GPIO with pull-up to ensure right MCC card mode */	/* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */	imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);	/* imx_gpio_mode(PB11_PF_SD_DAT3); */	imx_gpio_mode(PB12_PF_SD_CLK);	imx_gpio_mode(PB13_PF_SD_CMD);	imxmci_softreset();	if ( MMC_REV_NO != 0x390 ) {		dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",		        MMC_REV_NO);		goto out;	}	MMC_READ_TO = 0x2db4; /* recommended in data sheet */	host->imask = IMXMCI_INT_MASK_DEFAULT;	MMC_INT_MASK = host->imask;	if(imx_dma_request_by_prio(&host->dma, DRIVER_NAME, DMA_PRIO_LOW)<0){		dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");		ret = -EBUSY;		goto out;	}	host->dma_allocated=1;	imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);	tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);	host->status_reg=0;	host->pending_events=0;	ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host);	if (ret)		goto out;	host->present = host->pdata->card_present();	init_timer(&host->timer);	host->timer.data = (unsigned long)host;	host->timer.function = imxmci_check_status;	add_timer(&host->timer);	mod_timer(&host->timer, jiffies + (HZ>>1));	platform_set_drvdata(pdev, mmc);	mmc_add_host(mmc);	return 0;out:	if (host) {		if(host->dma_allocated){			imx_dma_free(host->dma);			host->dma_allocated=0;		}	}	if (mmc)		mmc_free_host(mmc);	release_resource(r);	return ret;}static int imxmci_remove(struct platform_device *pdev){	struct mmc_host *mmc = platform_get_drvdata(pdev);	platform_set_drvdata(pdev, NULL);	if (mmc) {		struct imxmci_host *host = mmc_priv(mmc);		tasklet_disable(&host->tasklet);		del_timer_sync(&host->timer);		mmc_remove_host(mmc);		free_irq(host->irq, host);		if(host->dma_allocated){			imx_dma_free(host->dma);			host->dma_allocated=0;		}		tasklet_kill(&host->tasklet);		release_resource(host->res);		mmc_free_host(mmc);	}	return 0;}#ifdef CONFIG_PMstatic int imxmci_suspend(struct platform_device *dev, pm_message_t state){	struct mmc_host *mmc = platform_get_drvdata(dev);	int ret = 0;	if (mmc)		ret = mmc_suspend_host(mmc, state);	return ret;}static int imxmci_resume(struct platform_device *dev){	struct mmc_host *mmc = platform_get_drvdata(dev);	struct imxmci_host *host;	int ret = 0;	if (mmc) {		host = mmc_priv(mmc);		if(host)			set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);		ret = mmc_resume_host(mmc);	}	return ret;}#else#define imxmci_suspend  NULL#define imxmci_resume   NULL#endif /* CONFIG_PM */static struct platform_driver imxmci_driver = {	.probe		= imxmci_probe,	.remove		= imxmci_remove,	.suspend	= imxmci_suspend,	.resume		= imxmci_resume,	.driver		= {		.name		= DRIVER_NAME,	}};static int __init imxmci_init(void){	return platform_driver_register(&imxmci_driver);}static void __exit imxmci_exit(void){	platform_driver_unregister(&imxmci_driver);}module_init(imxmci_init);module_exit(imxmci_exit);MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");MODULE_AUTHOR("Sascha Hauer, Pengutronix");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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