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

📄 intel_vr_nor.c

📁 基于linux-2.6.28的mtd驱动
💻 C
字号:
/* * drivers/mtd/maps/intel_vr_nor.c * * An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel * Vermilion Range chipset. * * The Vermilion Range Expansion Bus supports four chip selects, each of which * has 64MiB of address space.  The 2nd BAR of the Expansion Bus PCI Device * is a 256MiB memory region containing the address spaces for all four of the * chip selects, with start addresses hardcoded on 64MiB boundaries. * * This map driver only supports NOR flash on chip select 0.  The buswidth * (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing * and Control Register for Chip Select 0 (EXP_TIMING_CS0).  This driver does * not modify the value in the EXP_TIMING_CS0 register except to enable writing * and disable boot acceleration.  The timing parameters in the register are * assumed to have been properly initialized by the BIOS.  The reset default * timing parameters are maximally conservative (slow), so access to the flash * will be slower than it should be if the BIOS has not initialized the timing * parameters. * * Author: Andy Lowe <alowe@mvista.com> * * 2006 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/mtd/mtd.h>#include <linux/mtd/map.h>#include <linux/mtd/partitions.h>#include <linux/mtd/cfi.h>#include <linux/mtd/flashchip.h>#define DRV_NAME "vr_nor"struct vr_nor_mtd {	void __iomem *csr_base;	struct map_info map;	struct mtd_info *info;	int nr_parts;	struct pci_dev *dev;};/* Expansion Bus Configuration and Status Registers are in BAR 0 */#define EXP_CSR_MBAR 0/* Expansion Bus Memory Window is BAR 1 */#define EXP_WIN_MBAR 1/* Maximum address space for Chip Select 0 is 64MiB */#define CS0_SIZE 0x04000000/* Chip Select 0 is at offset 0 in the Memory Window */#define CS0_START 0x0/* Chip Select 0 Timing Register is at offset 0 in CSR */#define EXP_TIMING_CS0 0x00#define TIMING_CS_EN		(1 << 31)	/* Chip Select Enable */#define TIMING_BOOT_ACCEL_DIS	(1 <<  8)	/* Boot Acceleration Disable */#define TIMING_WR_EN		(1 <<  1)	/* Write Enable */#define TIMING_BYTE_EN		(1 <<  0)	/* 8-bit vs 16-bit bus */#define TIMING_MASK		0x3FFF0000static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p){	if (p->nr_parts > 0) {#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)		del_mtd_partitions(p->info);#endif	} else		del_mtd_device(p->info);}static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p){	int err = 0;#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)	struct mtd_partition *parts;	static const char *part_probes[] = { "cmdlinepart", NULL };#endif	/* register the flash bank */#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)	/* partition the flash bank */	p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0);	if (p->nr_parts > 0)		err = add_mtd_partitions(p->info, parts, p->nr_parts);#endif	if (p->nr_parts <= 0)		err = add_mtd_device(p->info);	return err;}static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p){	map_destroy(p->info);}static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p){	static const char *probe_types[] =	    { "cfi_probe", "jedec_probe", NULL };	const char **type;	for (type = probe_types; !p->info && *type; type++)		p->info = do_map_probe(*type, &p->map);	if (!p->info)		return -ENODEV;	p->info->owner = THIS_MODULE;	return 0;}static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p){	unsigned int exp_timing_cs0;	/* write-protect the flash bank */	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);	exp_timing_cs0 &= ~TIMING_WR_EN;	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);	/* unmap the flash window */	iounmap(p->map.virt);	/* unmap the csr window */	iounmap(p->csr_base);}/* * Initialize the map_info structure and map the flash. * Returns 0 on success, nonzero otherwise. */static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p){	unsigned long csr_phys, csr_len;	unsigned long win_phys, win_len;	unsigned int exp_timing_cs0;	int err;	csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);	csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);	win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);	win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);	if (!csr_phys || !csr_len || !win_phys || !win_len)		return -ENODEV;	if (win_len < (CS0_START + CS0_SIZE))		return -ENXIO;	p->csr_base = ioremap_nocache(csr_phys, csr_len);	if (!p->csr_base)		return -ENOMEM;	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);	if (!(exp_timing_cs0 & TIMING_CS_EN)) {		dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "		       "is disabled.\n");		err = -ENODEV;		goto release;	}	if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {		dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "		       "is configured for maximally slow access times.\n");	}	p->map.name = DRV_NAME;	p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;	p->map.phys = win_phys + CS0_START;	p->map.size = CS0_SIZE;	p->map.virt = ioremap_nocache(p->map.phys, p->map.size);	if (!p->map.virt) {		err = -ENOMEM;		goto release;	}	simple_map_init(&p->map);	/* Enable writes to flash bank */	exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);	return 0;      release:	iounmap(p->csr_base);	return err;}static struct pci_device_id vr_nor_pci_ids[] = {	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},	{0,}};static void __devexit vr_nor_pci_remove(struct pci_dev *dev){	struct vr_nor_mtd *p = pci_get_drvdata(dev);	pci_set_drvdata(dev, NULL);	vr_nor_destroy_partitions(p);	vr_nor_destroy_mtd_setup(p);	vr_nor_destroy_maps(p);	kfree(p);	pci_release_regions(dev);	pci_disable_device(dev);}static int __devinitvr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id){	struct vr_nor_mtd *p = NULL;	unsigned int exp_timing_cs0;	int err;	err = pci_enable_device(dev);	if (err)		goto out;	err = pci_request_regions(dev, DRV_NAME);	if (err)		goto disable_dev;	p = kzalloc(sizeof(*p), GFP_KERNEL);	err = -ENOMEM;	if (!p)		goto release;	p->dev = dev;	err = vr_nor_init_maps(p);	if (err)		goto release;	err = vr_nor_mtd_setup(p);	if (err)		goto destroy_maps;	err = vr_nor_init_partitions(p);	if (err)		goto destroy_mtd_setup;	pci_set_drvdata(dev, p);	return 0;      destroy_mtd_setup:	map_destroy(p->info);      destroy_maps:	/* write-protect the flash bank */	exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);	exp_timing_cs0 &= ~TIMING_WR_EN;	writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);	/* unmap the flash window */	iounmap(p->map.virt);	/* unmap the csr window */	iounmap(p->csr_base);      release:	kfree(p);	pci_release_regions(dev);      disable_dev:	pci_disable_device(dev);      out:	return err;}static struct pci_driver vr_nor_pci_driver = {	.name = DRV_NAME,	.probe = vr_nor_pci_probe,	.remove = __devexit_p(vr_nor_pci_remove),	.id_table = vr_nor_pci_ids,};static int __init vr_nor_mtd_init(void){	return pci_register_driver(&vr_nor_pci_driver);}static void __exit vr_nor_mtd_exit(void){	pci_unregister_driver(&vr_nor_pci_driver);}module_init(vr_nor_mtd_init);module_exit(vr_nor_mtd_exit);MODULE_AUTHOR("Andy Lowe");MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);

⌨️ 快捷键说明

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