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

📄 sdhci.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 3 页
字号:
	}	intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);	if (intmask & SDHCI_INT_BUS_POWER) {		printk(KERN_ERR "%s: Card is consuming too much power!\n",			mmc_hostname(host->mmc));		writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);	}	intmask &= SDHCI_INT_BUS_POWER;	if (intmask) {		printk(KERN_ERR "%s: Unexpected interrupt 0x%08x. Please "			"report this to " BUGMAIL ".\n",			mmc_hostname(host->mmc), intmask);		sdhci_dumpregs(host);		writel(intmask, host->ioaddr + SDHCI_INT_STATUS);	}	result = IRQ_HANDLED;	mmiowb();out:	spin_unlock(&host->lock);	return result;}/*****************************************************************************\ *                                                                           * * Suspend/resume                                                            * *                                                                           *\*****************************************************************************/#ifdef CONFIG_PMstatic int sdhci_suspend (struct pci_dev *pdev, pm_message_t state){	struct sdhci_chip *chip;	int i, ret;	chip = pci_get_drvdata(pdev);	if (!chip)		return 0;	DBG("Suspending...\n");	for (i = 0;i < chip->num_slots;i++) {		if (!chip->hosts[i])			continue;		ret = mmc_suspend_host(chip->hosts[i]->mmc, state);		if (ret) {			for (i--;i >= 0;i--)				mmc_resume_host(chip->hosts[i]->mmc);			return ret;		}	}	pci_save_state(pdev);	pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);	pci_disable_device(pdev);	pci_set_power_state(pdev, pci_choose_state(pdev, state));	return 0;}static int sdhci_resume (struct pci_dev *pdev){	struct sdhci_chip *chip;	int i, ret;	chip = pci_get_drvdata(pdev);	if (!chip)		return 0;	DBG("Resuming...\n");	pci_set_power_state(pdev, PCI_D0);	pci_restore_state(pdev);	pci_enable_device(pdev);	for (i = 0;i < chip->num_slots;i++) {		if (!chip->hosts[i])			continue;		if (chip->hosts[i]->flags & SDHCI_USE_DMA)			pci_set_master(pdev);		sdhci_init(chip->hosts[i]);		mmiowb();		ret = mmc_resume_host(chip->hosts[i]->mmc);		if (ret)			return ret;	}	return 0;}#else /* CONFIG_PM */#define sdhci_suspend NULL#define sdhci_resume NULL#endif /* CONFIG_PM *//*****************************************************************************\ *                                                                           * * Device probing/removal                                                    * *                                                                           *\*****************************************************************************/static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot){	int ret;	unsigned int version;	struct sdhci_chip *chip;	struct mmc_host *mmc;	struct sdhci_host *host;	u8 first_bar;	unsigned int caps;	chip = pci_get_drvdata(pdev);	BUG_ON(!chip);	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);	if (ret)		return ret;	first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;	if (first_bar > 5) {		printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n");		return -ENODEV;	}	if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) {		printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n");		return -ENODEV;	}	if (pci_resource_len(pdev, first_bar + slot) != 0x100) {		printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. "			"You may experience problems.\n");	}	if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {		printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n");		return -ENODEV;	}	if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {		printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n");		return -ENODEV;	}	mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev);	if (!mmc)		return -ENOMEM;	host = mmc_priv(mmc);	host->mmc = mmc;	host->chip = chip;	chip->hosts[slot] = host;	host->bar = first_bar + slot;	host->addr = pci_resource_start(pdev, host->bar);	host->irq = pdev->irq;	DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);	snprintf(host->slot_descr, 20, "sdhci:slot%d", slot);	ret = pci_request_region(pdev, host->bar, host->slot_descr);	if (ret)		goto free;	host->ioaddr = ioremap_nocache(host->addr,		pci_resource_len(pdev, host->bar));	if (!host->ioaddr) {		ret = -ENOMEM;		goto release;	}	sdhci_reset(host, SDHCI_RESET_ALL);	version = readw(host->ioaddr + SDHCI_HOST_VERSION);	version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;	if (version != 0) {		printk(KERN_ERR "%s: Unknown controller version (%d). "			"You may experience problems.\n", host->slot_descr,			version);	}	caps = readl(host->ioaddr + SDHCI_CAPABILITIES);	if (debug_nodma)		DBG("DMA forced off\n");	else if (debug_forcedma) {		DBG("DMA forced on\n");		host->flags |= SDHCI_USE_DMA;	} else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)		host->flags |= SDHCI_USE_DMA;	else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA)		DBG("Controller doesn't have DMA interface\n");	else if (!(caps & SDHCI_CAN_DO_DMA))		DBG("Controller doesn't have DMA capability\n");	else		host->flags |= SDHCI_USE_DMA;	if (host->flags & SDHCI_USE_DMA) {		if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {			printk(KERN_WARNING "%s: No suitable DMA available. "				"Falling back to PIO.\n", host->slot_descr);			host->flags &= ~SDHCI_USE_DMA;		}	}	if (host->flags & SDHCI_USE_DMA)		pci_set_master(pdev);	else /* XXX: Hack to get MMC layer to avoid highmem */		pdev->dma_mask = 0;	host->max_clk =		(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;	if (host->max_clk == 0) {		printk(KERN_ERR "%s: Hardware doesn't specify base clock "			"frequency.\n", host->slot_descr);		ret = -ENODEV;		goto unmap;	}	host->max_clk *= 1000000;	host->timeout_clk =		(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;	if (host->timeout_clk == 0) {		printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "			"frequency.\n", host->slot_descr);		ret = -ENODEV;		goto unmap;	}	if (caps & SDHCI_TIMEOUT_CLK_UNIT)		host->timeout_clk *= 1000;	host->max_block = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;	if (host->max_block >= 3) {		printk(KERN_ERR "%s: Invalid maximum block size.\n",			host->slot_descr);		ret = -ENODEV;		goto unmap;	}	host->max_block = 512 << host->max_block;	/*	 * Set host parameters.	 */	mmc->ops = &sdhci_ops;	mmc->f_min = host->max_clk / 256;	mmc->f_max = host->max_clk;	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;	mmc->ocr_avail = 0;	if (caps & SDHCI_CAN_VDD_330)		mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;	else if (caps & SDHCI_CAN_VDD_300)		mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;	else if (caps & SDHCI_CAN_VDD_180)		mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19;	if ((host->max_clk > 25000000) && !(caps & SDHCI_CAN_DO_HISPD)) {		printk(KERN_ERR "%s: Controller reports > 25 MHz base clock,"			" but no high speed support.\n",			host->slot_descr);		mmc->f_max = 25000000;	}	if (mmc->ocr_avail == 0) {		printk(KERN_ERR "%s: Hardware doesn't report any "			"support voltages.\n", host->slot_descr);		ret = -ENODEV;		goto unmap;	}	spin_lock_init(&host->lock);	/*	 * Maximum number of segments. Hardware cannot do scatter lists.	 */	if (host->flags & SDHCI_USE_DMA)		mmc->max_hw_segs = 1;	else		mmc->max_hw_segs = 16;	mmc->max_phys_segs = 16;	/*	 * Maximum number of sectors in one transfer. Limited by DMA boundary	 * size (512KiB), which means (512 KiB/512=) 1024 entries.	 */	mmc->max_sectors = 1024;	/*	 * Maximum segment size. Could be one segment with the maximum number	 * of sectors.	 */	mmc->max_seg_size = mmc->max_sectors * 512;	/*	 * Init tasklets.	 */	tasklet_init(&host->card_tasklet,		sdhci_tasklet_card, (unsigned long)host);	tasklet_init(&host->finish_tasklet,		sdhci_tasklet_finish, (unsigned long)host);	setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,		host->slot_descr, host);	if (ret)		goto untasklet;	sdhci_init(host);#ifdef CONFIG_MMC_DEBUG	sdhci_dumpregs(host);#endif	mmiowb();	mmc_add_host(mmc);	printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc),		host->addr, host->irq,		(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");	return 0;untasklet:	tasklet_kill(&host->card_tasklet);	tasklet_kill(&host->finish_tasklet);unmap:	iounmap(host->ioaddr);release:	pci_release_region(pdev, host->bar);free:	mmc_free_host(mmc);	return ret;}static void sdhci_remove_slot(struct pci_dev *pdev, int slot){	struct sdhci_chip *chip;	struct mmc_host *mmc;	struct sdhci_host *host;	chip = pci_get_drvdata(pdev);	host = chip->hosts[slot];	mmc = host->mmc;	chip->hosts[slot] = NULL;	mmc_remove_host(mmc);	sdhci_reset(host, SDHCI_RESET_ALL);	free_irq(host->irq, host);	del_timer_sync(&host->timer);	tasklet_kill(&host->card_tasklet);	tasklet_kill(&host->finish_tasklet);	iounmap(host->ioaddr);	pci_release_region(pdev, host->bar);	mmc_free_host(mmc);}static int __devinit sdhci_probe(struct pci_dev *pdev,	const struct pci_device_id *ent){	int ret, i;	u8 slots, rev;	struct sdhci_chip *chip;	BUG_ON(pdev == NULL);	BUG_ON(ent == NULL);	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);	printk(KERN_INFO DRIVER_NAME		": SDHCI controller found at %s [%04x:%04x] (rev %x)\n",		pci_name(pdev), (int)pdev->vendor, (int)pdev->device,		(int)rev);	ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);	if (ret)		return ret;	slots = PCI_SLOT_INFO_SLOTS(slots) + 1;	DBG("found %d slot(s)\n", slots);	if (slots == 0)		return -ENODEV;	ret = pci_enable_device(pdev);	if (ret)		return ret;	chip = kzalloc(sizeof(struct sdhci_chip) +		sizeof(struct sdhci_host*) * slots, GFP_KERNEL);	if (!chip) {		ret = -ENOMEM;		goto err;	}	chip->pdev = pdev;	chip->quirks = ent->driver_data;	if (debug_quirks)		chip->quirks = debug_quirks;	chip->num_slots = slots;	pci_set_drvdata(pdev, chip);	for (i = 0;i < slots;i++) {		ret = sdhci_probe_slot(pdev, i);		if (ret) {			for (i--;i >= 0;i--)				sdhci_remove_slot(pdev, i);			goto free;		}	}	return 0;free:	pci_set_drvdata(pdev, NULL);	kfree(chip);err:	pci_disable_device(pdev);	return ret;}static void __devexit sdhci_remove(struct pci_dev *pdev){	int i;	struct sdhci_chip *chip;	chip = pci_get_drvdata(pdev);	if (chip) {		for (i = 0;i < chip->num_slots;i++)			sdhci_remove_slot(pdev, i);		pci_set_drvdata(pdev, NULL);		kfree(chip);	}	pci_disable_device(pdev);}static struct pci_driver sdhci_driver = {	.name = 	DRIVER_NAME,	.id_table =	pci_ids,	.probe = 	sdhci_probe,	.remove =	__devexit_p(sdhci_remove),	.suspend =	sdhci_suspend,	.resume	=	sdhci_resume,};/*****************************************************************************\ *                                                                           * * Driver init/exit                                                          * *                                                                           *\*****************************************************************************/static int __init sdhci_drv_init(void){	printk(KERN_INFO DRIVER_NAME		": Secure Digital Host Controller Interface driver, "		DRIVER_VERSION "\n");	printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");	return pci_register_driver(&sdhci_driver);}static void __exit sdhci_drv_exit(void){	DBG("Exiting\n");	pci_unregister_driver(&sdhci_driver);}module_init(sdhci_drv_init);module_exit(sdhci_drv_exit);module_param(debug_nodma, uint, 0444);module_param(debug_forcedma, uint, 0444);module_param(debug_quirks, uint, 0444);MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");MODULE_VERSION(DRIVER_VERSION);MODULE_LICENSE("GPL");MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)");MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)");MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");

⌨️ 快捷键说明

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