// SPDX-License-Identifier: GPL-2.0-only /* * 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...
*/
/* * try to get flash chip ID
*/
kick_open();
c2 = inb(0x80);
*(volatileunsignedchar *) (FLASH_BASE + 0x8000) = 0x90;
udelay(15);
c1 = *(volatileunsignedchar *) FLASH_BASE;
c2 = inb(0x80);
/* * on 4 Meg flash the second byte is actually at offset 2...
*/ if (c1 == 0xB0)
c2 = *(volatileunsignedchar *) (FLASH_BASE + 2); else
c2 = *(volatileunsignedchar *) (FLASH_BASE + 1);
c2 += (c1 << 8);
/* * set it back to read mode
*/
*(volatileunsignedchar *) (FLASH_BASE + 0x8000) = 0xFF;
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;
}
static ssize_t flash_write(struct file *file, constchar __user *buf,
size_t size, loff_t * ppos)
{ unsignedlong p = *ppos; unsignedint 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 (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;
}
/* * 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",
(unsignedint) offset, orig);
ret = no_seek_end_llseek_size(file, offset, orig, gbFlashSize);
mutex_unlock(&flash_mutex); return ret;
}
/* * assume that main Write routine did the parameter checking... * so just go ahead and erase, what requested!
*/
/* * 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 = *(volatileunsignedchar *) (pWritePtr); // printk("Flash_erase: status=%X.\n",c1);
}
/* * set flash for normal read access
*/
kick_open(); // *(volatile unsigned char*)(FLASH_BASE+0x8000) = 0xFF;
*(volatileunsignedchar *) 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);
/* * write_block will limit number of bytes written to the space in this block
*/ staticint write_block(unsignedlong p, constchar __user *buf, int count)
{ volatileunsignedint c1; volatileunsignedint c2; unsignedchar *pWritePtr; unsignedint uAddress; unsignedint offset; unsignedlong timeout; unsignedlong timeout1;
/* * data to write
*/
*(volatileunsignedchar *) (uAddress) = c2;
/* * get status
*/
*(volatileunsignedchar *) (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 = *(volatileunsignedchar *) (FLASH_BASE + 0x8000);
/* * if timeout getting status
*/ if (time_after_eq(jiffies, timeout1)) {
kick_open(); /* * reset err
*/
*(volatileunsignedchar *) (FLASH_BASE + 0x8000) = 0x50;
goto WriteRetry;
} /* * switch on read access, as a default flash operation mode
*/
kick_open(); /* * read access
*/
*(volatileunsignedchar *) (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
*/
*(volatileunsignedchar *) (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);
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;
}
staticvoid kick_open(void)
{ unsignedlong 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);
}
staticint __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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.