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