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

📄 wbsd.c

📁 The latest MontaVista Linux driver about SD/MMC code
💻 C
📖 第 1 页 / 共 2 页
字号:
	DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);		wbsd_request_end(host, host->mrq);}/* * MMC 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 (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT))	{		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))	{				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;	}	if (ios->power_mode != MMC_POWER_OFF)	{		/*		 * Power up card.		 */		pwr = inb(host->base + WBSD_CSR);		pwr &= ~WBSD_POWER_N;		outb(pwr, host->base + WBSD_CSR);		/*		 * This behaviour is stolen from the		 * Windows driver. Don't know why, but		 * it is needed.		 */		setup = wbsd_read_index(host, WBSD_IDX_SETUP);		if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)			setup |= WBSD_DAT3_H;		else			setup &= ~WBSD_DAT3_H;		wbsd_write_index(host, WBSD_IDX_SETUP, setup);		mdelay(1);	}	spin_unlock_bh(&host->lock);}/* * Tasklets */inline static 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);		csr = inb(host->base + WBSD_CSR);	WARN_ON(csr == 0xff);		if (csr & WBSD_CARDPRESENT)		DBG("Card inserted\n");	else	{		DBG("Card removed\n");				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, 0);}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);		WARN_ON(!host->mrq);	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);		WARN_ON(!host->mrq);	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;	/*	 * Schedule tasklets as needed.	 */	if (isr & WBSD_INT_CARD)		tasklet_schedule(&host->card_tasklet);	if (isr & WBSD_INT_FIFO_THRE)		tasklet_hi_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;}/* * Support functions for probe */static int 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 < sizeof(config_ports)/sizeof(int);i++)	{		if (!request_region(config_ports[i], 2, DRIVER_NAME))			continue;					for (j = 0;j < sizeof(unlock_codes)/sizeof(int);j++)		{			id = 0xFFFF;						outb(unlock_codes[j], config_ports[i]);			outb(unlock_codes[j], config_ports[i]);						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);						for (k = 0;k < sizeof(valid_ids)/sizeof(int);k++)			{				if (id == valid_ids[k])				{									host->chip_id = id;					host->config = config_ports[i];					host->unlock_code = unlock_codes[i];									return 0;				}			}						if (id != 0xFFFF)			{				DBG("Unknown hardware (id %x) found at %x\n",					id, config_ports[i]);			}			outb(LOCK_CODE, config_ports[i]);		}				release_region(config_ports[i], 2);	}		return -ENODEV;}static int wbsd_request_regions(struct wbsd_host* host){	if (io & 0x7)		return -EINVAL;		if (!request_region(io, 8, DRIVER_NAME))		return -EIO;		host->base = io;			return 0;}static void wbsd_release_regions(struct wbsd_host* host){	if (host->base)		release_region(host->base, 8);	if (host->config)		release_region(host->config, 2);}static void wbsd_init_dma(struct wbsd_host* host){	host->dma = -1;		if (dma < 0)		return;		if (request_dma(dma, DRIVER_NAME))		goto err;		/*	 * We need to allocate a special buffer in	 * order for ISA to be able to DMA to it.	 */	host->dma_buffer = kmalloc(65536,		GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);	if (!host->dma_buffer)		goto free;	/*	 * Translate the address to a physical address.	 */	host->dma_addr = isa_virt_to_bus(host->dma_buffer);				/*	 * ISA DMA must be aligned on a 64k basis.	 */	if ((host->dma_addr & 0xffff) != 0)		goto kfree;	/*	 * ISA cannot access memory above 16 MB.	 */	else if (host->dma_addr >= 0x1000000)		goto kfree;	host->dma = dma;		return;	kfree:	/*	 * If we've gotten here then there is some kind of alignment bug	 */	BUG_ON(1);		kfree(host->dma_buffer);	host->dma_buffer = NULL;free:	free_dma(dma);err:	printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "		"Falling back on FIFO.\n", dma);}static struct mmc_host_ops wbsd_ops = {	.request	= wbsd_request,	.set_ios	= wbsd_set_ios,};/* * Device probe */static int wbsd_probe(struct device* dev){	struct wbsd_host* host = NULL;	struct mmc_host* mmc = NULL;	int ret;		/*	 * Allocate MMC structure.	 */	mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);	if (!mmc)		return -ENOMEM;		host = mmc_priv(mmc);	host->mmc = mmc;		/*	 * Scan for hardware.	 */	ret = wbsd_scan(host);	if (ret)		goto freemmc;	/*	 * Reset the chip.	 */		wbsd_write_config(host, WBSD_CONF_SWRST, 1);	wbsd_write_config(host, WBSD_CONF_SWRST, 0);	/*	 * Allocate I/O ports.	 */	ret = wbsd_request_regions(host);	if (ret)		goto release;	/*	 * 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);	/*	 * Select SD/MMC function.	 */	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);		/*	 * Set up card detection.	 */	wbsd_write_config(host, WBSD_CONF_PINS, 0x02);		/*	 * Configure I/O port.	 */	wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);	wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);	/*	 * Allocate interrupt.	 */	ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);	if (ret)		goto release;		host->irq = irq;		/*	 * Set up tasklets.	 */	tasklet_init(&host->card_tasklet, wbsd_tasklet_card, (unsigned long)host);	tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, (unsigned long)host);	tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, (unsigned long)host);	tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, (unsigned long)host);	tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host);	tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);		/*	 * Configure interrupt.	 */	wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);		/*	 * Allocate DMA.	 */	wbsd_init_dma(host);		/*	 * If all went well, then configure DMA.	 */	if (host->dma >= 0)		wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);		/*	 * Maximum number of segments. Worst case is one sector per segment	 * so this will be 64kB/512.	 */	mmc->max_hw_segs = NR_SG;	mmc->max_phys_segs = NR_SG;		/*	 * 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;		/*	 * Enable chip.	 */	wbsd_write_config(host, WBSD_CONF_ENABLE, 1);		/*	 * Power up chip.	 */	wbsd_write_config(host, WBSD_CONF_POWER, 0x20);		/*	 * Power Management stuff. No idea how this works.	 * Not tested.	 */#ifdef CONFIG_PM	wbsd_write_config(host, WBSD_CONF_PME, 0xA0);#endif	/*	 * Reset the chip into a known state.	 */	wbsd_init_device(host);		dev_set_drvdata(dev, mmc);		/*	 * Add host to MMC layer.	 */	mmc_add_host(mmc);	printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n",		mmc_hostname(mmc), (int)host->chip_id, (int)host->base,		(int)host->irq, (int)host->dma);	return 0;release:	wbsd_release_regions(host);freemmc:	mmc_free_host(mmc);	return ret;}/* * Device remove */static int wbsd_remove(struct device* dev){	struct mmc_host* mmc = dev_get_drvdata(dev);	struct wbsd_host* host;		if (!mmc)		return 0;	host = mmc_priv(mmc);		/*	 * Unregister host with MMC layer.	 */	mmc_remove_host(mmc);	/*	 * Power down the SD/MMC function.	 */	wbsd_unlock_config(host);	wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);	wbsd_write_config(host, WBSD_CONF_ENABLE, 0);	wbsd_lock_config(host);		/*	 * Free resources.	 */	if (host->dma_buffer)		kfree(host->dma_buffer);		if (host->dma >= 0)		free_dma(host->dma);	free_irq(host->irq, host);		tasklet_kill(&host->card_tasklet);	tasklet_kill(&host->fifo_tasklet);	tasklet_kill(&host->crc_tasklet);	tasklet_kill(&host->timeout_tasklet);	tasklet_kill(&host->finish_tasklet);	tasklet_kill(&host->block_tasklet);		wbsd_release_regions(host);		mmc_free_host(mmc);	return 0;}/* * Power management */#ifdef CONFIG_PMstatic int wbsd_suspend(struct device *dev, u32 state, u32 level){	DBGF("Not yet supported\n");	return 0;}static int wbsd_resume(struct device *dev, u32 level){	DBGF("Not yet supported\n");	return 0;}#else#define wbsd_suspend NULL#define wbsd_resume NULL#endifstatic void wbsd_release(struct device *dev){}static struct platform_device wbsd_device = {	.name		= DRIVER_NAME,	.id			= -1,	.dev		= {		.release = wbsd_release,	},};static struct device_driver wbsd_driver = {	.name		= DRIVER_NAME,	.bus		= &platform_bus_type,	.probe		= wbsd_probe,	.remove		= wbsd_remove,		.suspend	= wbsd_suspend,	.resume		= wbsd_resume,};/* * Module loading/unloading */static int __init wbsd_drv_init(void){	int result;		printk(KERN_INFO DRIVER_NAME		": Winbond W83L51xD SD/MMC card interface driver, "		DRIVER_VERSION "\n");	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");		result = driver_register(&wbsd_driver);	if (result < 0)		return result;	result = platform_device_register(&wbsd_device);	if (result < 0)		return result;	return 0;}static void __exit wbsd_drv_exit(void){	platform_device_unregister(&wbsd_device);		driver_unregister(&wbsd_driver);	DBG("unloaded\n");}module_init(wbsd_drv_init);module_exit(wbsd_drv_exit);module_param(io, uint, 0444);module_param(irq, uint, 0444);module_param(dma, int, 0444);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");

⌨️ 快捷键说明

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