diff options
author | Dinh Nguyen <dinguyen@altera.com> | 2012-09-27 18:58:05 +0200 |
---|---|---|
committer | Artem Bityutskiy <artem.bityutskiy@linux.intel.com> | 2012-11-15 14:37:46 +0100 |
commit | 2a0a288ec258bd38d8855fad9419aeb45b7e8499 (patch) | |
tree | f726010165435c55d7b54936fa2521391b05a89b | |
parent | mtd: fsl_elbc_nand: remove unnecessary badblock_pattern (diff) | |
download | linux-2a0a288ec258bd38d8855fad9419aeb45b7e8499.tar.xz linux-2a0a288ec258bd38d8855fad9419aeb45b7e8499.zip |
mtd: denali: split the generic driver and PCI layer
The Denali controller can also be found in SoC devices attached to a
simple bus. Move the PCI specific parts into denali_pci so that we can
add a denali_dt that uses the same driver but for a device tree driver
instead of a PCI based device.
Signed-off-by: Jamie Iles <jamie@jamieiles.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
-rw-r--r-- | drivers/mtd/nand/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/denali.c | 162 | ||||
-rw-r--r-- | drivers/mtd/nand/denali.h | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/denali_pci.c | 144 |
5 files changed, 188 insertions, 134 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4883139460be..5102e87f8df0 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -57,8 +57,15 @@ config MTD_NAND_AUTCPU12 access the SmartMediaCard. config MTD_NAND_DENALI - depends on PCI + tristate "Support Denali NAND controller" + help + Enable support for the Denali NAND controller. This should be + combined with either the PCI or platform drivers to provide device + registration. + +config MTD_NAND_DENALI_PCI tristate "Support Denali NAND controller on Intel Moorestown" + depends on PCI && MTD_NAND_DENALI help Enable the driver for NAND flash on Intel Moorestown, using the Denali NAND controller core. @@ -66,7 +73,7 @@ config MTD_NAND_DENALI config MTD_NAND_DENALI_SCRATCH_REG_ADDR hex "Denali NAND size scratch register address" default "0xFF108018" - depends on MTD_NAND_DENALI + depends on MTD_NAND_DENALI_PCI help Some platforms place the NAND chip size in a scratch register because (some versions of) the driver aren't able to automatically diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 2cbd0916b733..96a6171c9d8f 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_DENALI) += denali.o +obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index e706a237170f..0c8bb6bf8424 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -16,14 +16,12 @@ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ - #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/wait.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/pci.h> #include <linux/mtd/mtd.h> #include <linux/module.h> @@ -89,13 +87,6 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." * format the bank into the proper bits for the controller */ #define BANK(x) ((x) << 24) -/* List of platforms this NAND controller has be integrated into */ -static const struct pci_device_id denali_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, - { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, - { /* end: all zeroes */ } -}; - /* forward declarations */ static void clear_interrupts(struct denali_nand_info *denali); static uint32_t wait_for_irq(struct denali_nand_info *denali, @@ -699,7 +690,7 @@ static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) if (comp_res == 0) { /* timeout */ - printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n", intr_status, irq_mask); intr_status = 0; @@ -1305,8 +1296,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, /* TODO: Read OOB data */ break; default: - printk(KERN_ERR ": unsupported command" - " received 0x%x\n", cmd); + pr_err(": unsupported command received 0x%x\n", cmd); break; } } @@ -1425,107 +1415,48 @@ void denali_drv_init(struct denali_nand_info *denali) denali->irq_status = 0; } -/* driver entry point */ -static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +int denali_init(struct denali_nand_info *denali) { - int ret = -ENODEV; - resource_size_t csr_base, mem_base; - unsigned long csr_len, mem_len; - struct denali_nand_info *denali; - - denali = kzalloc(sizeof(*denali), GFP_KERNEL); - if (!denali) - return -ENOMEM; + int ret; - ret = pci_enable_device(dev); - if (ret) { - printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); - goto failed_alloc_memery; - } - - if (id->driver_data == INTEL_CE4100) { + if (denali->platform == INTEL_CE4100) { /* Due to a silicon limitation, we can only support * ONFI timing mode 1 and below. */ if (onfi_timing_mode < -1 || onfi_timing_mode > 1) { - printk(KERN_ERR "Intel CE4100 only supports" - " ONFI timing mode 1 or below\n"); - ret = -EINVAL; - goto failed_enable_dev; - } - denali->platform = INTEL_CE4100; - mem_base = pci_resource_start(dev, 0); - mem_len = pci_resource_len(dev, 1); - csr_base = pci_resource_start(dev, 1); - csr_len = pci_resource_len(dev, 1); - } else { - denali->platform = INTEL_MRST; - csr_base = pci_resource_start(dev, 0); - csr_len = pci_resource_len(dev, 0); - mem_base = pci_resource_start(dev, 1); - mem_len = pci_resource_len(dev, 1); - if (!mem_len) { - mem_base = csr_base + csr_len; - mem_len = csr_len; + pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n"); + return -EINVAL; } } /* Is 32-bit DMA supported? */ - ret = dma_set_mask(&dev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32)); if (ret) { - printk(KERN_ERR "Spectra: no usable DMA configuration\n"); - goto failed_enable_dev; + pr_err("Spectra: no usable DMA configuration\n"); + return ret; } - denali->buf.dma_buf = dma_map_single(&dev->dev, denali->buf.buf, + denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf, DENALI_BUF_SIZE, DMA_BIDIRECTIONAL); - if (dma_mapping_error(&dev->dev, denali->buf.dma_buf)) { - dev_err(&dev->dev, "Spectra: failed to map DMA buffer\n"); - goto failed_enable_dev; - } - - pci_set_master(dev); - denali->dev = &dev->dev; - denali->mtd.dev.parent = &dev->dev; - - ret = pci_request_regions(dev, DENALI_NAND_NAME); - if (ret) { - printk(KERN_ERR "Spectra: Unable to request memory regions\n"); - goto failed_dma_map; - } - - denali->flash_reg = ioremap_nocache(csr_base, csr_len); - if (!denali->flash_reg) { - printk(KERN_ERR "Spectra: Unable to remap memory region\n"); - ret = -ENOMEM; - goto failed_req_regions; - } - - denali->flash_mem = ioremap_nocache(mem_base, mem_len); - if (!denali->flash_mem) { - printk(KERN_ERR "Spectra: ioremap_nocache failed!"); - ret = -ENOMEM; - goto failed_remap_reg; + if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) { + dev_err(denali->dev, "Spectra: failed to map DMA buffer\n"); + return -EIO; } - + denali->mtd.dev.parent = denali->dev; denali_hw_init(denali); denali_drv_init(denali); /* denali_isr register is done after all the hardware * initilization is finished*/ - if (request_irq(dev->irq, denali_isr, IRQF_SHARED, + if (request_irq(denali->irq, denali_isr, IRQF_SHARED, DENALI_NAND_NAME, denali)) { - printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); - ret = -ENODEV; - goto failed_remap_mem; + pr_err("Spectra: Unable to allocate IRQ\n"); + return -ENODEV; } /* now that our ISR is registered, we can enable interrupts */ denali_set_intr_modes(denali, true); - - pci_set_drvdata(dev, denali); - denali->mtd.name = "denali-nand"; denali->mtd.owner = THIS_MODULE; denali->mtd.priv = &denali->nand; @@ -1549,8 +1480,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) */ if (denali->mtd.writesize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) { ret = -ENODEV; - printk(KERN_ERR "Spectra: device size not supported by this " - "version of MTD."); + pr_err("Spectra: device size not supported by this version of MTD."); goto failed_req_irq; } @@ -1602,8 +1532,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } else if (denali->mtd.oobsize < (denali->bbtskipbytes + ECC_8BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE))) { - printk(KERN_ERR "Your NAND chip OOB is not large enough to" - " contain 8bit ECC correction codes"); + pr_err("Your NAND chip OOB is not large enough to \ + contain 8bit ECC correction codes"); goto failed_req_irq; } else { denali->nand.ecc.strength = 8; @@ -1655,56 +1585,24 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ret = mtd_device_register(&denali->mtd, NULL, 0); if (ret) { - dev_err(&dev->dev, "Spectra: Failed to register MTD: %d\n", + dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n", ret); goto failed_req_irq; } return 0; failed_req_irq: - denali_irq_cleanup(dev->irq, denali); -failed_remap_mem: - iounmap(denali->flash_mem); -failed_remap_reg: - iounmap(denali->flash_reg); -failed_req_regions: - pci_release_regions(dev); -failed_dma_map: - dma_unmap_single(&dev->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - DMA_BIDIRECTIONAL); -failed_enable_dev: - pci_disable_device(dev); -failed_alloc_memery: - kfree(denali); + denali_irq_cleanup(denali->irq, denali); + return ret; } +EXPORT_SYMBOL(denali_init); /* driver exit point */ -static void denali_pci_remove(struct pci_dev *dev) +void denali_remove(struct denali_nand_info *denali) { - struct denali_nand_info *denali = pci_get_drvdata(dev); - - nand_release(&denali->mtd); - - denali_irq_cleanup(dev->irq, denali); - - iounmap(denali->flash_reg); - iounmap(denali->flash_mem); - pci_release_regions(dev); - pci_disable_device(dev); - dma_unmap_single(&dev->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, - DMA_BIDIRECTIONAL); - pci_set_drvdata(dev, NULL); - kfree(denali); + denali_irq_cleanup(denali->irq, denali); + dma_unmap_single(denali->dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + DMA_BIDIRECTIONAL); } - -MODULE_DEVICE_TABLE(pci, denali_pci_ids); - -static struct pci_driver denali_pci_driver = { - .name = DENALI_NAND_NAME, - .id_table = denali_pci_ids, - .probe = denali_pci_probe, - .remove = denali_pci_remove, -}; - -module_pci_driver(denali_pci_driver); +EXPORT_SYMBOL(denali_remove); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index fabb9d56b39e..e5aa99583e43 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -487,6 +487,7 @@ struct denali_nand_info { uint32_t irq_status; int irq_debug_array[32]; int idx; + int irq; uint32_t devnum; /* represent how many nands connected */ uint32_t fwblks; /* represent how many blocks FW used */ @@ -496,4 +497,7 @@ struct denali_nand_info { uint32_t max_banks; }; +extern int denali_init(struct denali_nand_info *denali); +extern void denali_remove(struct denali_nand_info *denali); + #endif /*_LLD_NAND_*/ diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c new file mode 100644 index 000000000000..ea074e6c7622 --- /dev/null +++ b/drivers/mtd/nand/denali_pci.c @@ -0,0 +1,144 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/slab.h> + +#include "denali.h" + +#define DENALI_NAND_NAME "denali-nand-pci" + +/* List of platforms this NAND controller has be integrated into */ +static DEFINE_PCI_DEVICE_TABLE(denali_pci_ids) = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, denali_pci_ids); + +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pci_enable_device(dev); + if (ret) { + pr_err("Spectra: pci_enable_device failed.\n"); + goto failed_alloc_memery; + } + + if (id->driver_data == INTEL_CE4100) { + denali->platform = INTEL_CE4100; + mem_base = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 1); + csr_base = pci_resource_start(dev, 1); + csr_len = pci_resource_len(dev, 1); + } else { + denali->platform = INTEL_MRST; + csr_base = pci_resource_start(dev, 0); + csr_len = pci_resource_len(dev, 0); + mem_base = pci_resource_start(dev, 1); + mem_len = pci_resource_len(dev, 1); + if (!mem_len) { + mem_base = csr_base + csr_len; + mem_len = csr_len; + } + } + + pci_set_master(dev); + denali->dev = &dev->dev; + denali->irq = dev->irq; + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + pr_err("Spectra: Unable to request memory regions\n"); + goto failed_enable_dev; + } + + denali->flash_reg = ioremap_nocache(csr_base, csr_len); + if (!denali->flash_reg) { + pr_err("Spectra: Unable to remap memory region\n"); + ret = -ENOMEM; + goto failed_req_regions; + } + + denali->flash_mem = ioremap_nocache(mem_base, mem_len); + if (!denali->flash_mem) { + pr_err("Spectra: ioremap_nocache failed!"); + ret = -ENOMEM; + goto failed_remap_reg; + } + + ret = denali_init(denali); + if (ret) + goto failed_remap_mem; + + pci_set_drvdata(dev, denali); + + return 0; + +failed_remap_mem: + iounmap(denali->flash_mem); +failed_remap_reg: + iounmap(denali->flash_reg); +failed_req_regions: + pci_release_regions(dev); +failed_enable_dev: + pci_disable_device(dev); +failed_alloc_memery: + kfree(denali); + + return ret; +} + +/* driver exit point */ +static void denali_pci_remove(struct pci_dev *dev) +{ + struct denali_nand_info *denali = pci_get_drvdata(dev); + + denali_remove(denali); + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + pci_release_regions(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + kfree(denali); +} + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; + +static int __devinit denali_init_pci(void) +{ + pr_info("Spectra MTD driver built on %s @ %s\n", __DATE__, __TIME__); + return pci_register_driver(&denali_pci_driver); +} +module_init(denali_init_pci); + +static void __devexit denali_exit_pci(void) +{ + pci_unregister_driver(&denali_pci_driver); +} +module_exit(denali_exit_pci); |