📄 sdhci.c
字号:
} 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 + -