18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Flash memory interface rev.5 driver for the Intel
48c2ecf20Sopenharmony_ci * Flash chips used on the NetWinder.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * 20/08/2000	RMK	use __ioremap to map flash into virtual memory
78c2ecf20Sopenharmony_ci *			make a few more places use "volatile"
88c2ecf20Sopenharmony_ci * 22/05/2001	RMK	- Lock read against write
98c2ecf20Sopenharmony_ci *			- merge printk level changes (with mods) from Alan Cox.
108c2ecf20Sopenharmony_ci *			- use *ppos as the file position, not file->f_pos.
118c2ecf20Sopenharmony_ci *			- fix check for out of range pos and r/w size
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Please note that we are tampering with the only flash chip in the
148c2ecf20Sopenharmony_ci * machine, which contains the bootup code.  We therefore have the
158c2ecf20Sopenharmony_ci * power to convert these machines into doorstops...
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/fs.h>
218c2ecf20Sopenharmony_ci#include <linux/errno.h>
228c2ecf20Sopenharmony_ci#include <linux/mm.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
258c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
268c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
278c2ecf20Sopenharmony_ci#include <linux/rwsem.h>
288c2ecf20Sopenharmony_ci#include <linux/init.h>
298c2ecf20Sopenharmony_ci#include <linux/mutex.h>
308c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <asm/hardware/dec21285.h>
338c2ecf20Sopenharmony_ci#include <asm/io.h>
348c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
358c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*****************************************************************************/
388c2ecf20Sopenharmony_ci#include <asm/nwflash.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define	NWFLASH_VERSION "6.4"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(flash_mutex);
438c2ecf20Sopenharmony_cistatic void kick_open(void);
448c2ecf20Sopenharmony_cistatic int get_flash_id(void);
458c2ecf20Sopenharmony_cistatic int erase_block(int nBlock);
468c2ecf20Sopenharmony_cistatic int write_block(unsigned long p, const char __user *buf, int count);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define KFLASH_SIZE	1024*1024	//1 Meg
498c2ecf20Sopenharmony_ci#define KFLASH_SIZE4	4*1024*1024	//4 Meg
508c2ecf20Sopenharmony_ci#define KFLASH_ID	0x89A6		//Intel flash
518c2ecf20Sopenharmony_ci#define KFLASH_ID4	0xB0D4		//Intel flash 4Meg
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic bool flashdebug;		//if set - we will display progress msgs
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int gbWriteEnable;
568c2ecf20Sopenharmony_cistatic int gbWriteBase64Enable;
578c2ecf20Sopenharmony_cistatic volatile unsigned char *FLASH_BASE;
588c2ecf20Sopenharmony_cistatic int gbFlashSize = KFLASH_SIZE;
598c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nwflash_mutex);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int get_flash_id(void)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	volatile unsigned int c1, c2;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/*
668c2ecf20Sopenharmony_ci	 * try to get flash chip ID
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	kick_open();
698c2ecf20Sopenharmony_ci	c2 = inb(0x80);
708c2ecf20Sopenharmony_ci	*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x90;
718c2ecf20Sopenharmony_ci	udelay(15);
728c2ecf20Sopenharmony_ci	c1 = *(volatile unsigned char *) FLASH_BASE;
738c2ecf20Sopenharmony_ci	c2 = inb(0x80);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/*
768c2ecf20Sopenharmony_ci	 * on 4 Meg flash the second byte is actually at offset 2...
778c2ecf20Sopenharmony_ci	 */
788c2ecf20Sopenharmony_ci	if (c1 == 0xB0)
798c2ecf20Sopenharmony_ci		c2 = *(volatile unsigned char *) (FLASH_BASE + 2);
808c2ecf20Sopenharmony_ci	else
818c2ecf20Sopenharmony_ci		c2 = *(volatile unsigned char *) (FLASH_BASE + 1);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	c2 += (c1 << 8);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * set it back to read mode
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0xFF;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (c2 == KFLASH_ID4)
918c2ecf20Sopenharmony_ci		gbFlashSize = KFLASH_SIZE4;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return c2;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic long flash_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	mutex_lock(&flash_mutex);
998c2ecf20Sopenharmony_ci	switch (cmd) {
1008c2ecf20Sopenharmony_ci	case CMD_WRITE_DISABLE:
1018c2ecf20Sopenharmony_ci		gbWriteBase64Enable = 0;
1028c2ecf20Sopenharmony_ci		gbWriteEnable = 0;
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	case CMD_WRITE_ENABLE:
1068c2ecf20Sopenharmony_ci		gbWriteEnable = 1;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	case CMD_WRITE_BASE64K_ENABLE:
1108c2ecf20Sopenharmony_ci		gbWriteBase64Enable = 1;
1118c2ecf20Sopenharmony_ci		break;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	default:
1148c2ecf20Sopenharmony_ci		gbWriteBase64Enable = 0;
1158c2ecf20Sopenharmony_ci		gbWriteEnable = 0;
1168c2ecf20Sopenharmony_ci		mutex_unlock(&flash_mutex);
1178c2ecf20Sopenharmony_ci		return -EINVAL;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	mutex_unlock(&flash_mutex);
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic ssize_t flash_read(struct file *file, char __user *buf, size_t size,
1248c2ecf20Sopenharmony_ci			  loff_t *ppos)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	ssize_t ret;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (flashdebug)
1298c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "flash_read: flash_read: offset=0x%llx, "
1308c2ecf20Sopenharmony_ci		       "buffer=%p, count=0x%zx.\n", *ppos, buf, size);
1318c2ecf20Sopenharmony_ci	/*
1328c2ecf20Sopenharmony_ci	 * We now lock against reads and writes. --rmk
1338c2ecf20Sopenharmony_ci	 */
1348c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&nwflash_mutex))
1358c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(buf, size, ppos, (void *)FLASH_BASE, gbFlashSize);
1388c2ecf20Sopenharmony_ci	mutex_unlock(&nwflash_mutex);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return ret;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic ssize_t flash_write(struct file *file, const char __user *buf,
1448c2ecf20Sopenharmony_ci			   size_t size, loff_t * ppos)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	unsigned long p = *ppos;
1478c2ecf20Sopenharmony_ci	unsigned int count = size;
1488c2ecf20Sopenharmony_ci	int written;
1498c2ecf20Sopenharmony_ci	int nBlock, temp, rc;
1508c2ecf20Sopenharmony_ci	int i, j;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (flashdebug)
1538c2ecf20Sopenharmony_ci		printk("flash_write: offset=0x%lX, buffer=0x%p, count=0x%X.\n",
1548c2ecf20Sopenharmony_ci		       p, buf, count);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (!gbWriteEnable)
1578c2ecf20Sopenharmony_ci		return -EINVAL;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (p < 64 * 1024 && (!gbWriteBase64Enable))
1608c2ecf20Sopenharmony_ci		return -EINVAL;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/*
1638c2ecf20Sopenharmony_ci	 * check for out of range pos or count
1648c2ecf20Sopenharmony_ci	 */
1658c2ecf20Sopenharmony_ci	if (p >= gbFlashSize)
1668c2ecf20Sopenharmony_ci		return count ? -ENXIO : 0;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (count > gbFlashSize - p)
1698c2ecf20Sopenharmony_ci		count = gbFlashSize - p;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (!access_ok(buf, count))
1728c2ecf20Sopenharmony_ci		return -EFAULT;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/*
1758c2ecf20Sopenharmony_ci	 * We now lock against reads and writes. --rmk
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&nwflash_mutex))
1788c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	written = 0;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	nBlock = (int) p >> 16;	//block # of 64K bytes
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/*
1858c2ecf20Sopenharmony_ci	 * # of 64K blocks to erase and write
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	temp = ((int) (p + count) >> 16) - nBlock + 1;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/*
1908c2ecf20Sopenharmony_ci	 * write ends at exactly 64k boundary?
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	if (((int) (p + count) & 0xFFFF) == 0)
1938c2ecf20Sopenharmony_ci		temp -= 1;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (flashdebug)
1968c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "flash_write: writing %d block(s) "
1978c2ecf20Sopenharmony_ci			"starting at %d.\n", temp, nBlock);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	for (; temp; temp--, nBlock++) {
2008c2ecf20Sopenharmony_ci		if (flashdebug)
2018c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "flash_write: erasing block %d.\n", nBlock);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		/*
2048c2ecf20Sopenharmony_ci		 * first we have to erase the block(s), where we will write...
2058c2ecf20Sopenharmony_ci		 */
2068c2ecf20Sopenharmony_ci		i = 0;
2078c2ecf20Sopenharmony_ci		j = 0;
2088c2ecf20Sopenharmony_ci	  RetryBlock:
2098c2ecf20Sopenharmony_ci		do {
2108c2ecf20Sopenharmony_ci			rc = erase_block(nBlock);
2118c2ecf20Sopenharmony_ci			i++;
2128c2ecf20Sopenharmony_ci		} while (rc && i < 10);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		if (rc) {
2158c2ecf20Sopenharmony_ci			printk(KERN_ERR "flash_write: erase error %x\n", rc);
2168c2ecf20Sopenharmony_ci			break;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci		if (flashdebug)
2198c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "flash_write: writing offset %lX, "
2208c2ecf20Sopenharmony_ci			       "from buf %p, bytes left %X.\n", p, buf,
2218c2ecf20Sopenharmony_ci			       count - written);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		/*
2248c2ecf20Sopenharmony_ci		 * write_block will limit write to space left in this block
2258c2ecf20Sopenharmony_ci		 */
2268c2ecf20Sopenharmony_ci		rc = write_block(p, buf, count - written);
2278c2ecf20Sopenharmony_ci		j++;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		/*
2308c2ecf20Sopenharmony_ci		 * if somehow write verify failed? Can't happen??
2318c2ecf20Sopenharmony_ci		 */
2328c2ecf20Sopenharmony_ci		if (!rc) {
2338c2ecf20Sopenharmony_ci			/*
2348c2ecf20Sopenharmony_ci			 * retry up to 10 times
2358c2ecf20Sopenharmony_ci			 */
2368c2ecf20Sopenharmony_ci			if (j < 10)
2378c2ecf20Sopenharmony_ci				goto RetryBlock;
2388c2ecf20Sopenharmony_ci			else
2398c2ecf20Sopenharmony_ci				/*
2408c2ecf20Sopenharmony_ci				 * else quit with error...
2418c2ecf20Sopenharmony_ci				 */
2428c2ecf20Sopenharmony_ci				rc = -1;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci		if (rc < 0) {
2468c2ecf20Sopenharmony_ci			printk(KERN_ERR "flash_write: write error %X\n", rc);
2478c2ecf20Sopenharmony_ci			break;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci		p += rc;
2508c2ecf20Sopenharmony_ci		buf += rc;
2518c2ecf20Sopenharmony_ci		written += rc;
2528c2ecf20Sopenharmony_ci		*ppos += rc;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		if (flashdebug)
2558c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written);
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	mutex_unlock(&nwflash_mutex);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return written;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/*
2658c2ecf20Sopenharmony_ci * The memory devices use the full 32/64 bits of the offset, and so we cannot
2668c2ecf20Sopenharmony_ci * check against negative addresses: they are ok. The return value is weird,
2678c2ecf20Sopenharmony_ci * though, in that case (0).
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * also note that seeking relative to the "end of file" isn't supported:
2708c2ecf20Sopenharmony_ci * it has no meaning, so it returns -EINVAL.
2718c2ecf20Sopenharmony_ci */
2728c2ecf20Sopenharmony_cistatic loff_t flash_llseek(struct file *file, loff_t offset, int orig)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	loff_t ret;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	mutex_lock(&flash_mutex);
2778c2ecf20Sopenharmony_ci	if (flashdebug)
2788c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "flash_llseek: offset=0x%X, orig=0x%X.\n",
2798c2ecf20Sopenharmony_ci		       (unsigned int) offset, orig);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	ret = no_seek_end_llseek_size(file, offset, orig, gbFlashSize);
2828c2ecf20Sopenharmony_ci	mutex_unlock(&flash_mutex);
2838c2ecf20Sopenharmony_ci	return ret;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * assume that main Write routine did the parameter checking...
2898c2ecf20Sopenharmony_ci * so just go ahead and erase, what requested!
2908c2ecf20Sopenharmony_ci */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int erase_block(int nBlock)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	volatile unsigned int c1;
2958c2ecf20Sopenharmony_ci	volatile unsigned char *pWritePtr;
2968c2ecf20Sopenharmony_ci	unsigned long timeout;
2978c2ecf20Sopenharmony_ci	int temp, temp1;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/*
3008c2ecf20Sopenharmony_ci	 * reset footbridge to the correct offset 0 (...0..3)
3018c2ecf20Sopenharmony_ci	 */
3028c2ecf20Sopenharmony_ci	*CSR_ROMWRITEREG = 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/*
3058c2ecf20Sopenharmony_ci	 * dummy ROM read
3068c2ecf20Sopenharmony_ci	 */
3078c2ecf20Sopenharmony_ci	c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	kick_open();
3108c2ecf20Sopenharmony_ci	/*
3118c2ecf20Sopenharmony_ci	 * reset status if old errors
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/*
3168c2ecf20Sopenharmony_ci	 * erase a block...
3178c2ecf20Sopenharmony_ci	 * aim at the middle of a current block...
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + 0x8000 + (nBlock << 16)));
3208c2ecf20Sopenharmony_ci	/*
3218c2ecf20Sopenharmony_ci	 * dummy read
3228c2ecf20Sopenharmony_ci	 */
3238c2ecf20Sopenharmony_ci	c1 = *pWritePtr;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	kick_open();
3268c2ecf20Sopenharmony_ci	/*
3278c2ecf20Sopenharmony_ci	 * erase
3288c2ecf20Sopenharmony_ci	 */
3298c2ecf20Sopenharmony_ci	*(volatile unsigned char *) pWritePtr = 0x20;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/*
3328c2ecf20Sopenharmony_ci	 * confirm
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	*(volatile unsigned char *) pWritePtr = 0xD0;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * wait 10 ms
3388c2ecf20Sopenharmony_ci	 */
3398c2ecf20Sopenharmony_ci	msleep(10);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/*
3428c2ecf20Sopenharmony_ci	 * wait while erasing in process (up to 10 sec)
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	timeout = jiffies + 10 * HZ;
3458c2ecf20Sopenharmony_ci	c1 = 0;
3468c2ecf20Sopenharmony_ci	while (!(c1 & 0x80) && time_before(jiffies, timeout)) {
3478c2ecf20Sopenharmony_ci		msleep(10);
3488c2ecf20Sopenharmony_ci		/*
3498c2ecf20Sopenharmony_ci		 * read any address
3508c2ecf20Sopenharmony_ci		 */
3518c2ecf20Sopenharmony_ci		c1 = *(volatile unsigned char *) (pWritePtr);
3528c2ecf20Sopenharmony_ci		//              printk("Flash_erase: status=%X.\n",c1);
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/*
3568c2ecf20Sopenharmony_ci	 * set flash for normal read access
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	kick_open();
3598c2ecf20Sopenharmony_ci//      *(volatile unsigned char*)(FLASH_BASE+0x8000) = 0xFF;
3608c2ecf20Sopenharmony_ci	*(volatile unsigned char *) pWritePtr = 0xFF;	//back to normal operation
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/*
3638c2ecf20Sopenharmony_ci	 * check if erase errors were reported
3648c2ecf20Sopenharmony_ci	 */
3658c2ecf20Sopenharmony_ci	if (c1 & 0x20) {
3668c2ecf20Sopenharmony_ci		printk(KERN_ERR "flash_erase: err at %p\n", pWritePtr);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		/*
3698c2ecf20Sopenharmony_ci		 * reset error
3708c2ecf20Sopenharmony_ci		 */
3718c2ecf20Sopenharmony_ci		*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
3728c2ecf20Sopenharmony_ci		return -2;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	/*
3768c2ecf20Sopenharmony_ci	 * just to make sure - verify if erased OK...
3778c2ecf20Sopenharmony_ci	 */
3788c2ecf20Sopenharmony_ci	msleep(10);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + (nBlock << 16)));
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	for (temp = 0; temp < 16 * 1024; temp++, pWritePtr += 4) {
3838c2ecf20Sopenharmony_ci		if ((temp1 = *(volatile unsigned int *) pWritePtr) != 0xFFFFFFFF) {
3848c2ecf20Sopenharmony_ci			printk(KERN_ERR "flash_erase: verify err at %p = %X\n",
3858c2ecf20Sopenharmony_ci			       pWritePtr, temp1);
3868c2ecf20Sopenharmony_ci			return -1;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/*
3958c2ecf20Sopenharmony_ci * write_block will limit number of bytes written to the space in this block
3968c2ecf20Sopenharmony_ci */
3978c2ecf20Sopenharmony_cistatic int write_block(unsigned long p, const char __user *buf, int count)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	volatile unsigned int c1;
4008c2ecf20Sopenharmony_ci	volatile unsigned int c2;
4018c2ecf20Sopenharmony_ci	unsigned char *pWritePtr;
4028c2ecf20Sopenharmony_ci	unsigned int uAddress;
4038c2ecf20Sopenharmony_ci	unsigned int offset;
4048c2ecf20Sopenharmony_ci	unsigned long timeout;
4058c2ecf20Sopenharmony_ci	unsigned long timeout1;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/*
4108c2ecf20Sopenharmony_ci	 * check if write will end in this block....
4118c2ecf20Sopenharmony_ci	 */
4128c2ecf20Sopenharmony_ci	offset = p & 0xFFFF;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (offset + count > 0x10000)
4158c2ecf20Sopenharmony_ci		count = 0x10000 - offset;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/*
4188c2ecf20Sopenharmony_ci	 * wait up to 30 sec for this block
4198c2ecf20Sopenharmony_ci	 */
4208c2ecf20Sopenharmony_ci	timeout = jiffies + 30 * HZ;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	for (offset = 0; offset < count; offset++, pWritePtr++) {
4238c2ecf20Sopenharmony_ci		uAddress = (unsigned int) pWritePtr;
4248c2ecf20Sopenharmony_ci		uAddress &= 0xFFFFFFFC;
4258c2ecf20Sopenharmony_ci		if (__get_user(c2, buf + offset))
4268c2ecf20Sopenharmony_ci			return -EFAULT;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	  WriteRetry:
4298c2ecf20Sopenharmony_ci	  	/*
4308c2ecf20Sopenharmony_ci	  	 * dummy read
4318c2ecf20Sopenharmony_ci	  	 */
4328c2ecf20Sopenharmony_ci		c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		/*
4358c2ecf20Sopenharmony_ci		 * kick open the write gate
4368c2ecf20Sopenharmony_ci		 */
4378c2ecf20Sopenharmony_ci		kick_open();
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		/*
4408c2ecf20Sopenharmony_ci		 * program footbridge to the correct offset...0..3
4418c2ecf20Sopenharmony_ci		 */
4428c2ecf20Sopenharmony_ci		*CSR_ROMWRITEREG = (unsigned int) pWritePtr & 3;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		/*
4458c2ecf20Sopenharmony_ci		 * write cmd
4468c2ecf20Sopenharmony_ci		 */
4478c2ecf20Sopenharmony_ci		*(volatile unsigned char *) (uAddress) = 0x40;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		/*
4508c2ecf20Sopenharmony_ci		 * data to write
4518c2ecf20Sopenharmony_ci		 */
4528c2ecf20Sopenharmony_ci		*(volatile unsigned char *) (uAddress) = c2;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		/*
4558c2ecf20Sopenharmony_ci		 * get status
4568c2ecf20Sopenharmony_ci		 */
4578c2ecf20Sopenharmony_ci		*(volatile unsigned char *) (FLASH_BASE + 0x10000) = 0x70;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		c1 = 0;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci		/*
4628c2ecf20Sopenharmony_ci		 * wait up to 1 sec for this byte
4638c2ecf20Sopenharmony_ci		 */
4648c2ecf20Sopenharmony_ci		timeout1 = jiffies + 1 * HZ;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		/*
4678c2ecf20Sopenharmony_ci		 * while not ready...
4688c2ecf20Sopenharmony_ci		 */
4698c2ecf20Sopenharmony_ci		while (!(c1 & 0x80) && time_before(jiffies, timeout1))
4708c2ecf20Sopenharmony_ci			c1 = *(volatile unsigned char *) (FLASH_BASE + 0x8000);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		/*
4738c2ecf20Sopenharmony_ci		 * if timeout getting status
4748c2ecf20Sopenharmony_ci		 */
4758c2ecf20Sopenharmony_ci		if (time_after_eq(jiffies, timeout1)) {
4768c2ecf20Sopenharmony_ci			kick_open();
4778c2ecf20Sopenharmony_ci			/*
4788c2ecf20Sopenharmony_ci			 * reset err
4798c2ecf20Sopenharmony_ci			 */
4808c2ecf20Sopenharmony_ci			*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci			goto WriteRetry;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci		/*
4858c2ecf20Sopenharmony_ci		 * switch on read access, as a default flash operation mode
4868c2ecf20Sopenharmony_ci		 */
4878c2ecf20Sopenharmony_ci		kick_open();
4888c2ecf20Sopenharmony_ci		/*
4898c2ecf20Sopenharmony_ci		 * read access
4908c2ecf20Sopenharmony_ci		 */
4918c2ecf20Sopenharmony_ci		*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0xFF;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		/*
4948c2ecf20Sopenharmony_ci		 * if hardware reports an error writing, and not timeout -
4958c2ecf20Sopenharmony_ci		 * reset the chip and retry
4968c2ecf20Sopenharmony_ci		 */
4978c2ecf20Sopenharmony_ci		if (c1 & 0x10) {
4988c2ecf20Sopenharmony_ci			kick_open();
4998c2ecf20Sopenharmony_ci			/*
5008c2ecf20Sopenharmony_ci			 * reset err
5018c2ecf20Sopenharmony_ci			 */
5028c2ecf20Sopenharmony_ci			*(volatile unsigned char *) (FLASH_BASE + 0x8000) = 0x50;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci			/*
5058c2ecf20Sopenharmony_ci			 * before timeout?
5068c2ecf20Sopenharmony_ci			 */
5078c2ecf20Sopenharmony_ci			if (time_before(jiffies, timeout)) {
5088c2ecf20Sopenharmony_ci				if (flashdebug)
5098c2ecf20Sopenharmony_ci					printk(KERN_DEBUG "write_block: Retrying write at 0x%X)n",
5108c2ecf20Sopenharmony_ci					       pWritePtr - FLASH_BASE);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci				/*
5138c2ecf20Sopenharmony_ci				 * wait couple ms
5148c2ecf20Sopenharmony_ci				 */
5158c2ecf20Sopenharmony_ci				msleep(10);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci				goto WriteRetry;
5188c2ecf20Sopenharmony_ci			} else {
5198c2ecf20Sopenharmony_ci				printk(KERN_ERR "write_block: timeout at 0x%X\n",
5208c2ecf20Sopenharmony_ci				       pWritePtr - FLASH_BASE);
5218c2ecf20Sopenharmony_ci				/*
5228c2ecf20Sopenharmony_ci				 * return error -2
5238c2ecf20Sopenharmony_ci				 */
5248c2ecf20Sopenharmony_ci				return -2;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci			}
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	msleep(10);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	for (offset = 0; offset < count; offset++) {
5358c2ecf20Sopenharmony_ci		char c, c1;
5368c2ecf20Sopenharmony_ci		if (__get_user(c, buf))
5378c2ecf20Sopenharmony_ci			return -EFAULT;
5388c2ecf20Sopenharmony_ci		buf++;
5398c2ecf20Sopenharmony_ci		if ((c1 = *pWritePtr++) != c) {
5408c2ecf20Sopenharmony_ci			printk(KERN_ERR "write_block: verify error at 0x%X (%02X!=%02X)\n",
5418c2ecf20Sopenharmony_ci			       pWritePtr - FLASH_BASE, c1, c);
5428c2ecf20Sopenharmony_ci			return 0;
5438c2ecf20Sopenharmony_ci		}
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	return count;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void kick_open(void)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	unsigned long flags;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/*
5558c2ecf20Sopenharmony_ci	 * we want to write a bit pattern XXX1 to Xilinx to enable
5568c2ecf20Sopenharmony_ci	 * the write gate, which will be open for about the next 2ms.
5578c2ecf20Sopenharmony_ci	 */
5588c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
5598c2ecf20Sopenharmony_ci	nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
5608c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/*
5638c2ecf20Sopenharmony_ci	 * let the ISA bus to catch on...
5648c2ecf20Sopenharmony_ci	 */
5658c2ecf20Sopenharmony_ci	udelay(25);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic const struct file_operations flash_fops =
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5718c2ecf20Sopenharmony_ci	.llseek		= flash_llseek,
5728c2ecf20Sopenharmony_ci	.read		= flash_read,
5738c2ecf20Sopenharmony_ci	.write		= flash_write,
5748c2ecf20Sopenharmony_ci	.unlocked_ioctl	= flash_ioctl,
5758c2ecf20Sopenharmony_ci};
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic struct miscdevice flash_miscdev =
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	NWFLASH_MINOR,
5808c2ecf20Sopenharmony_ci	"nwflash",
5818c2ecf20Sopenharmony_ci	&flash_fops
5828c2ecf20Sopenharmony_ci};
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic int __init nwflash_init(void)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	int ret = -ENODEV;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (machine_is_netwinder()) {
5898c2ecf20Sopenharmony_ci		int id;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		FLASH_BASE = ioremap(DC21285_FLASH, KFLASH_SIZE4);
5928c2ecf20Sopenharmony_ci		if (!FLASH_BASE)
5938c2ecf20Sopenharmony_ci			goto out;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		id = get_flash_id();
5968c2ecf20Sopenharmony_ci		if ((id != KFLASH_ID) && (id != KFLASH_ID4)) {
5978c2ecf20Sopenharmony_ci			ret = -ENXIO;
5988c2ecf20Sopenharmony_ci			iounmap((void *)FLASH_BASE);
5998c2ecf20Sopenharmony_ci			printk("Flash: incorrect ID 0x%04X.\n", id);
6008c2ecf20Sopenharmony_ci			goto out;
6018c2ecf20Sopenharmony_ci		}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		printk("Flash ROM driver v.%s, flash device ID 0x%04X, size %d Mb.\n",
6048c2ecf20Sopenharmony_ci		       NWFLASH_VERSION, id, gbFlashSize / (1024 * 1024));
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		ret = misc_register(&flash_miscdev);
6078c2ecf20Sopenharmony_ci		if (ret < 0) {
6088c2ecf20Sopenharmony_ci			iounmap((void *)FLASH_BASE);
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ciout:
6128c2ecf20Sopenharmony_ci	return ret;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic void __exit nwflash_exit(void)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	misc_deregister(&flash_miscdev);
6188c2ecf20Sopenharmony_ci	iounmap((void *)FLASH_BASE);
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cimodule_param(flashdebug, bool, 0644);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cimodule_init(nwflash_init);
6268c2ecf20Sopenharmony_cimodule_exit(nwflash_exit);
627