📄 flash.patch
字号:
+ kfree(flash);+ return;+ }++ fdi->num_regions = cfi_read_char(ptr, CFI_NUM_ERASE_BLOCKS_OFFSET,+ 0, bit_width);+ DEBUG("Num regions: %d\n", fdi->num_regions);++ fdi->region = kmalloc(sizeof(flash_region_info_t)*fdi->num_regions, GFP_KERNEL);+ if (!fdi->region) {+ kfree(flash);+ kfree(fdi);+ return;+ }++ flash->ptr = ptr;+ flash->size = size;+ flash->bit_width = bit_width;+ flash->chip_cfg = chip_cfg;+ flash->mutex = MUTEX;++ fdi->ops = &amd_ops;+ fdi->next = NULL;+ fdi->handle = flash;+ fdi->dev = 0;++ offset = 0;+ for (i=0;i<fdi->num_regions;i++) {+ reg_info = cfi_read_int(ptr, CFI_ERASE_REGION_INFO_OFFSET+4*i,+ 0, bit_width, chip_cfg);+ fdi->region[i].offset = offset;+ sector_size = (reg_info >> 16) & 0xFFFF;+ if (sector_size==0)+ fdi->region[i].sector_size = 128 << chip_cfg;+ else+ fdi->region[i].sector_size = (sector_size*256) << chip_cfg;++ fdi->region[i].num_sectors = (reg_info & 0xFFFF) + 1;++ DEBUG("sector %i size: %d, num_sectors: %d, total size: %d\n", i,+ fdi->region[i].sector_size , fdi->region[i].num_sectors,+ fdi->region[i].sector_size * fdi->region[i].num_sectors);++ offset += fdi->region[i].sector_size * fdi->region[i].num_sectors;+ }++ register_flash(fdi);++ /* Return to read array mode: */++ ptr[0] = 0xff;+ ptr[1] = 0xff;+ ptr[2] = 0xff;+ ptr[3] = 0xff;+}++static u_int read_with_chip_width(struct amd_flash *flash, u_long offs)+{+ u_int data;+ vu_char *ptr = flash->ptr;++ switch(individual_bit_width(flash)) {+ case 0: /* 8bit */+ data = (ptr[offs]);+ break;+ case 1: /* 16bit */+ data = (*(u_short *)&ptr[offs]);+ break;+ case 2: /* 32bit */+ data = (*(u_int *)&ptr[offs]);+ break;+ }+ return data;+}++#define PROGRAM_WRITE 1+#define PROGRAM_ERASE 2++static int program_in_progress(struct amd_flash *flash)+{+ /* This function doesn't work reliably if erase_suspend is used. */+ vu_char *ptr;+ int i;+ int num_chip;+ u_int data1, data2, data;++ num_chip = 1<<flash->chip_cfg;++ ptr = flash->ptr;++ for (i=0;i<num_chip;i++) {+ data1 = read_with_chip_width(flash, 0) & 0x44; /* mask out DQ6 and DQ2 */+ data2 = read_with_chip_width(flash, 0) & 0x44 ;/* mask out DQ6 and DQ2 */+ data = data1 ^ data2; /* xor */+ if (data&0x40) {+ /* DQ6 toggled, some program is running (might be in erase suspend) */+ DEBUG("dq6 on chip %d toggled, embedded program running.\n", i);+ if (data&0x04) {+ DEBUG("dq2 on chip %d toggled, erase runnung.\n", i);+ return PROGRAM_ERASE;+ } else {+ DEBUG("dq2 on chip %d not toggled, write runnung.\n", i);+ return PROGRAM_WRITE;+ }+ }+ }+ return 0; /* No program in progress. */+}++static void do_erase_sector(vu_char *ptr, off_t offset)+{+ u_long j;+ u_int data;+ vu_int *addr;++ j = jiffies;++ addr = (vu_int *)(&ptr[0x555 << 2]); *addr = 0x00AA00AA;+ addr = (vu_int *)(&ptr[0x2AA << 2]); *addr = 0x00550055;+ addr = (vu_int *)(&ptr[0x555 << 2]); *addr = 0x00800080;+ addr = (vu_int *)(&ptr[0x555 << 2]); *addr = 0x00AA00AA;+ addr = (vu_int *)(&ptr[0x2AA << 2]); *addr = 0x00550055;+ addr = (vu_int *)(&ptr[offset ]); *addr = 0x00300030;++ current->state = TASK_INTERRUPTIBLE;+ schedule_timeout(10*100/HZ); /* Time out 0.1 sec. */+ while ((data = *(vu_int *)&ptr[offset])!=0xffffffff) {+ current->state = TASK_INTERRUPTIBLE;+ schedule_timeout(10*100/HZ); /* Time out 0.1 sec. */+ }++ j = jiffies - j;+ DEBUG("Erasing @ 0x%lx took %ld jiffies\n", offset, j);+}++static int amd_read(struct flash_device_info *fdi,+ char *buf, size_t count, off_t offset, int userspace_ptr)+{+ struct amd_flash *flash = fdi->handle;+ int ret;++ ret = 0;++/********+ DEBUG("amd_read buf=0x%lx, ptr=0x%x, count=%d, offset=0x%lx, us_ptr=0x%ld\n",+ (u_long)buf, (int)flash->ptr, (int)count, offset, (long) userspace_ptr);+********/++ down(&flash->mutex);++ if (program_in_progress(flash)) {+ printk("Error, embedded program in progress during flash read\n");+ ret = -EIO;+ goto error;+ }++ if (userspace_ptr) {+ if (copy_to_user(buf, (char *)flash->ptr+offset, count) > 0) {+ ret = -EFAULT;+ }+ } else {+ memcpy(buf, (char *)flash->ptr+offset, count);+ }++ error:++ up(&flash->mutex);++ return ret;+}++static void unlock_bypass(vu_char *ptr, off_t offset)+{+ vu_int *addr;++ addr = (vu_int *)(&ptr[0x555 << 2]); *addr = 0x00AA00AA;+ addr = (vu_int *)(&ptr[0x2AA << 2]); *addr = 0x00550055;+ addr = (vu_int *)(&ptr[0x555 << 2]); *addr = 0x00200020;+}++static void unlock_bypass_reset(vu_char *ptr, off_t offset)+{+ /* Address is "don't care" for unlock_bypass_reset */+ *(vu_int *)&ptr[offset] = 0x00900090;+ *(vu_int *)&ptr[offset] = 0x00000000;+}++static int do_write_int(vu_char *ptr, off_t offset, u_int data )+{+ int wait, wait1;+ u_int old;++ if ( ((old = *(vu_int *)&ptr[offset]) & data) != data) {+ printk("Flash write error, not fully erased at 0x%p+0x%lx: old 0x%08x new 0x%08x\n", ptr, offset, old, data);+ return (-1);+ }+ /* Program word: */+ /* Address is "don't care" in unlock_bypass mode */+ *(vu_int *)&ptr[offset] = 0x00A000A0;+ *(vu_int *)&ptr[offset] = data;++ wait1 = wait = 0;+ while ((old = *(vu_int *)&ptr[offset]) != data) {+ wait++;+ if ((wait%10000)==0) {+ DEBUG("do_write_int, wait=%d\n", wait);+ DEBUG("addr = 0x%x, offset = 0x%lx, data = 0x%08x, flash: 0x%08x\n",+ (int)&ptr[offset], offset, data, old);+ if (++wait1 > 32) {+ return (-1);+ }+ }+ /* Busy wait */+ }+ return (0);+}++static int do_write_char(vu_char *ptr, off_t offset, u_char data )+{+ int wait, wait1;+ off_t s_offset;+ u_short value, old;+ u_char c;++ if ( ((c = ptr[offset]) & data) != data) {+ printk("Flash write error, not fully erased at 0x%p+0x%lx: old 0x%02x new 0x%02x\n",+ ptr, offset, c, data);+ return (-1);+ }++ s_offset = offset & (~1);+ old = *(vu_short *)&ptr[s_offset];++ if (s_offset == offset) { /* even address */+ value = (old & 0x00FF) | (data << 8);+ } else { /* off address */+ value = (old & 0xFF00) | data;+ }++ *(vu_short *)&ptr[s_offset] = 0x00A0;+ *(vu_short *)&ptr[s_offset] = value;++ wait1 = wait = 0;+ while ((c = ptr[offset]) != data) {+ wait++;+ if ((wait%1000)==0) {+ old = *(vu_short *)&ptr[s_offset];+ DEBUG("do_write_char, wait=%d, "+ "offset = %lx, data = %02x, flash: %02x [0x%04x]\n",+ wait, offset, data, c, old);+ if (++wait1 > 32) {+ return (-1);+ }+ }+ /* Busy wait */+ }+ return (0);+}+++static int amd_write(struct flash_device_info *fdi,+ const char *buf, size_t count, off_t offset, int userspace_ptr)+{+ struct amd_flash *flash = fdi->handle;+ struct timeval time1, time2;+ vu_char *ptr;+ int ret;+ off_t bypass_offset;+ u_long end_offset;+ u_int data;+ int loops;++ ret = 0;+ ptr = flash->ptr;++/***+ DEBUG("amd_write buf=0x%x, count=%d=0x%x, offset=0x%x, us_ptr=%d\n",+ (int)buf, (int)count, (int)count, (int)offset, (int) userspace_ptr);+***/++ down(&flash->mutex);++ if (program_in_progress(flash)) {+ printk("Error, embedded program in progress during flash write\n");+ ret = -EIO;+ goto ERROR;+ }++ end_offset = offset + count;++ if (userspace_ptr && (!access_ok(VERIFY_READ, buf, count)) ) {+ ret = -EFAULT;+ goto ERROR;+ }++ bypass_offset = offset;+ unlock_bypass(ptr, bypass_offset);++ do_gettimeofday(&time1);++ loops = 0;+ while (offset<end_offset) {+ if ((offset%4 == 0) && (end_offset-offset >= 4)) {+ if (userspace_ptr) {+ if (__get_user(data, (int *)buf)) {+ ret = -EFAULT;+ goto UNLOCK_BYPASS_RESET_ERROR;+ }+ } else {+ data = *(int *)buf;+ }++ if (do_write_int(ptr, offset, data)) {+ ret = -EFAULT;+ goto UNLOCK_BYPASS_RESET_ERROR;+ }+ buf += 4;+ offset += 4;+ } else {+ if (userspace_ptr) {+ if (__get_user(data, (char *)buf)) {+ ret = -EFAULT;+ goto UNLOCK_BYPASS_RESET_ERROR;+ }+ } else {+ data = *(char *)buf;+ }+ if (do_write_char(ptr, offset, data)) {+ ret = -EFAULT;+ goto UNLOCK_BYPASS_RESET_ERROR;+ }+ buf += 1;+ offset += 1;+ }++ /* every 32 writes we check if >1msec has passed. then we schedule */+ if ((++loops & 0x1f) == 0) {+ do_gettimeofday(&time2);+ time2.tv_sec -= time1.tv_sec;+ time2.tv_usec -= time1.tv_usec;++ if ((time2.tv_sec>0) || (time2.tv_usec>1000)) {+ /* May wait to short if tv_sec wraps. Who cares... */+ current->state = TASK_RUNNING;+ schedule();+ if (userspace_ptr && (!access_ok(VERIFY_READ, buf, count))) {+ ret = -EFAULT;+ goto UNLOCK_BYPASS_RESET_ERROR;+ }+ do_gettimeofday(&time1);+ }+ }+ }++UNLOCK_BYPASS_RESET_ERROR:+ unlock_bypass_reset(ptr, bypass_offset);+ERROR:+ up(&flash->mutex);++ return ret;+}++static int amd_erase_sector(struct flash_device_info *fdi,+ off_t offset, size_t count)+{+ struct amd_flash *flash = fdi->handle;+ flash_region_info_t *region = NULL;+ u_long region_size, region_end;+ u_long end;+ int ret;+ int i;++ ret = 0;++ end = offset + count;++ DEBUG("%s[%d]: amd_erase count=%d, offset=0x%x, end=0x%x\n",__FILE__,__LINE__,+ (int)count, (int)offset,(int)end);++ down(&flash->mutex);++ if (!flash_on_sector_boundaries(fdi, offset, count)) {+ DEBUG("Trying to erase, but not at sector boundaries. offs=%x, count=%x\n",+ (int)offset, (int)count);+ ret = -EINVAL;+ goto error;+ }++ if (program_in_progress(flash)) {+ printk("Error, embedded program in progress during flash erase\n");+ ret = -EIO;+ goto error;+ }++ /* erase all sectors in mem region */+ /* Now guaranteed to be on sector boundaries */+ for (i = 0; i<fdi->num_regions; i++) {+ region = &fdi->region[i];++ region_size = region->sector_size * region->num_sectors;+ region_end = region->offset + region_size;++ if (region->offset >= end) {+ break;+ }++ if ( (offset >= region->offset) && (offset < region_end) ) {+ u_long erase_end = (end > region_end) ? region_end : end;++ while (offset < erase_end) {+ do_erase_sector(flash->ptr, offset);+ offset += region->sector_size;+ }+ }+ }++++ error:++ up(&flash->mutex);++ return ret;+}++static int amd_protect_sector(struct flash_device_info *fdi,+ off_t offset, size_t count, int protected)+{+ return -ENOSYS;+}++static int amd_get_info(struct flash_device_info *fdi,+ flash_info_t *flash_info)+{+ struct amd_flash *flash = fdi->handle;+ flash_info_t t;+ int ret;++ t.size = flash->size;+ t.bitwidth = 8<<individual_bit_width(flash);+ t.chip_config = 1<<flash->chip_cfg;+ t.num_regions = fdi->num_regions;+ t.num_regions = fdi->num_regions;++ ret = copy_to_user(flash_info, &t, sizeof(flash_info_t));+ if (ret)+ return -EFAULT;++ return 0;+}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -