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

📄 wbsd.c

📁 嵌入式Linux下SD卡的驱动 驱动主要以三星2410板设计的
💻 C
📖 第 1 页 / 共 3 页
字号:
			}		}	}		wbsd_kunmap_sg(host);		/*	 * The controller stops sending interrupts for	 * 'FIFO empty' under certain conditions. So we	 * need to be a bit more pro-active.	 */	tasklet_schedule(&host->fifo_tasklet);}static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data){	u16 blksize;	u8 setup;	unsigned long dmaflags;	DBGF("blksz %04x blks %04x flags %08x\n",		1 << data->blksz_bits, data->blocks, data->flags);	DBGF("tsac %d ms nsac %d clk\n",		data->timeout_ns / 1000000, data->timeout_clks);		/*	 * Calculate size.	 */	host->size = data->blocks << data->blksz_bits;	/*	 * Check timeout values for overflow.	 * (Yes, some cards cause this value to overflow).	 */	if (data->timeout_ns > 127000000)		wbsd_write_index(host, WBSD_IDX_TAAC, 127);	else		wbsd_write_index(host, WBSD_IDX_TAAC, data->timeout_ns/1000000);		if (data->timeout_clks > 255)		wbsd_write_index(host, WBSD_IDX_NSAC, 255);	else		wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);		/*	 * Inform the chip of how large blocks will be	 * sent. It needs this to determine when to	 * calculate CRC.	 *	 * Space for CRC must be included in the size.	 */	blksize = (1 << data->blksz_bits) + 2;		wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);	wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);	/*	 * Clear the FIFO. This is needed even for DMA	 * transfers since the chip still uses the FIFO	 * internally.	 */	setup = wbsd_read_index(host, WBSD_IDX_SETUP);	setup |= WBSD_FIFO_RESET;	wbsd_write_index(host, WBSD_IDX_SETUP, setup);		/*	 * DMA transfer?	 */	if (host->dma >= 0)	{			/*		 * The buffer for DMA is only 64 kB.		 */		BUG_ON(host->size > 0x10000);		if (host->size > 0x10000)		{			data->error = MMC_ERR_INVALID;			return;		}				/*		 * Transfer data from the SG list to		 * the DMA buffer.		 */		if (data->flags & MMC_DATA_WRITE)			wbsd_sg_to_dma(host, data);				/*		 * Initialise the ISA DMA controller.		 */			dmaflags = claim_dma_lock();		disable_dma(host->dma);		clear_dma_ff(host->dma);		if (data->flags & MMC_DATA_READ)			set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);		else			set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);		set_dma_addr(host->dma, host->dma_addr);		set_dma_count(host->dma, host->size);		enable_dma(host->dma);		release_dma_lock(dmaflags);		/*		 * Enable DMA on the host.		 */		wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);	}	else	{		/*		 * This flag is used to keep printk		 * output to a minimum.		 */		host->firsterr = 1;				/*		 * Initialise the SG list.		 */		wbsd_init_sg(host, data);			/*		 * Turn off DMA.		 */		wbsd_write_index(host, WBSD_IDX_DMA, 0);			/*		 * Set up FIFO threshold levels (and fill		 * buffer if doing a write).		 */		if (data->flags & MMC_DATA_READ)		{			wbsd_write_index(host, WBSD_IDX_FIFOEN,				WBSD_FIFOEN_FULL | 8);		}		else		{			wbsd_write_index(host, WBSD_IDX_FIFOEN,				WBSD_FIFOEN_EMPTY | 8);			wbsd_fill_fifo(host);		}	}				data->error = MMC_ERR_NONE;}static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data){	unsigned long dmaflags;	int count;	u8 status;		WARN_ON(host->mrq == NULL);	/*	 * Send a stop command if needed.	 */	if (data->stop)		wbsd_send_command(host, data->stop);	/*	 * Wait for the controller to leave data	 * transfer state.	 */	do	{		status = wbsd_read_index(host, WBSD_IDX_STATUS);	} while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));		/*	 * DMA transfer?	 */	if (host->dma >= 0)	{		/*		 * Disable DMA on the host.		 */		wbsd_write_index(host, WBSD_IDX_DMA, 0);				/*		 * Turn of ISA DMA controller.		 */		dmaflags = claim_dma_lock();		disable_dma(host->dma);		clear_dma_ff(host->dma);		count = get_dma_residue(host->dma);		release_dma_lock(dmaflags);				/*		 * Any leftover data?		 */		if (count)		{			printk(KERN_ERR DRIVER_NAME ": Incomplete DMA "				"transfer. %d bytes left.\n", count);						data->error = MMC_ERR_FAILED;		}		else		{			/*			 * Transfer data from DMA buffer to			 * SG list.			 */			if (data->flags & MMC_DATA_READ)				wbsd_dma_to_sg(host, data);						data->bytes_xfered = host->size;		}	}		DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);		wbsd_request_end(host, host->mrq);}/*****************************************************************************\ *                                                                           * * MMC layer callbacks                                                       * *                                                                           *\*****************************************************************************/static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq){	struct wbsd_host* host = mmc_priv(mmc);	struct mmc_command* cmd;	/*	 * Disable tasklets to avoid a deadlock.	 */	spin_lock_bh(&host->lock);	BUG_ON(host->mrq != NULL);	cmd = mrq->cmd;	host->mrq = mrq;		/*	 * If there is no card in the slot then	 * timeout immediatly.	 */	if (!(host->flags & WBSD_FCARD_PRESENT))	{		cmd->error = MMC_ERR_TIMEOUT;		goto done;	}	/*	 * Does the request include data?	 */	if (cmd->data)	{		wbsd_prepare_data(host, cmd->data);				if (cmd->data->error != MMC_ERR_NONE)			goto done;	}		wbsd_send_command(host, cmd);	/*	 * If this is a data transfer the request	 * will be finished after the data has	 * transfered.	 */		if (cmd->data && (cmd->error == MMC_ERR_NONE))	{		/*		 * Dirty fix for hardware bug.		 */		if (host->dma == -1)			tasklet_schedule(&host->fifo_tasklet);		spin_unlock_bh(&host->lock);		return;	}		done:	wbsd_request_end(host, mrq);	spin_unlock_bh(&host->lock);}static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios){	struct wbsd_host* host = mmc_priv(mmc);	u8 clk, setup, pwr;		DBGF("clock %uHz busmode %u powermode %u Vdd %u\n",		ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);	spin_lock_bh(&host->lock);	/*	 * Reset the chip on each power off.	 * Should clear out any weird states.	 */	if (ios->power_mode == MMC_POWER_OFF)		wbsd_init_device(host);		if (ios->clock >= 24000000)		clk = WBSD_CLK_24M;	else if (ios->clock >= 16000000)		clk = WBSD_CLK_16M;	else if (ios->clock >= 12000000)		clk = WBSD_CLK_12M;	else		clk = WBSD_CLK_375K;	/*	 * Only write to the clock register when	 * there is an actual change.	 */	if (clk != host->clk)	{		wbsd_write_index(host, WBSD_IDX_CLK, clk);		host->clk = clk;	}	/*	 * Power up card.	 */	if (ios->power_mode != MMC_POWER_OFF)	{		pwr = inb(host->base + WBSD_CSR);		pwr &= ~WBSD_POWER_N;		outb(pwr, host->base + WBSD_CSR);	}	/*	 * MMC cards need to have pin 1 high during init.	 * Init time corresponds rather nicely with the bus mode.	 * It wreaks havoc with the card detection though so	 * that needs to be disabed.	 */	setup = wbsd_read_index(host, WBSD_IDX_SETUP);	if ((ios->power_mode == MMC_POWER_ON) &&		(ios->bus_mode == MMC_BUSMODE_OPENDRAIN))	{		setup |= WBSD_DAT3_H;		host->flags |= WBSD_FIGNORE_DETECT;	}	else	{		setup &= ~WBSD_DAT3_H;		host->flags &= ~WBSD_FIGNORE_DETECT;	}	wbsd_write_index(host, WBSD_IDX_SETUP, setup);		spin_unlock_bh(&host->lock);}static struct mmc_host_ops wbsd_ops = {	.request	= wbsd_request,	.set_ios	= wbsd_set_ios,};/*****************************************************************************\ *                                                                           * * Interrupt handling                                                        * *                                                                           *\*****************************************************************************//* * Helper function for card detection */static void wbsd_detect_card(unsigned long data){	struct wbsd_host *host = (struct wbsd_host*)data;		BUG_ON(host == NULL);		DBG("Executing card detection\n");		mmc_detect_change(host->mmc);	}/* * Tasklets */static inline struct mmc_data* wbsd_get_data(struct wbsd_host* host){	WARN_ON(!host->mrq);	if (!host->mrq)		return NULL;	WARN_ON(!host->mrq->cmd);	if (!host->mrq->cmd)		return NULL;	WARN_ON(!host->mrq->cmd->data);	if (!host->mrq->cmd->data)		return NULL;		return host->mrq->cmd->data;}static void wbsd_tasklet_card(unsigned long param){	struct wbsd_host* host = (struct wbsd_host*)param;	u8 csr;		spin_lock(&host->lock);		if (host->flags & WBSD_FIGNORE_DETECT)	{		spin_unlock(&host->lock);		return;	}		csr = inb(host->base + WBSD_CSR);	WARN_ON(csr == 0xff);		if (csr & WBSD_CARDPRESENT)	{		if (!(host->flags & WBSD_FCARD_PRESENT))		{			DBG("Card inserted\n");			host->flags |= WBSD_FCARD_PRESENT;						/*			 * Delay card detection to allow electrical connections			 * to stabilise.			 */			mod_timer(&host->timer, jiffies + HZ/2);		}				spin_unlock(&host->lock);	}	else if (host->flags & WBSD_FCARD_PRESENT)	{		DBG("Card removed\n");		host->flags &= ~WBSD_FCARD_PRESENT;				if (host->mrq)		{			printk(KERN_ERR DRIVER_NAME				": Card removed during transfer!\n");			wbsd_reset(host);						host->mrq->cmd->error = MMC_ERR_FAILED;			tasklet_schedule(&host->finish_tasklet);		}				/*		 * Unlock first since we might get a call back.		 */		spin_unlock(&host->lock);		mmc_detect_change(host->mmc);	}}static void wbsd_tasklet_fifo(unsigned long param){	struct wbsd_host* host = (struct wbsd_host*)param;	struct mmc_data* data;		spin_lock(&host->lock);			if (!host->mrq)		goto end;		data = wbsd_get_data(host);	if (!data)		goto end;	if (data->flags & MMC_DATA_WRITE)		wbsd_fill_fifo(host);	else		wbsd_empty_fifo(host);	/*	 * Done?	 */	if (host->size == data->bytes_xfered)	{		wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);		tasklet_schedule(&host->finish_tasklet);	}end:		spin_unlock(&host->lock);}static void wbsd_tasklet_crc(unsigned long param){	struct wbsd_host* host = (struct wbsd_host*)param;	struct mmc_data* data;		spin_lock(&host->lock);		if (!host->mrq)		goto end;		data = wbsd_get_data(host);	if (!data)		goto end;		DBGF("CRC error\n");	data->error = MMC_ERR_BADCRC;		tasklet_schedule(&host->finish_tasklet);end:			spin_unlock(&host->lock);}static void wbsd_tasklet_timeout(unsigned long param){	struct wbsd_host* host = (struct wbsd_host*)param;	struct mmc_data* data;		spin_lock(&host->lock);		if (!host->mrq)		goto end;		data = wbsd_get_data(host);	if (!data)		goto end;		DBGF("Timeout\n");	data->error = MMC_ERR_TIMEOUT;		tasklet_schedule(&host->finish_tasklet);end:		spin_unlock(&host->lock);}static void wbsd_tasklet_finish(unsigned long param){	struct wbsd_host* host = (struct wbsd_host*)param;	struct mmc_data* data;		spin_lock(&host->lock);		WARN_ON(!host->mrq);	if (!host->mrq)		goto end;		data = wbsd_get_data(host);	if (!data)		goto end;	wbsd_finish_data(host, data);	end:		spin_unlock(&host->lock);}static void wbsd_tasklet_block(unsigned long param){	struct wbsd_host* host = (struct wbsd_host*)param;	struct mmc_data* data;		spin_lock(&host->lock);	if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=		WBSD_CRC_OK)	{		data = wbsd_get_data(host);		if (!data)			goto end;				DBGF("CRC error\n");		data->error = MMC_ERR_BADCRC;			tasklet_schedule(&host->finish_tasklet);	}end:		spin_unlock(&host->lock);}/* * Interrupt handling */static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs){	struct wbsd_host* host = dev_id;	int isr;		isr = inb(host->base + WBSD_ISR);	/*	 * Was it actually our hardware that caused the interrupt?	 */	if (isr == 0xff || isr == 0x00)		return IRQ_NONE;		host->isr |= isr;	/*	 * Schedule tasklets as needed.	 */	if (isr & WBSD_INT_CARD)		tasklet_schedule(&host->card_tasklet);	if (isr & WBSD_INT_FIFO_THRE)		tasklet_schedule(&host->fifo_tasklet);	if (isr & WBSD_INT_CRC)		tasklet_hi_schedule(&host->crc_tasklet);	if (isr & WBSD_INT_TIMEOUT)		tasklet_hi_schedule(&host->timeout_tasklet);	if (isr & WBSD_INT_BUSYEND)		tasklet_hi_schedule(&host->block_tasklet);	if (isr & WBSD_INT_TC)		tasklet_schedule(&host->finish_tasklet);		return IRQ_HANDLED;}/*****************************************************************************\ *                                                                           * * Device initialisation and shutdown                                        * *                                                                           *\*****************************************************************************//* * Allocate/free MMC structure. */static int __devinit wbsd_alloc_mmc(struct device* dev){	struct mmc_host* mmc;	struct wbsd_host* host;		/*	 * Allocate MMC structure.	 */	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);	if (!mmc)		return -ENOMEM;		host = mmc_priv(mmc);	host->mmc = mmc;	host->dma = -1;	/*	 * Set host parameters.	 */	mmc->ops = &wbsd_ops;	mmc->f_min = 375000;	mmc->f_max = 24000000;	mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;		spin_lock_init(&host->lock);		/*	 * Set up detection timer	 */	init_timer(&host->timer);	host->timer.data = (unsigned long)host;	host->timer.function = wbsd_detect_card;		/*	 * Maximum number of segments. Worst case is one sector per segment	 * so this will be 64kB/512.	 */	mmc->max_hw_segs = 128;	mmc->max_phys_segs = 128;		/*

⌨️ 快捷键说明

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