📄 wbsd.c
字号:
return -ENODEV;}/* * Allocate/free io port ranges */static int __devinit wbsd_request_region(struct wbsd_host *host, int base){ if (base & 0x7) return -EINVAL; if (!request_region(base, 8, DRIVER_NAME)) return -EIO; host->base = base; return 0;}static void __devexit wbsd_release_regions(struct wbsd_host *host){ if (host->base) release_region(host->base, 8); host->base = 0; if (host->config) release_region(host->config, 2); host->config = 0;}/* * Allocate/free DMA port and buffer */static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma){ 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(WBSD_DMA_SIZE, GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); if (!host->dma_buffer) goto free; /* * Translate the address to a physical address. */ host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); /* * 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); dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); host->dma_addr = (dma_addr_t)NULL; 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 void __devexit wbsd_release_dma(struct wbsd_host *host){ if (host->dma_addr) { dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); } kfree(host->dma_buffer); if (host->dma >= 0) free_dma(host->dma); host->dma = -1; host->dma_buffer = NULL; host->dma_addr = (dma_addr_t)NULL;}/* * Allocate/free IRQ. */static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq){ int ret; /* * Allocate interrupt. */ ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); if (ret) return ret; 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); return 0;}static void __devexit wbsd_release_irq(struct wbsd_host *host){ if (!host->irq) return; free_irq(host->irq, host); host->irq = 0; 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);}/* * Allocate all resources for the host. */static int __devinit wbsd_request_resources(struct wbsd_host *host, int base, int irq, int dma){ int ret; /* * Allocate I/O ports. */ ret = wbsd_request_region(host, base); if (ret) return ret; /* * Allocate interrupt. */ ret = wbsd_request_irq(host, irq); if (ret) return ret; /* * Allocate DMA. */ wbsd_request_dma(host, dma); return 0;}/* * Release all resources for the host. */static void __devexit wbsd_release_resources(struct wbsd_host *host){ wbsd_release_dma(host); wbsd_release_irq(host); wbsd_release_regions(host);}/* * Configure the resources the chip should use. */static void wbsd_chip_config(struct wbsd_host *host){ wbsd_unlock_config(host); /* * Reset the chip. */ wbsd_write_config(host, WBSD_CONF_SWRST, 1); wbsd_write_config(host, WBSD_CONF_SWRST, 0); /* * Select SD/MMC function. */ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); /* * Set up card detection. */ wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); /* * Configure chip */ wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); if (host->dma >= 0) wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); /* * Enable and power up chip. */ wbsd_write_config(host, WBSD_CONF_ENABLE, 1); wbsd_write_config(host, WBSD_CONF_POWER, 0x20); wbsd_lock_config(host);}/* * Check that configured resources are correct. */static int wbsd_chip_validate(struct wbsd_host *host){ int base, irq, dma; wbsd_unlock_config(host); /* * Select SD/MMC function. */ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); /* * Read configuration. */ base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); irq = wbsd_read_config(host, WBSD_CONF_IRQ); dma = wbsd_read_config(host, WBSD_CONF_DRQ); wbsd_lock_config(host); /* * Validate against given configuration. */ if (base != host->base) return 0; if (irq != host->irq) return 0; if ((dma != host->dma) && (host->dma != -1)) return 0; return 1;}/* * Powers down the SD function */static void wbsd_chip_poweroff(struct wbsd_host *host){ 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);}/*****************************************************************************\ * * * Devices setup and shutdown * * *\*****************************************************************************/static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, int pnp){ struct wbsd_host *host = NULL; struct mmc_host *mmc = NULL; int ret; ret = wbsd_alloc_mmc(dev); if (ret) return ret; mmc = dev_get_drvdata(dev); host = mmc_priv(mmc); /* * Scan for hardware. */ ret = wbsd_scan(host); if (ret) { if (pnp && (ret == -ENODEV)) { printk(KERN_WARNING DRIVER_NAME ": Unable to confirm device presence. You may " "experience lock-ups.\n"); } else { wbsd_free_mmc(dev); return ret; } } /* * Request resources. */ ret = wbsd_request_resources(host, base, irq, dma); if (ret) { wbsd_release_resources(host); wbsd_free_mmc(dev); return ret; } /* * See if chip needs to be configured. */ if (pnp) { if ((host->config != 0) && !wbsd_chip_validate(host)) { printk(KERN_WARNING DRIVER_NAME ": PnP active but chip not configured! " "You probably have a buggy BIOS. " "Configuring chip manually.\n"); wbsd_chip_config(host); } } else wbsd_chip_config(host); /* * Power Management stuff. No idea how this works. * Not tested. */#ifdef CONFIG_PM if (host->config) { wbsd_unlock_config(host); wbsd_write_config(host, WBSD_CONF_PME, 0xA0); wbsd_lock_config(host); }#endif /* * Allow device to initialise itself properly. */ mdelay(5); /* * Reset the chip into a known state. */ wbsd_init_device(host); mmc_add_host(mmc); printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); if (host->chip_id != 0) printk(" id %x", (int)host->chip_id); printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); if (host->dma >= 0) printk(" dma %d", (int)host->dma); else printk(" FIFO"); if (pnp) printk(" PnP"); printk("\n"); return 0;}static void __devexit wbsd_shutdown(struct device *dev, int pnp){ struct mmc_host *mmc = dev_get_drvdata(dev); struct wbsd_host *host; if (!mmc) return; host = mmc_priv(mmc); mmc_remove_host(mmc); /* * Power down the SD/MMC function. */ if (!pnp) wbsd_chip_poweroff(host); wbsd_release_resources(host); wbsd_free_mmc(dev);}/* * Non-PnP */static int __devinit wbsd_probe(struct platform_device *dev){ /* Use the module parameters for resources */ return wbsd_init(&dev->dev, io, irq, dma, 0);}static int __devexit wbsd_remove(struct platform_device *dev){ wbsd_shutdown(&dev->dev, 0); return 0;}/* * PnP */#ifdef CONFIG_PNPstatic int __devinitwbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id){ int io, irq, dma; /* * Get resources from PnP layer. */ io = pnp_port_start(pnpdev, 0); irq = pnp_irq(pnpdev, 0); if (pnp_dma_valid(pnpdev, 0)) dma = pnp_dma(pnpdev, 0); else dma = -1; DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); return wbsd_init(&pnpdev->dev, io, irq, dma, 1);}static void __devexit wbsd_pnp_remove(struct pnp_dev *dev){ wbsd_shutdown(&dev->dev, 1);}#endif /* CONFIG_PNP *//* * Power management */#ifdef CONFIG_PMstatic int wbsd_suspend(struct wbsd_host *host, pm_message_t state){ BUG_ON(host == NULL); return mmc_suspend_host(host->mmc, state);}static int wbsd_resume(struct wbsd_host *host){ BUG_ON(host == NULL); wbsd_init_device(host); return mmc_resume_host(host->mmc);}static int wbsd_platform_suspend(struct platform_device *dev, pm_message_t state){ struct mmc_host *mmc = platform_get_drvdata(dev); struct wbsd_host *host; int ret; if (mmc == NULL) return 0; DBGF("Suspending...\n"); host = mmc_priv(mmc); ret = wbsd_suspend(host, state); if (ret) return ret; wbsd_chip_poweroff(host); return 0;}static int wbsd_platform_resume(struct platform_device *dev){ struct mmc_host *mmc = platform_get_drvdata(dev); struct wbsd_host *host; if (mmc == NULL) return 0; DBGF("Resuming...\n"); host = mmc_priv(mmc); wbsd_chip_config(host); /* * Allow device to initialise itself properly. */ mdelay(5); return wbsd_resume(host);}#ifdef CONFIG_PNPstatic int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state){ struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); struct wbsd_host *host; if (mmc == NULL) return 0; DBGF("Suspending...\n"); host = mmc_priv(mmc); return wbsd_suspend(host, state);}static int wbsd_pnp_resume(struct pnp_dev *pnp_dev){ struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); struct wbsd_host *host; if (mmc == NULL) return 0; DBGF("Resuming...\n"); host = mmc_priv(mmc); /* * See if chip needs to be configured. */ if (host->config != 0) { if (!wbsd_chip_validate(host)) { printk(KERN_WARNING DRIVER_NAME ": PnP active but chip not configured! " "You probably have a buggy BIOS. " "Configuring chip manually.\n"); wbsd_chip_config(host); } } /* * Allow device to initialise itself properly. */ mdelay(5); return wbsd_resume(host);}#endif /* CONFIG_PNP */#else /* CONFIG_PM */#define wbsd_platform_suspend NULL#define wbsd_platform_resume NULL#define wbsd_pnp_suspend NULL#define wbsd_pnp_resume NULL#endif /* CONFIG_PM */static struct platform_device *wbsd_device;static struct platform_driver wbsd_driver = { .probe = wbsd_probe, .remove = __devexit_p(wbsd_remove), .suspend = wbsd_platform_suspend, .resume = wbsd_platform_resume, .driver = { .name = DRIVER_NAME, },};#ifdef CONFIG_PNPstatic struct pnp_driver wbsd_pnp_driver = { .name = DRIVER_NAME, .id_table = pnp_dev_table, .probe = wbsd_pnp_probe, .remove = __devexit_p(wbsd_pnp_remove), .suspend = wbsd_pnp_suspend, .resume = wbsd_pnp_resume,};#endif /* CONFIG_PNP *//* * 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");#ifdef CONFIG_PNP if (!nopnp) { result = pnp_register_driver(&wbsd_pnp_driver); if (result < 0) return result; }#endif /* CONFIG_PNP */ if (nopnp) { result = platform_driver_register(&wbsd_driver); if (result < 0) return result; wbsd_device = platform_device_alloc(DRIVER_NAME, -1); if (!wbsd_device) { platform_driver_unregister(&wbsd_driver); return -ENOMEM; } result = platform_device_add(wbsd_device); if (result) { platform_device_put(wbsd_device); platform_driver_unregister(&wbsd_driver); return result; } } return 0;}static void __exit wbsd_drv_exit(void){#ifdef CONFIG_PNP if (!nopnp) pnp_unregister_driver(&wbsd_pnp_driver);#endif /* CONFIG_PNP */ if (nopnp) { platform_device_unregister(wbsd_device); platform_driver_unregister(&wbsd_driver); } DBG("unloaded\n");}module_init(wbsd_drv_init);module_exit(wbsd_drv_exit);#ifdef CONFIG_PNPmodule_param(nopnp, uint, 0444);#endifmodule_param(io, uint, 0444);module_param(irq, uint, 0444);module_param(dma, int, 0444);MODULE_LICENSE("GPL");MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");MODULE_VERSION(DRIVER_VERSION);#ifdef CONFIG_PNPMODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");#endifMODULE_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 + -