Release 4.12 drivers/char/nwflash.c
  
  
  
/*
 * Flash memory interface rev.5 driver for the Intel
 * Flash chips used on the NetWinder.
 *
 * 20/08/2000   RMK     use __ioremap to map flash into virtual memory
 *                      make a few more places use "volatile"
 * 22/05/2001   RMK     - Lock read against write
 *                      - merge printk level changes (with mods) from Alan Cox.
 *                      - use *ppos as the file position, not file->f_pos.
 *                      - fix check for out of range pos and r/w size
 *
 * Please note that we are tampering with the only flash chip in the
 * machine, which contains the bootup code.  We therefore have the
 * power to convert these machines into doorstops...
 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/jiffies.h>
#include <asm/hardware/dec21285.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <linux/uaccess.h>
/*****************************************************************************/
#include <asm/nwflash.h>
#define	NWFLASH_VERSION "6.4"
static DEFINE_MUTEX(flash_mutex);
static void kick_open(void);
static int get_flash_id(void);
static int erase_block(int nBlock);
static int write_block(unsigned long p, const char __user *buf, int count);
#define KFLASH_SIZE	1024*1024	
//1 Meg
#define KFLASH_SIZE4	4*1024*1024	
//4 Meg
#define KFLASH_ID	0x89A6		
//Intel flash
#define KFLASH_ID4	0xB0D4		
//Intel flash 4Meg
static bool flashdebug;		
//if set - we will display progress msgs
static int gbWriteEnable;
static int gbWriteBase64Enable;
static volatile unsigned char *FLASH_BASE;
static int gbFlashSize = KFLASH_SIZE;
static DEFINE_MUTEX(nwflash_mutex);
static int get_flash_id(void)
{
	volatile unsigned int c1, c2;
	/*
         * try to get flash chip ID
         */
	kick_open();
	c2 = inb(0x80);
	*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x90;
	udelay(15);
	c1 = *(volatile unsigned char *) FLASH_BASE;
	c2 = inb(0x80);
	/*
         * on 4 Meg flash the second byte is actually at offset 2...
         */
	if (c1 == 0xB0)
		c2 = *(volatile unsigned char *) (FLASH_BASE + 2);
	else
		c2 = *(volatile unsigned char *) (FLASH_BASE + 1);
	c2 += (c1 << 8);
	/*
         * set it back to read mode
         */
	*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0xFF;
	if (c2 == KFLASH_ID4)
		gbFlashSize = KFLASH_SIZE4;
	return c2;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 138 | 99.28% | 2 | 66.67% | 
| Linus Torvalds | 1 | 0.72% | 1 | 33.33% | 
| Total | 139 | 100.00% | 3 | 100.00% | 
static long flash_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
	mutex_lock(&flash_mutex);
	switch (cmd) {
	case CMD_WRITE_DISABLE:
		gbWriteBase64Enable = 0;
		gbWriteEnable = 0;
		break;
	case CMD_WRITE_ENABLE:
		gbWriteEnable = 1;
		break;
	case CMD_WRITE_BASE64K_ENABLE:
		gbWriteBase64Enable = 1;
		break;
	default:
		gbWriteBase64Enable = 0;
		gbWriteEnable = 0;
		mutex_unlock(&flash_mutex);
		return -EINVAL;
	}
	mutex_unlock(&flash_mutex);
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 68 | 78.16% | 1 | 33.33% | 
| Arnd Bergmann | 19 | 21.84% | 2 | 66.67% | 
| Total | 87 | 100.00% | 3 | 100.00% | 
static ssize_t flash_read(struct file *file, char __user *buf, size_t size,
			  loff_t *ppos)
{
	ssize_t ret;
	if (flashdebug)
		printk(KERN_DEBUG "flash_read: flash_read: offset=0x%llx, "
		       "buffer=%p, count=0x%zx.\n", *ppos, buf, size);
	/*
         * We now lock against reads and writes. --rmk
         */
	if (mutex_lock_interruptible(&nwflash_mutex))
		return -ERESTARTSYS;
	ret = simple_read_from_buffer(buf, size, ppos, (void *)FLASH_BASE, gbFlashSize);
	mutex_unlock(&nwflash_mutex);
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 45 | 52.94% | 2 | 28.57% | 
| Linus Torvalds | 18 | 21.18% | 1 | 14.29% | 
| Russell King | 10 | 11.76% | 2 | 28.57% | 
| Akinobu Mita | 8 | 9.41% | 1 | 14.29% | 
| Ingo Molnar | 4 | 4.71% | 1 | 14.29% | 
| Total | 85 | 100.00% | 7 | 100.00% | 
static ssize_t flash_write(struct file *file, const char __user *buf,
			   size_t size, loff_t * ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int written;
	int nBlock, temp, rc;
	int i, j;
	if (flashdebug)
		printk("flash_write: offset=0x%lX, buffer=0x%p, count=0x%X.\n",
		       p, buf, count);
	if (!gbWriteEnable)
		return -EINVAL;
	if (p < 64 * 1024 && (!gbWriteBase64Enable))
		return -EINVAL;
	/*
         * check for out of range pos or count
         */
	if (p >= gbFlashSize)
		return count ? -ENXIO : 0;
	if (count > gbFlashSize - p)
		count = gbFlashSize - p;
			
	if (!access_ok(VERIFY_READ, buf, count))
		return -EFAULT;
	/*
         * We now lock against reads and writes. --rmk
         */
	if (mutex_lock_interruptible(&nwflash_mutex))
		return -ERESTARTSYS;
	written = 0;
	nBlock = (int) p >> 16;	//block # of 64K bytes
	/*
         * # of 64K blocks to erase and write
         */
	temp = ((int) (p + count) >> 16) - nBlock + 1;
	/*
         * write ends at exactly 64k boundary?
         */
	if (((int) (p + count) & 0xFFFF) == 0)
		temp -= 1;
	if (flashdebug)
		printk(KERN_DEBUG "flash_write: writing %d block(s) "
			"starting at %d.\n", temp, nBlock);
	for (; temp; temp--, nBlock++) {
		if (flashdebug)
			printk(KERN_DEBUG "flash_write: erasing block %d.\n", nBlock);
		/*
                 * first we have to erase the block(s), where we will write...
                 */
		i = 0;
		j = 0;
	  RetryBlock:
		do {
			rc = erase_block(nBlock);
			i++;
		} while (rc && i < 10);
		if (rc) {
			printk(KERN_ERR "flash_write: erase error %x\n", rc);
			break;
		}
		if (flashdebug)
			printk(KERN_DEBUG "flash_write: writing offset %lX, "
			       "from buf %p, bytes left %X.\n", p, buf,
			       count - written);
		/*
                 * write_block will limit write to space left in this block
                 */
		rc = write_block(p, buf, count - written);
		j++;
		/*
                 * if somehow write verify failed? Can't happen??
                 */
		if (!rc) {
			/*
                         * retry up to 10 times
                         */
			if (j < 10)
				goto RetryBlock;
			else
				/*
                                 * else quit with error...
                                 */
				rc = -1;
		}
		if (rc < 0) {
			printk(KERN_ERR "flash_write: write error %X\n", rc);
			break;
		}
		p += rc;
		buf += rc;
		written += rc;
		*ppos += rc;
		if (flashdebug)
			printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written);
	}
	mutex_unlock(&nwflash_mutex);
	return written;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 359 | 87.35% | 2 | 28.57% | 
| Linus Torvalds | 42 | 10.22% | 1 | 14.29% | 
| Ingo Molnar | 4 | 0.97% | 1 | 14.29% | 
| Russell King | 3 | 0.73% | 1 | 14.29% | 
| Jesper Juhl | 2 | 0.49% | 1 | 14.29% | 
| Michael Hayes | 1 | 0.24% | 1 | 14.29% | 
| Total | 411 | 100.00% | 7 | 100.00% | 
/*
 * The memory devices use the full 32/64 bits of the offset, and so we cannot
 * check against negative addresses: they are ok. The return value is weird,
 * though, in that case (0).
 *
 * also note that seeking relative to the "end of file" isn't supported:
 * it has no meaning, so it returns -EINVAL.
 */
static loff_t flash_llseek(struct file *file, loff_t offset, int orig)
{
	loff_t ret;
	mutex_lock(&flash_mutex);
	if (flashdebug)
		printk(KERN_DEBUG "flash_llseek: offset=0x%X, orig=0x%X.\n",
		       (unsigned int) offset, orig);
	ret = no_seek_end_llseek_size(file, offset, orig, gbFlashSize);
	mutex_unlock(&flash_mutex);
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 34 | 51.52% | 1 | 16.67% | 
| Arnd Bergmann | 10 | 15.15% | 1 | 16.67% | 
| Robert Love | 10 | 15.15% | 1 | 16.67% | 
| Al Viro | 7 | 10.61% | 1 | 16.67% | 
| Dave Jones | 3 | 4.55% | 1 | 16.67% | 
| Linus Torvalds | 2 | 3.03% | 1 | 16.67% | 
| Total | 66 | 100.00% | 6 | 100.00% | 
/*
 * assume that main Write routine did the parameter checking...
 * so just go ahead and erase, what requested!
 */
static int erase_block(int nBlock)
{
	volatile unsigned int c1;
	volatile unsigned char *pWritePtr;
	unsigned long timeout;
	int temp, temp1;
	/*
         * reset footbridge to the correct offset 0 (...0..3)
         */
	*CSR_ROMWRITEREG = 0;
	/*
         * dummy ROM read
         */
	c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
	kick_open();
	/*
         * reset status if old errors
         */
	*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
	/*
         * erase a block...
         * aim at the middle of a current block...
         */
	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + 0x8000 + (nBlock << 16)));
	/*
         * dummy read
         */
	c1 = *pWritePtr;
	kick_open();
	/*
         * erase
         */
	*(volatile unsigned char *) pWritePtr = 0x20;
	/*
         * confirm
         */
	*(volatile unsigned char *) pWritePtr = 0xD0;
	/*
         * wait 10 ms
         */
	msleep(10);
	/*
         * wait while erasing in process (up to 10 sec)
         */
	timeout = jiffies + 10 * HZ;
	c1 = 0;
	while (!(c1 & 0x80) && time_before(jiffies, timeout)) {
		msleep(10);
		/*
                 * read any address
                 */
		c1 = *(volatile unsigned char *) (pWritePtr);
		//              printk("Flash_erase: status=%X.\n",c1);
	}
	/*
         * set flash for normal read access
         */
	kick_open();
//      *(volatile unsigned char*)(FLASH_BASE+0x8000) = 0xFF;
	*(volatile unsigned char *) pWritePtr = 0xFF;	//back to normal operation
	/*
         * check if erase errors were reported
         */
	if (c1 & 0x20) {
		printk(KERN_ERR "flash_erase: err at %p\n", pWritePtr);
		/*
                 * reset error
                 */
		*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
		return -2;
	}
	/*
         * just to make sure - verify if erased OK...
         */
	msleep(10);
	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + (nBlock << 16)));
	for (temp = 0; temp < 16 * 1024; temp++, pWritePtr += 4) {
		if ((temp1 = *(volatile unsigned int *) pWritePtr) != 0xFFFFFFFF) {
			printk(KERN_ERR "flash_erase: verify err at %p = %X\n",
			       pWritePtr, temp1);
			return -1;
		}
	}
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 309 | 94.79% | 1 | 25.00% | 
| Russell King | 6 | 1.84% | 1 | 25.00% | 
| Maximilian Attems | 6 | 1.84% | 1 | 25.00% | 
| Linus Torvalds | 5 | 1.53% | 1 | 25.00% | 
| Total | 326 | 100.00% | 4 | 100.00% | 
/*
 * write_block will limit number of bytes written to the space in this block
 */
static int write_block(unsigned long p, const char __user *buf, int count)
{
	volatile unsigned int c1;
	volatile unsigned int c2;
	unsigned char *pWritePtr;
	unsigned int uAddress;
	unsigned int offset;
	unsigned long timeout;
	unsigned long timeout1;
	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
	/*
         * check if write will end in this block....
         */
	offset = p & 0xFFFF;
	if (offset + count > 0x10000)
		count = 0x10000 - offset;
	/*
         * wait up to 30 sec for this block
         */
	timeout = jiffies + 30 * HZ;
	for (offset = 0; offset < count; offset++, pWritePtr++) {
		uAddress = (unsigned int) pWritePtr;
		uAddress &= 0xFFFFFFFC;
		if (__get_user(c2, buf + offset))
			return -EFAULT;
	  WriteRetry:
	  	/*
                 * dummy read
                 */
		c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
		/*
                 * kick open the write gate
                 */
		kick_open();
		/*
                 * program footbridge to the correct offset...0..3
                 */
		*CSR_ROMWRITEREG = (unsigned int) pWritePtr & 3;
		/*
                 * write cmd
                 */
		*(volatile unsigned char *) (uAddress) = 0x40;
		/*
                 * data to write
                 */
		*(volatile unsigned char *) (uAddress) = c2;
		/*
                 * get status
                 */
		*(volatile unsigned char *) (FLASH_BASE + 0x10000) = 0x70;
		c1 = 0;
		/*
                 * wait up to 1 sec for this byte
                 */
		timeout1 = jiffies + 1 * HZ;
		/*
                 * while not ready...
                 */
		while (!(c1 & 0x80) && time_before(jiffies, timeout1))
			c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
		/*
                 * if timeout getting status
                 */
		if (time_after_eq(jiffies, timeout1)) {
			kick_open();
			/*
                         * reset err
                         */
			*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
			goto WriteRetry;
		}
		/*
                 * switch on read access, as a default flash operation mode
                 */
		kick_open();
		/*
                 * read access
                 */
		*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0xFF;
		/*
                 * if hardware reports an error writing, and not timeout - 
                 * reset the chip and retry
                 */
		if (c1 & 0x10) {
			kick_open();
			/*
                         * reset err
                         */
			*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
			/*
                         * before timeout?
                         */
			if (time_before(jiffies, timeout)) {
				if (flashdebug)
					printk(KERN_DEBUG "write_block: Retrying write at 0x%X)n",
					       pWritePtr - FLASH_BASE);
				/*
                                 * wait couple ms
                                 */
				msleep(10);
				goto WriteRetry;
			} else {
				printk(KERN_ERR "write_block: timeout at 0x%X\n",
				       pWritePtr - FLASH_BASE);
				/*
                                 * return error -2
                                 */
				return -2;
			}
		}
	}
	msleep(10);
	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
	for (offset = 0; offset < count; offset++) {
		char c, c1;
		if (__get_user(c, buf))
			return -EFAULT;
		buf++;
		if ((c1 = *pWritePtr++) != c) {
			printk(KERN_ERR "write_block: verify error at 0x%X (%02X!=%02X)\n",
			       pWritePtr - FLASH_BASE, c1, c);
			return 0;
		}
	}
	return count;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 480 | 96.77% | 1 | 20.00% | 
| Linus Torvalds | 9 | 1.81% | 1 | 20.00% | 
| Maximilian Attems | 4 | 0.81% | 1 | 20.00% | 
| Russell King | 3 | 0.60% | 2 | 40.00% | 
| Total | 496 | 100.00% | 5 | 100.00% | 
static void kick_open(void)
{
	unsigned long flags;
	/*
         * we want to write a bit pattern XXX1 to Xilinx to enable
         * the write gate, which will be open for about the next 2ms.
         */
	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
	nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
	/*
         * let the ISA bus to catch on...
         */
	udelay(25);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 34 | 80.95% | 1 | 25.00% | 
| Russell King | 5 | 11.90% | 1 | 25.00% | 
| Arnd Bergmann | 2 | 4.76% | 1 | 25.00% | 
| Linus Torvalds | 1 | 2.38% | 1 | 25.00% | 
| Total | 42 | 100.00% | 4 | 100.00% | 
static const struct file_operations flash_fops =
{
	.owner		= THIS_MODULE,
	.llseek		= flash_llseek,
	.read		= flash_read,
	.write		= flash_write,
	.unlocked_ioctl	= flash_ioctl,
};
static struct miscdevice flash_miscdev =
{
	FLASH_MINOR,
	"nwflash",
	&flash_fops
};
static int __init nwflash_init(void)
{
	int ret = -ENODEV;
	if (machine_is_netwinder()) {
		int id;
		FLASH_BASE = ioremap(DC21285_FLASH, KFLASH_SIZE4);
		if (!FLASH_BASE)
			goto out;
		id = get_flash_id();
		if ((id != KFLASH_ID) && (id != KFLASH_ID4)) {
			ret = -ENXIO;
			iounmap((void *)FLASH_BASE);
			printk("Flash: incorrect ID 0x%04X.\n", id);
			goto out;
		}
		printk("Flash ROM driver v.%s, flash device ID 0x%04X, size %d Mb.\n",
		       NWFLASH_VERSION, id, gbFlashSize / (1024 * 1024));
		ret = misc_register(&flash_miscdev);
		if (ret < 0) {
			iounmap((void *)FLASH_BASE);
		}
	}
out:
	return ret;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 76 | 56.72% | 2 | 40.00% | 
| Linus Torvalds | 41 | 30.60% | 1 | 20.00% | 
| Michael Still | 16 | 11.94% | 1 | 20.00% | 
| Russell King | 1 | 0.75% | 1 | 20.00% | 
| Total | 134 | 100.00% | 5 | 100.00% | 
static void __exit nwflash_exit(void)
{
	misc_deregister(&flash_miscdev);
	iounmap((void *)FLASH_BASE);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 23 | 95.83% | 2 | 66.67% | 
| Linus Torvalds | 1 | 4.17% | 1 | 33.33% | 
| Total | 24 | 100.00% | 3 | 100.00% | 
MODULE_LICENSE("GPL");
module_param(flashdebug, bool, 0644);
module_init(nwflash_init);
module_exit(nwflash_exit);
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Linus Torvalds (pre-git) | 1711 | 83.79% | 3 | 10.34% | 
| Linus Torvalds | 175 | 8.57% | 4 | 13.79% | 
| Arnd Bergmann | 38 | 1.86% | 3 | 10.34% | 
| Russell King | 32 | 1.57% | 6 | 20.69% | 
| Michael Still | 16 | 0.78% | 1 | 3.45% | 
| Ingo Molnar | 13 | 0.64% | 1 | 3.45% | 
| Art Haas | 10 | 0.49% | 1 | 3.45% | 
| Maximilian Attems | 10 | 0.49% | 1 | 3.45% | 
| Robert Love | 10 | 0.49% | 1 | 3.45% | 
| Akinobu Mita | 8 | 0.39% | 1 | 3.45% | 
| Al Viro | 7 | 0.34% | 1 | 3.45% | 
| Randy Dunlap | 4 | 0.20% | 1 | 3.45% | 
| Dave Jones | 3 | 0.15% | 1 | 3.45% | 
| Jesper Juhl | 2 | 0.10% | 1 | 3.45% | 
| Rusty Russell | 1 | 0.05% | 1 | 3.45% | 
| Arjan van de Ven | 1 | 0.05% | 1 | 3.45% | 
| Michael Hayes | 1 | 0.05% | 1 | 3.45% | 
| Total | 2042 | 100.00% | 29 | 100.00% | 
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.