📄 amd_flash.c
字号:
chip->state = FL_READY; wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return ret;}static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf){ struct map_info *map = mtd->priv; struct amd_flash_private *private = map->fldrv_priv; int ret = 0; int chipnum; unsigned long ofs; unsigned long chipstart; *retlen = 0; if (!len) { return 0; } chipnum = to >> private->chipshift; ofs = to - (chipnum << private->chipshift); chipstart = private->chips[chipnum].start; /* If it's not bus-aligned, do the first byte write. */ if (ofs & (map->buswidth - 1)) { unsigned long bus_ofs = ofs & ~(map->buswidth - 1); int i = ofs - bus_ofs; int n = 0; u_char tmp_buf[4]; __u32 datum; map_copy_from(map, tmp_buf, bus_ofs + private->chips[chipnum].start, map->buswidth); while (len && i < map->buswidth) tmp_buf[i++] = buf[n++], len--; if (map->buswidth == 2) { datum = *(__u16*)tmp_buf; } else if (map->buswidth == 4) { datum = *(__u32*)tmp_buf; } else { return -EINVAL; /* should never happen, but be safe */ } ret = write_one_word(map, &private->chips[chipnum], bus_ofs, datum); if (ret) { return ret; } ofs += n; buf += n; (*retlen) += n; if (ofs >> private->chipshift) { chipnum++; ofs = 0; if (chipnum == private->numchips) { return 0; } } } /* We are now aligned, write as much as possible. */ while(len >= map->buswidth) { __u32 datum; if (map->buswidth == 1) { datum = *(__u8*)buf; } else if (map->buswidth == 2) { datum = *(__u16*)buf; } else if (map->buswidth == 4) { datum = *(__u32*)buf; } else { return -EINVAL; } ret = write_one_word(map, &private->chips[chipnum], ofs, datum); if (ret) { return ret; } ofs += map->buswidth; buf += map->buswidth; (*retlen) += map->buswidth; len -= map->buswidth; if (ofs >> private->chipshift) { chipnum++; ofs = 0; if (chipnum == private->numchips) { return 0; } chipstart = private->chips[chipnum].start; } } if (len & (map->buswidth - 1)) { int i = 0, n = 0; u_char tmp_buf[2]; __u32 datum; map_copy_from(map, tmp_buf, ofs + private->chips[chipnum].start, map->buswidth); while (len--) { tmp_buf[i++] = buf[n++]; } if (map->buswidth == 2) { datum = *(__u16*)tmp_buf; } else if (map->buswidth == 4) { datum = *(__u32*)tmp_buf; } else { return -EINVAL; /* should never happen, but be safe */ } ret = write_one_word(map, &private->chips[chipnum], ofs, datum); if (ret) { return ret; } (*retlen) += n; } return 0;}static inline int erase_one_block(struct map_info *map, struct flchip *chip, unsigned long adr, u_long size){ unsigned long timeo = jiffies + HZ; struct amd_flash_private *private = map->fldrv_priv; DECLARE_WAITQUEUE(wait, current);retry: spin_lock_bh(chip->mutex); if (chip->state != FL_READY){ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); if (signal_pending(current)) { return -EINTR; } timeo = jiffies + HZ; goto retry; } chip->state = FL_ERASING; adr += chip->start; ENABLE_VPP(map); send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA); send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr); timeo = jiffies + (HZ * 20); spin_unlock_bh(chip->mutex); msleep(1000); spin_lock_bh(chip->mutex); while (flash_is_busy(map, adr, private->interleave)) { if (chip->state != FL_ERASING) { /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock_bh(chip->mutex); printk(KERN_INFO "%s: erase suspended. Sleeping\n", map->name); schedule(); remove_wait_queue(&chip->wq, &wait); if (signal_pending(current)) { return -EINTR; } timeo = jiffies + (HZ*2); /* FIXME */ spin_lock_bh(chip->mutex); continue; } /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_READY; spin_unlock_bh(chip->mutex); printk(KERN_WARNING "%s: waiting for erase to complete " "timed out.\n", map->name); DISABLE_VPP(map); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); if (need_resched()) schedule(); else udelay(1); spin_lock_bh(chip->mutex); } /* Verify every single word */ { int address; int error = 0; __u8 verify; for (address = adr; address < (adr + size); address++) { if ((verify = map_read8(map, address)) != 0xFF) { error = 1; break; } } if (error) { chip->state = FL_READY; spin_unlock_bh(chip->mutex); printk(KERN_WARNING "%s: verify error at 0x%x, size %ld.\n", map->name, address, size); DISABLE_VPP(map); return -EIO; } } DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return 0;}static int amd_flash_erase(struct mtd_info *mtd, struct erase_info *instr){ struct map_info *map = mtd->priv; struct amd_flash_private *private = map->fldrv_priv; unsigned long adr, len; int chipnum; int ret = 0; int i; int first; struct mtd_erase_region_info *regions = mtd->eraseregions; if (instr->addr > mtd->size) { return -EINVAL; } if ((instr->len + instr->addr) > mtd->size) { return -EINVAL; } /* Check that both start and end of the requested erase are * aligned with the erasesize at the appropriate addresses. */ i = 0; /* Skip all erase regions which are ended before the start of the requested erase. Actually, to save on the calculations, we skip to the first erase region which starts after the start of the requested erase, and then go back one. */ while ((i < mtd->numeraseregions) && (instr->addr >= regions[i].offset)) { i++; } i--; /* OK, now i is pointing at the erase region in which this * erase request starts. Check the start of the requested * erase range is aligned with the erase size which is in * effect here. */ if (instr->addr & (regions[i].erasesize-1)) { return -EINVAL; } /* Remember the erase region we start on. */ first = i; /* Next, check that the end of the requested erase is aligned * with the erase region at that address. */ while ((i < mtd->numeraseregions) && ((instr->addr + instr->len) >= regions[i].offset)) { i++; } /* As before, drop back one to point at the region in which * the address actually falls. */ i--; if ((instr->addr + instr->len) & (regions[i].erasesize-1)) { return -EINVAL; } chipnum = instr->addr >> private->chipshift; adr = instr->addr - (chipnum << private->chipshift); len = instr->len; i = first; while (len) { ret = erase_one_block(map, &private->chips[chipnum], adr, regions[i].erasesize); if (ret) { return ret; } adr += regions[i].erasesize; len -= regions[i].erasesize; if ((adr % (1 << private->chipshift)) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) % (1 << private->chipshift))) { i++; } if (adr >> private->chipshift) { adr = 0; chipnum++; if (chipnum >= private->numchips) { break; } } } instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return 0;}static void amd_flash_sync(struct mtd_info *mtd){ struct map_info *map = mtd->priv; struct amd_flash_private *private = map->fldrv_priv; int i; struct flchip *chip; int ret = 0; DECLARE_WAITQUEUE(wait, current); for (i = 0; !ret && (i < private->numchips); i++) { chip = &private->chips[i]; retry: spin_lock_bh(chip->mutex); switch(chip->state) { case FL_READY: case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY: chip->oldstate = chip->state; chip->state = FL_SYNCING; /* No need to wake_up() on this state change - * as the whole point is that nobody can do anything * with the chip now anyway. */ case FL_SYNCING: spin_unlock_bh(chip->mutex); break; default: /* Not an idle state */ add_wait_queue(&chip->wq, &wait); spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); goto retry; } } /* Unlock the chips again */ for (i--; i >= 0; i--) { chip = &private->chips[i]; spin_lock_bh(chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } spin_unlock_bh(chip->mutex); }}static int amd_flash_suspend(struct mtd_info *mtd){printk("amd_flash_suspend(): not implemented!\n"); return -EINVAL;}static void amd_flash_resume(struct mtd_info *mtd){printk("amd_flash_resume(): not implemented!\n");}static void amd_flash_destroy(struct mtd_info *mtd){ struct map_info *map = mtd->priv; struct amd_flash_private *private = map->fldrv_priv; kfree(private);}int __init amd_flash_init(void){ register_mtd_chip_driver(&amd_flash_chipdrv); return 0;}void __exit amd_flash_exit(void){ unregister_mtd_chip_driver(&amd_flash_chipdrv);}module_init(amd_flash_init);module_exit(amd_flash_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Jonas Holmberg <jonas.holmberg@axis.com>");MODULE_DESCRIPTION("Old MTD chip driver for AMD flash chips");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -