📄 flash.c
字号:
/* $Id: flash.c,v 1.2 2000/10/06 08:36:02 davidm Exp $ * * Flash-memory char or block device * * Copyright (c) 1998, 1999, 2000 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) * * This driver implements a read/write /erase interface to the flash chips. * It probes for chips, and tries to read a bootblock with partition info from * the bootblock on the first chip. This bootblock is given the device name * /dev/flash0, and the rest of the partitions are given flash1 and upwards. * If no bootblock is found, the entire first chip (including bootblock) is * set as /dev/flash1. * * TODO: * implement Unlock Bypass programming, to double the write speed * fix various loops for partitions than span more than one chip * take advantage of newer chips with 2 kword sector erase sizes * * $Log: flash.c,v $ * Revision 1.2 2000/10/06 08:36:02 davidm * * Changes to get the flash driver used by jffs working with the toshiba * flash in a NETtel with 4Mb. * * This code based on elinux_20000801.tgz. * * Revision 1.52 2000/07/31 18:32:15 finn * flash_ioctl: Made FLASHIO_ERASEALL work better. * * Revision 1.51 2000/07/25 08:38:13 finn * Uncommented CONFIG_USE_FLASH_PARTITION_TABLE. Some cleanup. * * Revision 1.50 2000/07/25 00:24:37 danielj * Fixed so that a partition that start after the end of chip 0 starts in * chip 1. * * Revision 1.49 2000/07/18 12:21:51 finn * Whitespace police. * * Revision 1.48 2000/07/14 16:26:42 johana * Flash partitiontable in flash seem to works but is not enabled. * Local CONFIG_USE_FLASH_PARTITION_TABLE can be defined to enabe it. * Maybe we should have a better magic (not 0xbeef) and a better checksum? * *//* define to make kernel bigger, but with verbose Flash messages *///#define LISAHACK/* if IRQ_LOCKS is enabled, interrupts are disabled while doing things * with the flash that cant have other accesses to the flash at the same * time. this is normally not needed since we have a wait_queue lock on the * flash chip as a mutex. */#include <linux/config.h>#include <linux/major.h>#include <linux/malloc.h>//#define IRQ_LOCKS#define FLASH_VERBOSE#define FLASH_16BIT/* This should perhaps be in kernal config: ? *//* Define it locally to test it - maybe we need a better checksum? * (it must work both with and without aptable defined and with a * trashed flash) */#define CONFIG_USE_FLASH_PARTITION_TABLE/* ROM and flash devices have major 31 (Documentation/devices.txt) * However, KROM is using that major now, so we use an experimental major = 60. * When this driver works, it will support read-only memory as well and we can * remove KROM. * Our minors start at: 16 = /dev/flash0 First flash memory card (rw) *//*#define MAJOR_NR 31 */#define MAJOR_NR 60#define ROM_MINOR 8 /* ro ROM card */#define FLASH_MINOR 16 /* rw Flash card *//* Don't touch the MAX_CHIPS until the hardcoded chip0 and chip1 are removed from the code. */#ifdef CONFIG_COLDFIRE#define MAX_CHIPS 1#else#define MAX_CHIPS 2#endif#define MAX_PARTITIONS 8#define DEF_FLASH2_SIZE 0x0d0000 /* 5 sectors for JFFS partition per default,Now we don't define it*//* all this stuff needs to go in blk.h */#define DEVICE_NAME "Flash/ROM device"#ifdef CONFIG_BLK_DEV_FLASH#define FLASH_SECTSIZE 512 /* arbitrary, but Linux assumes 512 often it seems */#define DEVICE_REQUEST do_flash_request#define DEVICE_NR(device) (MINOR(device))#define DEVICE_ON(device)#define DEVICE_OFF(device)#include <linux/blk.h>/* the device and block sizes for each minor */static int flash_sizes[32];static int flash_blk_sizes[32];#endif#include <asm/system.h>#ifdef CONFIG_SVINTO#include <asm/svinto.h>#endif#include <linux/flash.h>#ifndef MEM_NON_CACHEABLE#define MEM_NON_CACHEABLE 0#endif#ifndef MEM_DRAM_START#define MEM_DRAM_START 0#endif//#define DEBUG#ifdef DEBUG#define FDEBUG(x) x#else#define FDEBUG(x)#endif#if defined(__CRIS__) && !defined(DEBUG)/* breaks badly if we use printk before we flash an image... */extern void console_print_etrax(const char *b, ...);#define safe_printk console_print_etrax#else#define safe_printk printk#endifint flash_write(unsigned char *ptr, const unsigned char *source, unsigned int size);void flash_init_erase(unsigned char *ptr, unsigned int size);static struct flashchip *getchip(unsigned char *ptr);static void flash_init_partitions(void);#ifdef FLASH_16BIT/* 16-bit wide Flash-ROM */enum { unlockAddress1 = 0x5555, unlockData1 = 0xAAAA, unlockAddress2 = 0x2AAA, unlockData2 = 0x5555, manufacturerUnlockData = 0x9090, manufacturerAddress = 0x00, deviceIdAddress = 0x01, programUnlockData = 0xA0A0, resetData = 0xF0F0, sectorEraseUnlockData = 0x8080, sectorEraseUnlockData2 = 0x3030 };typedef unsigned volatile short *flashptr;#else/* 32-bit wide Flash-ROM * Since we have two devices in parallell, we need to duplicate * the special _data_ below so it reaches both chips. */enum { unlockAddress1 = 0x555, unlockData1 = 0x00AA00AA, unlockAddress2 = 0x2AA, unlockData2 = 0x00550055, manufacturerUnlockData = 0x00900090, manufacturerAddress = 0x00, deviceIdAddress = 0x01, programUnlockData = 0x00A000A0, resetData = 0x00F000F0, sectorEraseUnlockData = 0x00800080, sectorEraseUnlockData2 = 0x00300030 };typedef volatile unsigned long *flashptr;#endifenum { ManufacturerAMD = 0x01, AM29F800BB = 0x2258, AM29F800BT = 0x22D6, AM29LV800BB = 0x225B, AM29LV800BT = 0x22DA, AM29LV160BT = 0x22C4};enum { ManufacturerToshiba = 0x0098, TC58FVT160 = 0x00c2, TC58FVB160 = 0x0043};enum { /* ST - www.st.com */ ManufacturerST = 0x0020, M29W800T = 0x00D7 /* Used in 5600, similar to AM29LV800, * but no unlock bypass */};enum { /* SST*/ ManufacturerSST = 0x00BF, SST39LF800 = 0x2781, SST39VF160 = 0x2782};enum { D5_MASK = 0x2020, D6_MASK = 0x4040, D8_MASK = 0x8080};enum { maxNumberOfBootSectors = 8 };/* internal structure for keeping track of flash devices */struct flashchip { unsigned char *start; unsigned char *bootsector; unsigned int bootsectorsize[maxNumberOfBootSectors]; unsigned short manufacturer_id; unsigned short device_id; unsigned short isValid; int size, sectorsize; /* to block accesses during erase and writing etc */ int busy; struct wait_queue *wqueue;};/* and partitions */struct flashpartition { struct flashchip *chip; /* the chip we reside in */ unsigned char *start; /* starting flash mem address */ int size; /* size of partition in bytes */ unsigned int flags; /* protection bits etc */ __u16 type; /* type of partition */};static struct flashchip chips[MAX_CHIPS];static struct flashpartition partitions[MAX_PARTITIONS];/* check if flash is busy */static inline intflash_is_busy(flashptr ptr){ /* this should probably be protected! */ return ((*ptr & D8_MASK) != (0xffffffff & D8_MASK));}static inline intflash_pos_is_clean(unsigned char *ptr){ ptr = (unsigned char *)((unsigned long)ptr | MEM_NON_CACHEABLE); return (*((__u32 *)ptr) == 0xffffffff);}/* Open the device. We don't need to do anything really. */static intflash_open(struct inode *inode, struct file *filp){#ifdef CONFIG_CHR_DEV_FLASH int minor = MINOR(inode->i_rdev); struct flashpartition *part; if(minor < FLASH_MINOR) return -ENODEV; part = &partitions[minor - FLASH_MINOR]; if(!part->start) return -ENODEV; filp->private_data = (void *)part; /* remember for the future */#endif return 0; /* everything went ok */}static voidflash_release(struct inode *inode, struct file *filp){#ifdef CONFIG_BLK_DEV_FLASH sync_dev(inode->i_rdev);#endif return;}#ifdef CONFIG_BLK_DEV_FLASHstatic voiddo_flash_request(){ while(1) { int minor, fsize, opsize; struct flashchip *chip; struct flashpartition *part; unsigned char *fptr; unsigned long flags; INIT_REQUEST; minor = DEVICE_NR(CURRENT_DEV); minor -= FLASH_MINOR; /* for now, just handle requests to the flash minors */ if(minor < 0 || minor >= MAX_PARTITIONS || !partitions[minor].start) { printk(KERN_WARNING "flash: bad minor %d.", minor); end_request(0); continue; } part = partitions + minor; /* get the actual memory address of the sectors requested */ fptr = part->start + CURRENT->sector * FLASH_SECTSIZE; fsize = CURRENT->current_nr_sectors * FLASH_SECTSIZE; /* check so it's not totally out of bounds */ if(fptr + fsize > part->start + part->size) { printk(KERN_WARNING "flash: request past end " "of partition\n"); end_request(0); continue; } /* actually do something, but get a lock on the chip first. * since the partition might span several chips, we need to * loop and lock each chip in turn */ while(fsize > 0) { chip = getchip(fptr); /* how much fits in this chip ? */ opsize = (fptr + fsize) > (chip->start + chip->size) ? (chip->start + chip->size - fptr) : fsize; /* lock the chip */ save_flags(flags); cli(); while(chip->busy) sleep_on(&chip->wqueue); chip->busy = 1; restore_flags(flags); switch(CURRENT->cmd) { case READ: memcpy(CURRENT->buffer, fptr, opsize); FDEBUG(printk("flash read from %p to %p " "size %d\n", fptr, CURRENT->buffer, opsize)); break; case WRITE: FDEBUG(printk("flash write block at 0x%p\n", fptr)); flash_write(fptr, (unsigned char *) CURRENT->buffer, opsize); break; default: /* Shouldn't happen. */ chip->busy = 0; wake_up(&chip->wqueue); end_request(0); continue; } /* release the lock */ chip->busy = 0; wake_up(&chip->wqueue); /* see if there is anything left to write in the next chip */ fsize -= opsize; fptr += opsize; } /* We have a liftoff! */ end_request(1); }}#endif /* CONFIG_BLK_DEV_FLASH */voidflash_safe_acquire(void *part){ struct flashchip *chip = ((struct flashpartition *)part)->chip; while (chip->busy) sleep_on(&chip->wqueue); chip->busy = 1;}voidflash_safe_release(void *part){ struct flashchip *chip = ((struct flashpartition *)part)->chip; chip->busy = 0; wake_up(&chip->wqueue);}/* flash_safe_read and flash_safe_write are used by the JFFS flash filesystem */intflash_safe_read(void *_part, unsigned char *fptr, unsigned char *buf, int count){ struct flashpartition *part = (struct flashpartition *)_part; /* Check so it's not totally out of bounds. */ if(fptr + count > part->start + part->size) { printk(KERN_WARNING "flash: read request past " "end of device (address: 0x%p, size: %d)\n", fptr, count); return -EINVAL; } FDEBUG(printk("flash_safe_read: %d bytes from 0x%p to 0x%p\n", count, fptr, buf)); /* Actually do something, but get a lock on the chip first. */ flash_safe_acquire(part); memcpy(buf, fptr, count); /* Release the lock. */ flash_safe_release(part); return count; /* success */}intflash_safe_write(void *_part, unsigned char *fptr, const unsigned char *buf, int count){ struct flashpartition *part = (struct flashpartition *)_part; int err; /* Check so it's not totally out of bounds. */ if(fptr + count > part->start + part->size) { printk(KERN_WARNING "flash: write operation past " "end of device (address: 0x%p, size: %d)\n", fptr, count); return -EINVAL; } FDEBUG(printk("flash_safe_write: %d bytes from 0x%p to 0x%p\n", count, buf, fptr)); /* Actually do something, but get a lock on the chip first. */ flash_safe_acquire(part); if ((err = flash_write(fptr, buf, count)) < 0) { count = err; } /* Release the lock. */ flash_safe_release(part); return count; /* success */}#ifdef CONFIG_CHR_DEV_FLASHstatic intflash_char_read(struct inode *inode, struct file *filp, char *buf, int count){ int rlen; struct flashpartition *part = (struct flashpartition *)filp->private_data; FDEBUG(printk("flash_char_read\n")); rlen = flash_safe_read(part, (unsigned char *)part->start + filp->f_pos, (unsigned char *)buf, count); /* advance file position pointer */ if(rlen >= 0) filp->f_pos += rlen; return rlen;}static intflash_char_write(struct inode *inode, struct file *filp, const char *buf, int count){ int wlen; struct flashpartition *part = (struct flashpartition *)filp->private_data; FDEBUG(printk("flash_char_write\n"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -