📄 pmc551.c
字号:
/* * $Id: pmc551.c,v 1.11 2000/11/23 13:40:12 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * * Author: * Mark Ferrell <mferrell@mvista.com> * Copyright 1999,2000 Nortel Networks * * License: * As part of this driver was derrived 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 embeded * systems. The device contains a single SROM that initally 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 * Technologies Device) so that it can be used to hold a filesystem, or * for added swap space in embeded systems. Since the memory on this * board isn't as fast as main memory we do not try to hook it into main * memeory 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 usefull on * diskless systems in the embeded 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 relivant 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 relivant 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 written by David Hinds * <dhinds@allegro.stanford.edu> 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 automaticly figure out * the size via the PCI detection.o, later changes by Corey Minyard * settup the card to utilize a 1M sliding apature. * * Corey Minyard <minyard@nortelnetworks.com> * * Modified driver to utilize a sliding apature instead of mapping all * memory into kernel space which turned out to be very wastefull. * * 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 beeing the only chance at recover. */#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/malloc.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 <asm/segment.h>#include <stdarg.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>#if LINUX_VERSION_CODE > 0x20300#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)#else#define PCI_BASE_ADDRESS(dev) (dev->base_address[0])#endifstatic struct mtd_info *pmc551list = NULL;static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr){ struct mypriv *priv = mtd->priv; u32 start_addr_highbits; u32 end_addr_highbits; u32 start_addr_lowbits; u32 end_addr_lowbits; unsigned long end; end = instr->addr + instr->len; /* Is it too much memory? The second check find if we wrap around past the end of a u32. */ if ((end > mtd->size) || (end < instr->addr)) { return -EINVAL; } start_addr_highbits = instr->addr & PMC551_ADDR_HIGH_MASK; end_addr_highbits = end & PMC551_ADDR_HIGH_MASK; start_addr_lowbits = instr->addr & PMC551_ADDR_LOW_MASK; end_addr_lowbits = end & PMC551_ADDR_LOW_MASK; pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, (priv->mem_map0_base_val | start_addr_highbits)); if (start_addr_highbits == end_addr_highbits) { /* The whole thing fits within one access, so just one shot will do it. */ memset(priv->start + start_addr_lowbits, 0xff, instr->len); } else { /* We have to do multiple writes to get all the data written. */ memset(priv->start + start_addr_lowbits, 0xff, priv->aperture_size - start_addr_lowbits); start_addr_highbits += priv->aperture_size; while (start_addr_highbits != end_addr_highbits) { pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, (priv->mem_map0_base_val | start_addr_highbits)); memset(priv->start, 0xff, priv->aperture_size); start_addr_highbits += priv->aperture_size; } priv->curr_mem_map0_val = (priv->mem_map0_base_val | start_addr_highbits); pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, priv->curr_mem_map0_val); memset(priv->start, 0xff, end_addr_lowbits); } instr->state = MTD_ERASE_DONE; if (instr->callback) { (*(instr->callback))(instr); } return 0;}static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr){}static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct mypriv *priv = (struct mypriv *)mtd->priv; u32 start_addr_highbits; u32 end_addr_highbits; u32 start_addr_lowbits; u32 end_addr_lowbits; unsigned long end; u_char *copyto = buf; /* Is it past the end? */ if (from > mtd->size) { return -EINVAL; } end = from + len; start_addr_highbits = from & PMC551_ADDR_HIGH_MASK; end_addr_highbits = end & PMC551_ADDR_HIGH_MASK; start_addr_lowbits = from & PMC551_ADDR_LOW_MASK; end_addr_lowbits = end & PMC551_ADDR_LOW_MASK; /* Only rewrite the first value if it doesn't match our current values. Most operations are on the same page as the previous value, so this is a pretty good optimization. */ if (priv->curr_mem_map0_val != (priv->mem_map0_base_val | start_addr_highbits)) { priv->curr_mem_map0_val = (priv->mem_map0_base_val | start_addr_highbits); pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, priv->curr_mem_map0_val); } if (start_addr_highbits == end_addr_highbits) { /* The whole thing fits within one access, so just one shot will do it. */ memcpy(copyto, priv->start + start_addr_lowbits, len); copyto += len; } else { /* We have to do multiple writes to get all the data written. */ memcpy(copyto, priv->start + start_addr_lowbits, priv->aperture_size - start_addr_lowbits); copyto += priv->aperture_size - start_addr_lowbits; start_addr_highbits += priv->aperture_size; while (start_addr_highbits != end_addr_highbits) { pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, (priv->mem_map0_base_val | start_addr_highbits)); memcpy(copyto, priv->start, priv->aperture_size); copyto += priv->aperture_size; start_addr_highbits += priv->aperture_size; if (start_addr_highbits >= mtd->size) { /* Make sure we have the right value here. */ priv->curr_mem_map0_val = (priv->mem_map0_base_val | start_addr_highbits); goto out; } } priv->curr_mem_map0_val = (priv->mem_map0_base_val | start_addr_highbits); pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, priv->curr_mem_map0_val); memcpy(copyto, priv->start, end_addr_lowbits); copyto += end_addr_lowbits; }out: *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 = (struct mypriv *)mtd->priv; u32 start_addr_highbits; u32 end_addr_highbits; u32 start_addr_lowbits; u32 end_addr_lowbits; unsigned long end; const u_char *copyfrom = buf; /* Is it past the end? */ if (to > mtd->size) { return -EINVAL; } end = to + len; start_addr_highbits = to & PMC551_ADDR_HIGH_MASK; end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -