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

📄 pmc551.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * PMC551 PCI Mezzanine Ram Device * * Author: *	Mark Ferrell <mferrell@mvista.com> *	Copyright 1999,2000 Nortel Networks * * License: *	As part of this driver was derived from the slram.c driver it *	falls under the same license, which is GNU General Public *	License v2 * * Description: *	This driver is intended to support the PMC551 PCI Ram device *	from Ramix Inc.  The PMC551 is a PMC Mezzanine module for *	cPCI embedded systems.  The device contains a single SROM *	that initially programs the V370PDC chipset onboard the *	device, and various banks of DRAM/SDRAM onboard.  This driver *	implements this PCI Ram device as an MTD (Memory Technology *	Device) so that it can be used to hold a file system, or for *	added swap space in embedded systems.  Since the memory on *	this board isn't as fast as main memory we do not try to hook *	it into main memory as that would simply reduce performance *	on the system.  Using it as a block device allows us to use *	it as high speed swap or for a high speed disk device of some *	sort.  Which becomes very useful on diskless systems in the *	embedded market I might add. * * Notes: *	Due to what I assume is more buggy SROM, the 64M PMC551 I *	have available claims that all 4 of its DRAM banks have 64MiB *	of ram configured (making a grand total of 256MiB onboard). *	This is slightly annoying since the BAR0 size reflects the *	aperture size, not the dram size, and the V370PDC supplies no *	other method for memory size discovery.  This problem is *	mostly only relevant when compiled as a module, as the *	unloading of the module with an aperture size smaller then *	the ram will cause the driver to detect the onboard memory *	size to be equal to the aperture size when the module is *	reloaded.  Soooo, to help, the module supports an msize *	option to allow the specification of the onboard memory, and *	an asize option, to allow the specification of the aperture *	size.  The aperture must be equal to or less then the memory *	size, the driver will correct this if you screw it up.  This *	problem is not relevant for compiled in drivers as compiled *	in drivers only init once. * * Credits: *	Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the *	initial example code of how to initialize this device and for *	help with questions I had concerning operation of the device. * *	Most of the MTD code for this driver was originally written *	for the slram.o module in the MTD drivers package which *	allows the mapping of system memory into an MTD device. *	Since the PMC551 memory module is accessed in the same *	fashion as system memory, the slram.c code became a very nice *	fit to the needs of this driver.  All we added was PCI *	detection/initialization to the driver and automatically figure *	out the size via the PCI detection.o, later changes by Corey *	Minyard set up the card to utilize a 1M sliding apature. * *	Corey Minyard <minyard@nortelnetworks.com> *	* Modified driver to utilize a sliding aperture instead of *	 mapping all memory into kernel space which turned out to *	 be very wasteful. *	* Located a bug in the SROM's initialization sequence that *	 made the memory unusable, added a fix to code to touch up *	 the DRAM some. * * Bugs/FIXMEs: *	* MUST fix the init function to not spin on a register *	waiting for it to set .. this does not safely handle busted *	devices that never reset the register correctly which will *	cause the system to hang w/ a reboot being the only chance at *	recover. [sort of fixed, could be better] *	* Add I2C handling of the SROM so we can read the SROM's information *	about the aperture size.  This should always accurately reflect the *	onboard memory size. *	* Comb the init routine.  It's still a bit cludgy on a few things. */#include <linux/kernel.h>#include <linux/module.h>#include <asm/uaccess.h>#include <linux/types.h>#include <linux/init.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/major.h>#include <linux/fs.h>#include <linux/ioctl.h>#include <asm/io.h>#include <asm/system.h>#include <linux/pci.h>#include <linux/mtd/mtd.h>#include <linux/mtd/pmc551.h>#include <linux/mtd/compatmac.h>static struct mtd_info *pmc551list;static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr){	struct mypriv *priv = mtd->priv;	u32 soff_hi, soff_lo;	/* start address offset hi/lo */	u32 eoff_hi, eoff_lo;	/* end address offset hi/lo */	unsigned long end;	u_char *ptr;	size_t retlen;#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_erase(pos:%ld, len:%ld)\n", (long)instr->addr,		(long)instr->len);#endif	end = instr->addr + instr->len - 1;	/* Is it past the end? */	if (end > mtd->size) {#ifdef CONFIG_MTD_PMC551_DEBUG		printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n",			(long)end, (long)mtd->size);#endif		return -EINVAL;	}	eoff_hi = end & ~(priv->asize - 1);	soff_hi = instr->addr & ~(priv->asize - 1);	eoff_lo = end & (priv->asize - 1);	soff_lo = instr->addr & (priv->asize - 1);	pmc551_point(mtd, instr->addr, instr->len, &retlen,		     (void **)&ptr, NULL);	if (soff_hi == eoff_hi || mtd->size == priv->asize) {		/* The whole thing fits within one access, so just one shot		   will do it. */		memset(ptr, 0xff, instr->len);	} else {		/* We have to do multiple writes to get all the data		   written. */		while (soff_hi != eoff_hi) {#ifdef CONFIG_MTD_PMC551_DEBUG			printk(KERN_DEBUG "pmc551_erase() soff_hi: %ld, "				"eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi);#endif			memset(ptr, 0xff, priv->asize);			if (soff_hi + priv->asize >= mtd->size) {				goto out;			}			soff_hi += priv->asize;			pmc551_point(mtd, (priv->base_map0 | soff_hi),				     priv->asize, &retlen,				     (void **)&ptr, NULL);		}		memset(ptr, 0xff, eoff_lo);	}      out:	instr->state = MTD_ERASE_DONE;#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_erase() done\n");#endif	mtd_erase_callback(instr);	return 0;}static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,			size_t *retlen, void **virt, resource_size_t *phys){	struct mypriv *priv = mtd->priv;	u32 soff_hi;	u32 soff_lo;#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len);#endif	if (from + len > mtd->size) {#ifdef CONFIG_MTD_PMC551_DEBUG		printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n",			(long)from + len, (long)mtd->size);#endif		return -EINVAL;	}	/* can we return a physical address with this driver? */	if (phys)		return -EINVAL;	soff_hi = from & ~(priv->asize - 1);	soff_lo = from & (priv->asize - 1);	/* Cheap hack optimization */	if (priv->curr_map0 != from) {		pci_write_config_dword(priv->dev, PMC551_PCI_MEM_MAP0,					(priv->base_map0 | soff_hi));		priv->curr_map0 = soff_hi;	}	*virt = priv->start + soff_lo;	*retlen = len;	return 0;}static void pmc551_unpoint(struct mtd_info *mtd, loff_t from, size_t len){#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_unpoint()\n");#endif}static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,			size_t * retlen, u_char * buf){	struct mypriv *priv = mtd->priv;	u32 soff_hi, soff_lo;	/* start address offset hi/lo */	u32 eoff_hi, eoff_lo;	/* end address offset hi/lo */	unsigned long end;	u_char *ptr;	u_char *copyto = buf;#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_read(pos:%ld, len:%ld) asize: %ld\n",		(long)from, (long)len, (long)priv->asize);#endif	end = from + len - 1;	/* Is it past the end? */	if (end > mtd->size) {#ifdef CONFIG_MTD_PMC551_DEBUG		printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n",			(long)end, (long)mtd->size);#endif		return -EINVAL;	}	soff_hi = from & ~(priv->asize - 1);	eoff_hi = end & ~(priv->asize - 1);	soff_lo = from & (priv->asize - 1);	eoff_lo = end & (priv->asize - 1);	pmc551_point(mtd, from, len, retlen, (void **)&ptr, NULL);	if (soff_hi == eoff_hi) {		/* The whole thing fits within one access, so just one shot		   will do it. */		memcpy(copyto, ptr, len);		copyto += len;	} else {		/* We have to do multiple writes to get all the data		   written. */		while (soff_hi != eoff_hi) {#ifdef CONFIG_MTD_PMC551_DEBUG			printk(KERN_DEBUG "pmc551_read() soff_hi: %ld, "				"eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi);#endif			memcpy(copyto, ptr, priv->asize);			copyto += priv->asize;			if (soff_hi + priv->asize >= mtd->size) {				goto out;			}			soff_hi += priv->asize;			pmc551_point(mtd, soff_hi, priv->asize, retlen,				     (void **)&ptr, NULL);		}		memcpy(copyto, ptr, eoff_lo);		copyto += eoff_lo;	}      out:#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_read() done\n");#endif	*retlen = copyto - buf;	return 0;}static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,			size_t * retlen, const u_char * buf){	struct mypriv *priv = mtd->priv;	u32 soff_hi, soff_lo;	/* start address offset hi/lo */	u32 eoff_hi, eoff_lo;	/* end address offset hi/lo */	unsigned long end;	u_char *ptr;	const u_char *copyfrom = buf;#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_write(pos:%ld, len:%ld) asize:%ld\n",		(long)to, (long)len, (long)priv->asize);#endif	end = to + len - 1;	/* Is it past the end?  or did the u32 wrap? */	if (end > mtd->size) {#ifdef CONFIG_MTD_PMC551_DEBUG		printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, "			"size: %ld, to: %ld)\n", (long)end, (long)mtd->size,			(long)to);#endif		return -EINVAL;	}	soff_hi = to & ~(priv->asize - 1);	eoff_hi = end & ~(priv->asize - 1);	soff_lo = to & (priv->asize - 1);	eoff_lo = end & (priv->asize - 1);	pmc551_point(mtd, to, len, retlen, (void **)&ptr, NULL);	if (soff_hi == eoff_hi) {		/* The whole thing fits within one access, so just one shot		   will do it. */		memcpy(ptr, copyfrom, len);		copyfrom += len;	} else {		/* We have to do multiple writes to get all the data		   written. */		while (soff_hi != eoff_hi) {#ifdef CONFIG_MTD_PMC551_DEBUG			printk(KERN_DEBUG "pmc551_write() soff_hi: %ld, "				"eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi);#endif			memcpy(ptr, copyfrom, priv->asize);			copyfrom += priv->asize;			if (soff_hi >= mtd->size) {				goto out;			}			soff_hi += priv->asize;			pmc551_point(mtd, soff_hi, priv->asize, retlen,				     (void **)&ptr, NULL);		}		memcpy(ptr, copyfrom, eoff_lo);		copyfrom += eoff_lo;	}      out:#ifdef CONFIG_MTD_PMC551_DEBUG	printk(KERN_DEBUG "pmc551_write() done\n");#endif	*retlen = copyfrom - buf;	return 0;}/* * Fixup routines for the V370PDC * PCI device ID 0x020011b0 * * This function basicly kick starts the DRAM oboard the card and gets it * ready to be used.  Before this is done the device reads VERY erratic, so * much that it can crash the Linux 2.2.x series kernels when a user cat's * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL * register.  FIXME: stop spinning on registers .. must implement a timeout * mechanism * returns the size of the memory region found. */static u32 fixup_pmc551(struct pci_dev *dev){#ifdef CONFIG_MTD_PMC551_BUGFIX	u32 dram_data;#endif	u32 size, dcmd, cfg, dtmp;	u16 cmd, tmp, i;	u8 bcmd, counter;	/* Sanity Check */	if (!dev) {		return -ENODEV;	}	/*	 * Attempt to reset the card	 * FIXME: Stop Spinning registers	 */	counter = 0;	/* unlock registers */	pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5);	/* read in old data */	pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd);	/* bang the reset line up and down for a few */	for (i = 0; i < 10; i++) {		counter = 0;		bcmd &= ~0x80;		while (counter++ < 100) {			pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);		}		counter = 0;		bcmd |= 0x80;		while (counter++ < 100) {			pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);		}	}	bcmd |= (0x40 | 0x20);	pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);	/*	 * Take care and turn off the memory on the device while we	 * tweak the configurations	 */	pci_read_config_word(dev, PCI_COMMAND, &cmd);	tmp = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY);	pci_write_config_word(dev, PCI_COMMAND, tmp);	/*	 * Disable existing aperture before probing memory size	 */	pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd);	dtmp = (dcmd | PMC551_PCI_MEM_MAP_ENABLE | PMC551_PCI_MEM_MAP_REG_EN);	pci_write_config_dword(dev, PMC551_PCI_MEM_MAP0, dtmp);	/*	 * Grab old BAR0 config so that we can figure out memory size	 * This is another bit of kludge going on.  The reason for the	 * redundancy is I am hoping to retain the original configuration	 * previously assigned to the card by the BIOS or some previous	 * fixup routine in the kernel.  So we read the old config into cfg,	 * then write all 1's to the memory space, read back the result into	 * "size", and then write back all the old config.	 */	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &cfg);#ifndef CONFIG_MTD_PMC551_BUGFIX	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, ~0);	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &size);	size = (size & PCI_BASE_ADDRESS_MEM_MASK);	size &= ~(size - 1);	pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, cfg);#else	/*	 * Get the size of the memory by reading all the DRAM size values	 * and adding them up.

⌨️ 快捷键说明

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