pmc551.c 25.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * $Id: pmc551.c,v 1.32 2005/11/07 11:14:25 gleixner Exp $
L
Linus Torvalds 已提交
3 4 5 6
 *
 * PMC551 PCI Mezzanine Ram Device
 *
 * Author:
J
Jiri Slaby 已提交
7 8
 *	Mark Ferrell <mferrell@mvista.com>
 *	Copyright 1999,2000 Nortel Networks
L
Linus Torvalds 已提交
9 10
 *
 * License:
J
Jiri Slaby 已提交
11 12 13
 *	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
L
Linus Torvalds 已提交
14 15
 *
 * Description:
J
Jiri Slaby 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *	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.
30
 *
L
Linus Torvalds 已提交
31
 * Notes:
J
Jiri Slaby 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
 *	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.
L
Linus Torvalds 已提交
49 50
 *
 * Credits:
J
Jiri Slaby 已提交
51 52 53
 *	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.
L
Linus Torvalds 已提交
54
 *
J
Jiri Slaby 已提交
55 56 57 58 59 60 61 62 63
 *	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.
L
Linus Torvalds 已提交
64
 *
J
Jiri Slaby 已提交
65 66 67 68 69 70 71
 *	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.
L
Linus Torvalds 已提交
72 73
 *
 * Bugs/FIXME's:
J
Jiri Slaby 已提交
74 75 76 77 78 79 80 81 82
 *	* 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.
L
Linus Torvalds 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
 */

#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>

#include <linux/mtd/mtd.h>
#include <linux/mtd/pmc551.h>
#include <linux/mtd/compatmac.h>

static struct mtd_info *pmc551list;

J
Jiri Slaby 已提交
108
static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
L
Linus Torvalds 已提交
109
{
J
Jiri Slaby 已提交
110 111 112 113
	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;
L
Linus Torvalds 已提交
114 115 116 117
	u_char *ptr;
	size_t retlen;

#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
118 119
	printk(KERN_DEBUG "pmc551_erase(pos:%ld, len:%ld)\n", (long)instr->addr,
		(long)instr->len);
L
Linus Torvalds 已提交
120 121
#endif

J
Jiri Slaby 已提交
122
	end = instr->addr + instr->len - 1;
L
Linus Torvalds 已提交
123

J
Jiri Slaby 已提交
124 125
	/* Is it past the end? */
	if (end > mtd->size) {
L
Linus Torvalds 已提交
126
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
127 128
		printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n",
			(long)end, (long)mtd->size);
L
Linus Torvalds 已提交
129
#endif
J
Jiri Slaby 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
		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) {
L
Linus Torvalds 已提交
148
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
149 150
			printk(KERN_DEBUG "pmc551_erase() soff_hi: %ld, "
				"eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi);
L
Linus Torvalds 已提交
151
#endif
J
Jiri Slaby 已提交
152 153 154 155 156 157 158 159 160 161 162 163
			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:
L
Linus Torvalds 已提交
164 165 166 167 168
	instr->state = MTD_ERASE_DONE;
#ifdef CONFIG_MTD_PMC551_DEBUG
	printk(KERN_DEBUG "pmc551_erase() done\n");
#endif

J
Jiri Slaby 已提交
169 170
	mtd_erase_callback(instr);
	return 0;
L
Linus Torvalds 已提交
171 172
}

J
Jiri Slaby 已提交
173 174
static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
			size_t * retlen, u_char ** mtdbuf)
L
Linus Torvalds 已提交
175
{
J
Jiri Slaby 已提交
176 177 178
	struct mypriv *priv = mtd->priv;
	u32 soff_hi;
	u32 soff_lo;
L
Linus Torvalds 已提交
179 180 181 182 183 184 185

#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
J
Jiri Slaby 已提交
186 187
		printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n",
			(long)from + len, (long)mtd->size);
L
Linus Torvalds 已提交
188 189 190 191
#endif
		return -EINVAL;
	}

J
Jiri Slaby 已提交
192 193
	soff_hi = from & ~(priv->asize - 1);
	soff_lo = from & (priv->asize - 1);
L
Linus Torvalds 已提交
194 195

	/* Cheap hack optimization */
J
Jiri Slaby 已提交
196 197 198
	if (priv->curr_map0 != from) {
		pci_write_config_dword(priv->dev, PMC551_PCI_MEM_MAP0,
					(priv->base_map0 | soff_hi));
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206
		priv->curr_map0 = soff_hi;
	}

	*mtdbuf = priv->start + soff_lo;
	*retlen = len;
	return 0;
}

J
Jiri Slaby 已提交
207 208
static void pmc551_unpoint(struct mtd_info *mtd, u_char * addr, loff_t from,
			   size_t len)
L
Linus Torvalds 已提交
209 210 211 212 213 214
{
#ifdef CONFIG_MTD_PMC551_DEBUG
	printk(KERN_DEBUG "pmc551_unpoint()\n");
#endif
}

J
Jiri Slaby 已提交
215 216
static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
			size_t * retlen, u_char * buf)
L
Linus Torvalds 已提交
217
{
J
Jiri Slaby 已提交
218 219 220 221
	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;
L
Linus Torvalds 已提交
222
	u_char *ptr;
J
Jiri Slaby 已提交
223
	u_char *copyto = buf;
L
Linus Torvalds 已提交
224 225

#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
226 227
	printk(KERN_DEBUG "pmc551_read(pos:%ld, len:%ld) asize: %ld\n",
		(long)from, (long)len, (long)priv->asize);
L
Linus Torvalds 已提交
228 229
#endif

J
Jiri Slaby 已提交
230
	end = from + len - 1;
L
Linus Torvalds 已提交
231

J
Jiri Slaby 已提交
232 233
	/* Is it past the end? */
	if (end > mtd->size) {
L
Linus Torvalds 已提交
234
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
235 236
		printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n",
			(long)end, (long)mtd->size);
L
Linus Torvalds 已提交
237
#endif
J
Jiri Slaby 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
		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) {
L
Linus Torvalds 已提交
257
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
258 259
			printk(KERN_DEBUG "pmc551_read() soff_hi: %ld, "
				"eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi);
L
Linus Torvalds 已提交
260
#endif
J
Jiri Slaby 已提交
261 262 263 264 265 266 267 268 269 270 271 272 273
			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:
L
Linus Torvalds 已提交
274 275 276
#ifdef CONFIG_MTD_PMC551_DEBUG
	printk(KERN_DEBUG "pmc551_read() done\n");
#endif
J
Jiri Slaby 已提交
277 278
	*retlen = copyto - buf;
	return 0;
L
Linus Torvalds 已提交
279 280
}

J
Jiri Slaby 已提交
281 282
static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
			size_t * retlen, const u_char * buf)
L
Linus Torvalds 已提交
283
{
J
Jiri Slaby 已提交
284 285 286 287
	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;
L
Linus Torvalds 已提交
288
	u_char *ptr;
J
Jiri Slaby 已提交
289
	const u_char *copyfrom = buf;
L
Linus Torvalds 已提交
290 291

#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
292 293
	printk(KERN_DEBUG "pmc551_write(pos:%ld, len:%ld) asize:%ld\n",
		(long)to, (long)len, (long)priv->asize);
L
Linus Torvalds 已提交
294 295
#endif

J
Jiri Slaby 已提交
296 297 298
	end = to + len - 1;
	/* Is it past the end?  or did the u32 wrap? */
	if (end > mtd->size) {
L
Linus Torvalds 已提交
299
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
300 301 302
		printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, "
			"size: %ld, to: %ld)\n", (long)end, (long)mtd->size,
			(long)to);
L
Linus Torvalds 已提交
303
#endif
J
Jiri Slaby 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
		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) {
L
Linus Torvalds 已提交
323
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
324 325
			printk(KERN_DEBUG "pmc551_write() soff_hi: %ld, "
				"eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi);
L
Linus Torvalds 已提交
326
#endif
J
Jiri Slaby 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339
			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:
L
Linus Torvalds 已提交
340 341 342
#ifdef CONFIG_MTD_PMC551_DEBUG
	printk(KERN_DEBUG "pmc551_write() done\n");
#endif
J
Jiri Slaby 已提交
343 344
	*retlen = copyfrom - buf;
	return 0;
L
Linus Torvalds 已提交
345 346 347 348 349 350 351 352 353 354 355 356 357 358
}

/*
 * 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.
 */
J
Jiri Slaby 已提交
359
static u32 fixup_pmc551(struct pci_dev *dev)
L
Linus Torvalds 已提交
360 361
{
#ifdef CONFIG_MTD_PMC551_BUGFIX
J
Jiri Slaby 已提交
362
	u32 dram_data;
L
Linus Torvalds 已提交
363
#endif
J
Jiri Slaby 已提交
364 365
	u32 size, dcmd, cfg, dtmp;
	u16 cmd, tmp, i;
L
Linus Torvalds 已提交
366 367
	u8 bcmd, counter;

J
Jiri Slaby 已提交
368 369 370 371
	/* Sanity Check */
	if (!dev) {
		return -ENODEV;
	}
L
Linus Torvalds 已提交
372 373 374 375 376

	/*
	 * Attempt to reset the card
	 * FIXME: Stop Spinning registers
	 */
J
Jiri Slaby 已提交
377
	counter = 0;
L
Linus Torvalds 已提交
378
	/* unlock registers */
J
Jiri Slaby 已提交
379
	pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5);
L
Linus Torvalds 已提交
380
	/* read in old data */
J
Jiri Slaby 已提交
381
	pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd);
L
Linus Torvalds 已提交
382
	/* bang the reset line up and down for a few */
J
Jiri Slaby 已提交
383 384
	for (i = 0; i < 10; i++) {
		counter = 0;
L
Linus Torvalds 已提交
385
		bcmd &= ~0x80;
J
Jiri Slaby 已提交
386
		while (counter++ < 100) {
L
Linus Torvalds 已提交
387 388
			pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
		}
J
Jiri Slaby 已提交
389
		counter = 0;
L
Linus Torvalds 已提交
390
		bcmd |= 0x80;
J
Jiri Slaby 已提交
391
		while (counter++ < 100) {
L
Linus Torvalds 已提交
392 393 394
			pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
		}
	}
J
Jiri Slaby 已提交
395
	bcmd |= (0x40 | 0x20);
L
Linus Torvalds 已提交
396 397
	pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);

J
Jiri Slaby 已提交
398
	/*
L
Linus Torvalds 已提交
399 400 401
	 * Take care and turn off the memory on the device while we
	 * tweak the configurations
	 */
J
Jiri Slaby 已提交
402 403 404
	pci_read_config_word(dev, PCI_COMMAND, &cmd);
	tmp = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
	pci_write_config_word(dev, PCI_COMMAND, tmp);
L
Linus Torvalds 已提交
405 406 407 408 409

	/*
	 * Disable existing aperture before probing memory size
	 */
	pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd);
J
Jiri Slaby 已提交
410
	dtmp = (dcmd | PMC551_PCI_MEM_MAP_ENABLE | PMC551_PCI_MEM_MAP_REG_EN);
L
Linus Torvalds 已提交
411 412 413 414 415
	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
416
	 * previously assigned to the card by the BIOS or some previous
L
Linus Torvalds 已提交
417 418 419 420
	 * 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.
	 */
J
Jiri Slaby 已提交
421
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &cfg);
L
Linus Torvalds 已提交
422
#ifndef CONFIG_MTD_PMC551_BUGFIX
J
Jiri Slaby 已提交
423 424 425 426 427
	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);
L
Linus Torvalds 已提交
428
#else
J
Jiri Slaby 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
	/*
	 * Get the size of the memory by reading all the DRAM size values
	 * and adding them up.
	 *
	 * KLUDGE ALERT: the boards we are using have invalid column and
	 * row mux values.  We fix them here, but this will break other
	 * memory configurations.
	 */
	pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data);
	size = PMC551_DRAM_BLK_GET_SIZE(dram_data);
	dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
	dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
	pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data);

	pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data);
	size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
	dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
	dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
	pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data);

	pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data);
	size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
	dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
	dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
	pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data);

	pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data);
	size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
	dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
	dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
	pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data);

	/*
	 * Oops .. something went wrong
	 */
	if ((size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) {
		return -ENODEV;
L
Linus Torvalds 已提交
466
	}
J
Jiri Slaby 已提交
467
#endif				/* CONFIG_MTD_PMC551_BUGFIX */
L
Linus Torvalds 已提交
468

J
Jiri Slaby 已提交
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
	if ((cfg & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
		return -ENODEV;
	}

	/*
	 * Precharge Dram
	 */
	pci_write_config_word(dev, PMC551_SDRAM_MA, 0x0400);
	pci_write_config_word(dev, PMC551_SDRAM_CMD, 0x00bf);

	/*
	 * Wait until command has gone through
	 * FIXME: register spinning issue
	 */
	do {
		pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
		if (counter++ > 100)
			break;
	} while ((PCI_COMMAND_IO) & cmd);

	/*
490
	 * Turn on auto refresh
L
Linus Torvalds 已提交
491 492 493
	 * The loop is taken directly from Ramix's example code.  I assume that
	 * this must be held high for some duration of time, but I can find no
	 * documentation refrencing the reasons why.
J
Jiri Slaby 已提交
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
	 */
	for (i = 1; i <= 8; i++) {
		pci_write_config_word(dev, PMC551_SDRAM_CMD, 0x0df);

		/*
		 * Make certain command has gone through
		 * FIXME: register spinning issue
		 */
		counter = 0;
		do {
			pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
			if (counter++ > 100)
				break;
		} while ((PCI_COMMAND_IO) & cmd);
	}

	pci_write_config_word(dev, PMC551_SDRAM_MA, 0x0020);
	pci_write_config_word(dev, PMC551_SDRAM_CMD, 0x0ff);

	/*
	 * Wait until command completes
	 * FIXME: register spinning issue
	 */
	counter = 0;
	do {
		pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
		if (counter++ > 100)
			break;
	} while ((PCI_COMMAND_IO) & cmd);

	pci_read_config_dword(dev, PMC551_DRAM_CFG, &dcmd);
	dcmd |= 0x02000000;
	pci_write_config_dword(dev, PMC551_DRAM_CFG, dcmd);

	/*
	 * Check to make certain fast back-to-back, if not
	 * then set it so
	 */
	pci_read_config_word(dev, PCI_STATUS, &cmd);
	if ((cmd & PCI_COMMAND_FAST_BACK) == 0) {
		cmd |= PCI_COMMAND_FAST_BACK;
		pci_write_config_word(dev, PCI_STATUS, cmd);
	}

	/*
	 * Check to make certain the DEVSEL is set correctly, this device
	 * has a tendancy to assert DEVSEL and TRDY when a write is performed
	 * to the memory when memory is read-only
	 */
	if ((cmd & PCI_STATUS_DEVSEL_MASK) != 0x0) {
		cmd &= ~PCI_STATUS_DEVSEL_MASK;
		pci_write_config_word(dev, PCI_STATUS, cmd);
	}
	/*
	 * Set to be prefetchable and put everything back based on old cfg.
L
Linus Torvalds 已提交
549 550
	 * it's possible that the reset of the V370PDC nuked the original
	 * setup
J
Jiri Slaby 已提交
551
	 */
L
Linus Torvalds 已提交
552
	/*
J
Jiri Slaby 已提交
553 554 555 556 557 558 559 560 561
	   cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH;
	   pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
	 */

	/*
	 * Turn PCI memory and I/O bus access back on
	 */
	pci_write_config_word(dev, PCI_COMMAND,
			      PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
L
Linus Torvalds 已提交
562
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
563 564 565 566 567 568 569 570
	/*
	 * Some screen fun
	 */
	printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at "
		"0x%llx\n", (size < 1024) ? size : (size < 1048576) ?
		size >> 10 : size >> 20,
		(size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size,
		((dcmd & (0x1 << 3)) == 0) ? "non-" : "",
J
Jiri Slaby 已提交
571
		(unsigned long long)pci_resource_start(dev, 0));
J
Jiri Slaby 已提交
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636

	/*
	 * Check to see the state of the memory
	 */
	pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dcmd);
	printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n"
		"pmc551: DRAM_BLK0 Size: %d at %d\n"
		"pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n",
		(((0x1 << 1) & dcmd) == 0) ? "RW" : "RO",
		(((0x1 << 0) & dcmd) == 0) ? "Off" : "On",
		PMC551_DRAM_BLK_GET_SIZE(dcmd),
		((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7),
		((dcmd >> 9) & 0xF));

	pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dcmd);
	printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n"
		"pmc551: DRAM_BLK1 Size: %d at %d\n"
		"pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n",
		(((0x1 << 1) & dcmd) == 0) ? "RW" : "RO",
		(((0x1 << 0) & dcmd) == 0) ? "Off" : "On",
		PMC551_DRAM_BLK_GET_SIZE(dcmd),
		((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7),
		((dcmd >> 9) & 0xF));

	pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dcmd);
	printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n"
		"pmc551: DRAM_BLK2 Size: %d at %d\n"
		"pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n",
		(((0x1 << 1) & dcmd) == 0) ? "RW" : "RO",
		(((0x1 << 0) & dcmd) == 0) ? "Off" : "On",
		PMC551_DRAM_BLK_GET_SIZE(dcmd),
		((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7),
		((dcmd >> 9) & 0xF));

	pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dcmd);
	printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n"
		"pmc551: DRAM_BLK3 Size: %d at %d\n"
		"pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n",
		(((0x1 << 1) & dcmd) == 0) ? "RW" : "RO",
		(((0x1 << 0) & dcmd) == 0) ? "Off" : "On",
		PMC551_DRAM_BLK_GET_SIZE(dcmd),
		((dcmd >> 20) & 0x7FF), ((dcmd >> 13) & 0x7),
		((dcmd >> 9) & 0xF));

	pci_read_config_word(dev, PCI_COMMAND, &cmd);
	printk(KERN_DEBUG "pmc551: Memory Access %s\n",
		(((0x1 << 1) & cmd) == 0) ? "off" : "on");
	printk(KERN_DEBUG "pmc551: I/O Access %s\n",
		(((0x1 << 0) & cmd) == 0) ? "off" : "on");

	pci_read_config_word(dev, PCI_STATUS, &cmd);
	printk(KERN_DEBUG "pmc551: Devsel %s\n",
		((PCI_STATUS_DEVSEL_MASK & cmd) == 0x000) ? "Fast" :
		((PCI_STATUS_DEVSEL_MASK & cmd) == 0x200) ? "Medium" :
		((PCI_STATUS_DEVSEL_MASK & cmd) == 0x400) ? "Slow" : "Invalid");

	printk(KERN_DEBUG "pmc551: %sFast Back-to-Back\n",
		((PCI_COMMAND_FAST_BACK & cmd) == 0) ? "Not " : "");

	pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd);
	printk(KERN_DEBUG "pmc551: EEPROM is under %s control\n"
		"pmc551: System Control Register is %slocked to PCI access\n"
		"pmc551: System Control Register is %slocked to EEPROM access\n",
		(bcmd & 0x1) ? "software" : "hardware",
		(bcmd & 0x20) ? "" : "un", (bcmd & 0x40) ? "" : "un");
L
Linus Torvalds 已提交
637
#endif
J
Jiri Slaby 已提交
638
	return size;
L
Linus Torvalds 已提交
639 640 641 642 643 644 645 646 647 648 649 650 651
}

/*
 * Kernel version specific module stuffages
 */

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Ferrell <mferrell@mvista.com>");
MODULE_DESCRIPTION(PMC551_VERSION);

/*
 * Stuff these outside the ifdef so as to not bust compiled in driver support
 */
J
Jiri Slaby 已提交
652
static int msize = 0;
L
Linus Torvalds 已提交
653
#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
J
Jiri Slaby 已提交
654
static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE
L
Linus Torvalds 已提交
655
#else
J
Jiri Slaby 已提交
656
static int asize = 0;
L
Linus Torvalds 已提交
657 658 659 660 661 662 663 664 665 666 667 668
#endif

module_param(msize, int, 0);
MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]");
module_param(asize, int, 0);
MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]");

/*
 * PMC551 Card Initialization
 */
static int __init init_pmc551(void)
{
J
Jiri Slaby 已提交
669 670 671 672 673 674 675 676 677 678 679
	struct pci_dev *PCI_Device = NULL;
	struct mypriv *priv;
	int count, found = 0;
	struct mtd_info *mtd;
	u32 length = 0;

	if (msize) {
		msize = (1 << (ffs(msize) - 1)) << 20;
		if (msize > (1 << 30)) {
			printk(KERN_NOTICE "pmc551: Invalid memory size [%d]\n",
				msize);
L
Linus Torvalds 已提交
680 681 682 683
			return -EINVAL;
		}
	}

J
Jiri Slaby 已提交
684 685 686 687 688
	if (asize) {
		asize = (1 << (ffs(asize) - 1)) << 20;
		if (asize > (1 << 30)) {
			printk(KERN_NOTICE "pmc551: Invalid aperture size "
				"[%d]\n", asize);
L
Linus Torvalds 已提交
689 690 691 692
			return -EINVAL;
		}
	}

J
Jiri Slaby 已提交
693 694 695 696 697 698 699 700 701 702 703 704 705 706
	printk(KERN_INFO PMC551_VERSION);

	/*
	 * PCU-bus chipset probe.
	 */
	for (count = 0; count < MAX_MTD_DEVICES; count++) {

		if ((PCI_Device = pci_get_device(PCI_VENDOR_ID_V3_SEMI,
						  PCI_DEVICE_ID_V3_SEMI_V370PDC,
						  PCI_Device)) == NULL) {
			break;
		}

		printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%llx\n",
J
Jiri Slaby 已提交
707
			(unsigned long long)pci_resource_start(PCI_Device, 0));
J
Jiri Slaby 已提交
708 709 710 711 712 713 714 715 716 717 718 719 720

		/*
		 * The PMC551 device acts VERY weird if you don't init it
		 * first.  i.e. it will not correctly report devsel.  If for
		 * some reason the sdram is in a wrote-protected state the
		 * device will DEVSEL when it is written to causing problems
		 * with the oldproc.c driver in
		 * some kernels (2.2.*)
		 */
		if ((length = fixup_pmc551(PCI_Device)) <= 0) {
			printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n");
			break;
		}
L
Linus Torvalds 已提交
721 722 723 724 725

		/*
		 * This is needed until the driver is capable of reading the
		 * onboard I2C SROM to discover the "real" memory size.
		 */
J
Jiri Slaby 已提交
726
		if (msize) {
L
Linus Torvalds 已提交
727
			length = msize;
J
Jiri Slaby 已提交
728 729
			printk(KERN_NOTICE "pmc551: Using specified memory "
				"size 0x%x\n", length);
L
Linus Torvalds 已提交
730 731 732 733
		} else {
			msize = length;
		}

J
Jiri Slaby 已提交
734
		mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
J
Jiri Slaby 已提交
735 736 737 738 739 740
		if (!mtd) {
			printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
				"device.\n");
			break;
		}

J
Jiri Slaby 已提交
741
		priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);
J
Jiri Slaby 已提交
742 743 744 745 746 747 748 749 750 751 752 753
		if (!priv) {
			printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
				"device.\n");
			kfree(mtd);
			break;
		}
		mtd->priv = priv;
		priv->dev = PCI_Device;

		if (asize > length) {
			printk(KERN_NOTICE "pmc551: reducing aperture size to "
				"fit %dM\n", length >> 20);
L
Linus Torvalds 已提交
754 755
			priv->asize = asize = length;
		} else if (asize == 0 || asize == length) {
J
Jiri Slaby 已提交
756 757
			printk(KERN_NOTICE "pmc551: Using existing aperture "
				"size %dM\n", length >> 20);
L
Linus Torvalds 已提交
758 759
			priv->asize = asize = length;
		} else {
J
Jiri Slaby 已提交
760 761
			printk(KERN_NOTICE "pmc551: Using specified aperture "
				"size %dM\n", asize >> 20);
L
Linus Torvalds 已提交
762 763
			priv->asize = asize;
		}
J
Jiri Slaby 已提交
764
		priv->start = pci_iomap(PCI_Device, 0, priv->asize);
765

L
Linus Torvalds 已提交
766 767
		if (!priv->start) {
			printk(KERN_NOTICE "pmc551: Unable to map IO space\n");
J
Jiri Slaby 已提交
768 769
			kfree(mtd->priv);
			kfree(mtd);
L
Linus Torvalds 已提交
770 771 772
			break;
		}
#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
773 774
		printk(KERN_DEBUG "pmc551: setting aperture to %d\n",
			ffs(priv->asize >> 20) - 1);
L
Linus Torvalds 已提交
775 776
#endif

J
Jiri Slaby 已提交
777 778 779 780 781 782
		priv->base_map0 = (PMC551_PCI_MEM_MAP_REG_EN
				   | PMC551_PCI_MEM_MAP_ENABLE
				   | (ffs(priv->asize >> 20) - 1) << 4);
		priv->curr_map0 = priv->base_map0;
		pci_write_config_dword(priv->dev, PMC551_PCI_MEM_MAP0,
					priv->curr_map0);
L
Linus Torvalds 已提交
783 784

#ifdef CONFIG_MTD_PMC551_DEBUG
J
Jiri Slaby 已提交
785 786
		printk(KERN_DEBUG "pmc551: aperture set to %d\n",
			(priv->base_map0 & 0xF0) >> 4);
L
Linus Torvalds 已提交
787 788
#endif

J
Jiri Slaby 已提交
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
		mtd->size = msize;
		mtd->flags = MTD_CAP_RAM;
		mtd->erase = pmc551_erase;
		mtd->read = pmc551_read;
		mtd->write = pmc551_write;
		mtd->point = pmc551_point;
		mtd->unpoint = pmc551_unpoint;
		mtd->type = MTD_RAM;
		mtd->name = "PMC551 RAM board";
		mtd->erasesize = 0x10000;
		mtd->writesize = 1;
		mtd->owner = THIS_MODULE;

		if (add_mtd_device(mtd)) {
			printk(KERN_NOTICE "pmc551: Failed to register new "
				"device\n");
J
Jiri Slaby 已提交
805
			pci_iounmap(PCI_Device, priv->start);
J
Jiri Slaby 已提交
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
			kfree(mtd->priv);
			kfree(mtd);
			break;
		}

		/* Keep a reference as the add_mtd_device worked */
		pci_dev_get(PCI_Device);

		printk(KERN_NOTICE "Registered pmc551 memory device.\n");
		printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
			priv->asize >> 20,
			priv->start, priv->start + priv->asize);
		printk(KERN_NOTICE "Total memory is %d%c\n",
			(length < 1024) ? length :
			(length < 1048576) ? length >> 10 : length >> 20,
			(length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M');
L
Linus Torvalds 已提交
822 823 824
		priv->nextpmc551 = pmc551list;
		pmc551list = mtd;
		found++;
J
Jiri Slaby 已提交
825
	}
L
Linus Torvalds 已提交
826

J
Jiri Slaby 已提交
827 828 829
	/* Exited early, reference left over */
	if (PCI_Device)
		pci_dev_put(PCI_Device);
830

J
Jiri Slaby 已提交
831 832 833 834
	if (!pmc551list) {
		printk(KERN_NOTICE "pmc551: not detected\n");
		return -ENODEV;
	} else {
L
Linus Torvalds 已提交
835
		printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found);
J
Jiri Slaby 已提交
836
		return 0;
L
Linus Torvalds 已提交
837 838 839 840 841 842 843 844
	}
}

/*
 * PMC551 Card Cleanup
 */
static void __exit cleanup_pmc551(void)
{
J
Jiri Slaby 已提交
845 846
	int found = 0;
	struct mtd_info *mtd;
L
Linus Torvalds 已提交
847 848
	struct mypriv *priv;

J
Jiri Slaby 已提交
849
	while ((mtd = pmc551list)) {
L
Linus Torvalds 已提交
850 851
		priv = mtd->priv;
		pmc551list = priv->nextpmc551;
852

J
Jiri Slaby 已提交
853 854 855
		if (priv->start) {
			printk(KERN_DEBUG "pmc551: unmapping %dM starting at "
				"0x%p\n", priv->asize >> 20, priv->start);
J
Jiri Slaby 已提交
856
			pci_iounmap(priv->dev, priv->start);
L
Linus Torvalds 已提交
857
		}
858
		pci_dev_put(priv->dev);
859

J
Jiri Slaby 已提交
860 861 862
		kfree(mtd->priv);
		del_mtd_device(mtd);
		kfree(mtd);
L
Linus Torvalds 已提交
863 864 865 866 867 868 869 870
		found++;
	}

	printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found);
}

module_init(init_pmc551);
module_exit(cleanup_pmc551);