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>
This commit is contained in:
Родитель
30fad64325
Коммит
2a0a288ec2
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
int ret;
|
||||
|
||||
denali = kzalloc(sizeof(*denali), GFP_KERNEL);
|
||||
if (!denali)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
|
||||
dev_err(denali->dev, "Spectra: failed to map DMA buffer\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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_*/
|
||||
|
|
|
@ -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);
|
Загрузка…
Ссылка в новой задаче