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

📄 wbsd.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 3 页
字号:
	 * 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 "%s: Incomplete DMA transfer. "				"%d bytes left.\n",				mmc_hostname(host->mmc), 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;	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.	 * It wreaks havoc with the card detection though so	 * that needs to be disabled.	 */	setup = wbsd_read_index(host, WBSD_IDX_SETUP);	if (ios->chip_select == MMC_CS_HIGH) {		BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);		setup |= WBSD_DAT3_H;		host->flags |= WBSD_FIGNORE_DETECT;	} else {		if (setup & WBSD_DAT3_H) {			setup &= ~WBSD_DAT3_H;			/*			 * We cannot resume card detection immediatly			 * because of capacitance and delays in the chip.			 */			mod_timer(&host->ignore_timer, jiffies + HZ / 100);		}	}	wbsd_write_index(host, WBSD_IDX_SETUP, setup);	/*	 * Store bus width for later. Will be used when	 * setting up the data transfer.	 */	host->bus_width = ios->bus_width;	spin_unlock_bh(&host->lock);}static int wbsd_get_ro(struct mmc_host *mmc){	struct wbsd_host *host = mmc_priv(mmc);	u8 csr;	spin_lock_bh(&host->lock);	csr = inb(host->base + WBSD_CSR);	csr |= WBSD_MSLED;	outb(csr, host->base + WBSD_CSR);	mdelay(1);	csr = inb(host->base + WBSD_CSR);	csr &= ~WBSD_MSLED;	outb(csr, host->base + WBSD_CSR);	spin_unlock_bh(&host->lock);	return csr & WBSD_WRPT;}static const struct mmc_host_ops wbsd_ops = {	.request	= wbsd_request,	.set_ios	= wbsd_set_ios,	.get_ro		= wbsd_get_ro,};/*****************************************************************************\ *                                                                           * * Interrupt handling                                                        * *                                                                           *\*****************************************************************************//* * Helper function to reset detection ignore */static void wbsd_reset_ignore(unsigned long data){	struct wbsd_host *host = (struct wbsd_host *)data;	BUG_ON(host == NULL);	DBG("Resetting card detection ignore\n");	spin_lock_bh(&host->lock);	host->flags &= ~WBSD_FIGNORE_DETECT;	/*	 * Card status might have changed during the	 * blackout.	 */	tasklet_schedule(&host->card_tasklet);	spin_unlock_bh(&host->lock);}/* * 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;	int delay = -1;	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 = 500;		}	} else if (host->flags & WBSD_FCARD_PRESENT) {		DBG("Card removed\n");		host->flags &= ~WBSD_FCARD_PRESENT;		if (host->mrq) {			printk(KERN_ERR "%s: Card removed during transfer!\n",				mmc_hostname(host->mmc));			wbsd_reset(host);			host->mrq->cmd->error = MMC_ERR_FAILED;			tasklet_schedule(&host->finish_tasklet);		}		delay = 0;	}	/*	 * Unlock first since we might get a call back.	 */	spin_unlock(&host->lock);	if (delay != -1)		mmc_detect_change(host->mmc, msecs_to_jiffies(delay));}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 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;	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;	spin_lock_init(&host->lock);	/*	 * Set up timers	 */	init_timer(&host->ignore_timer);	host->ignore_timer.data = (unsigned long)host;	host->ignore_timer.function = wbsd_reset_ignore;	/*	 * 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;	/*	 * Maximum number of sectors in one transfer. Also limited by 64kB	 * buffer.	 */	mmc->max_sectors = 128;	/*	 * Maximum segment size. Could be one segment with the maximum number	 * of segments.	 */	mmc->max_seg_size = mmc->max_sectors * 512;	dev_set_drvdata(dev, mmc);	return 0;}static void __devexit wbsd_free_mmc(struct device *dev){	struct mmc_host *mmc;	struct wbsd_host *host;	mmc = dev_get_drvdata(dev);	if (!mmc)		return;	host = mmc_priv(mmc);	BUG_ON(host == NULL);	del_timer_sync(&host->ignore_timer);	mmc_free_host(mmc);	dev_set_drvdata(dev, NULL);}/* * Scan for known chip id:s */static int __devinit wbsd_scan(struct wbsd_host *host){	int i, j, k;	int id;	/*	 * Iterate through all ports, all codes to	 * find hardware that is in our known list.	 */	for (i = 0; i < ARRAY_SIZE(config_ports); i++) {		if (!request_region(config_ports[i], 2, DRIVER_NAME))			continue;		for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {			id = 0xFFFF;			host->config = config_ports[i];			host->unlock_code = unlock_codes[j];			wbsd_unlock_config(host);			outb(WBSD_CONF_ID_HI, config_ports[i]);			id = inb(config_ports[i] + 1) << 8;			outb(WBSD_CONF_ID_LO, config_ports[i]);			id |= inb(config_ports[i] + 1);			wbsd_lock_config(host);			for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {				if (id == valid_ids[k]) {					host->chip_id = id;					return 0;				}			}			if (id != 0xFFFF) {				DBG("Unknown hardware (id %x) found at %x\n",					id, config_ports[i]);			}		}		release_region(config_ports[i], 2);	}	host->config = 0;	host->unlock_code = 0;

⌨️ 快捷键说明

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