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

📄 pmc551.c

📁 老版本的mtd-snap
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * $Id: pmc551.c,v 1.31 2005/03/17 19:44:27 gleixner Exp $ * * 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 it's DRAM banks have 64M *	 of ram configured (making a grand total of 256M 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/FIXME's: *       * 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/version.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <asm/uaccess.h>#include <linux/types.h>#include <linux/sched.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>#ifndef CONFIG_PCI#error Enable PCI in your kernel config#endif#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, &ptr);        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, &ptr);                }                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, u_char **mtdbuf){        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;	}        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;	}	*mtdbuf = priv->start + soff_lo;	*retlen = len;	return 0;}static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr, 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, &ptr);        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, &ptr);                }                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, &ptr);        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, &ptr);                }                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 );

⌨️ 快捷键说明

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