📄 cfi_cmdset_0001.c
字号:
/* * Common Flash Interface support: * Intel Extended Vendor Command Set (ID 0x0001) * * (C) 2000 Red Hat. GPL'd * * $Id: cfi_cmdset_0001.c,v 1.21 2000/07/13 10:36:14 dwmw2 Exp $ */#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <asm/io.h>#include <asm/byteorder.h>#include <linux/errno.h>#include <linux/malloc.h>#include <linux/delay.h>#include <linux/mtd/map.h>#include <linux/mtd/cfi.h>#if LINUX_VERSION_CODE < 0x20300#define set_current_state(x) current->state = (x);#endifstatic int cfi_intelext_read_1_by_16 (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static int cfi_intelext_write_1_by_16(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);static int cfi_intelext_erase_1_by_16 (struct mtd_info *, struct erase_info *);static void cfi_intelext_sync (struct mtd_info *);static int cfi_intelext_suspend (struct mtd_info *);static void cfi_intelext_resume (struct mtd_info *);static void cfi_intelext_destroy(struct mtd_info *);static void cfi_cmdset_0001(struct map_info *, int, unsigned long);static struct mtd_info *cfi_intelext_setup (struct map_info *);static const char im_name[] = "cfi_cmdset_0001";/* This routine is made available to other mtd code via * inter_module_register. It must only be accessed through * inter_module_get which will bump the use count of this module. The * addresses passed back in cfi are valid as long as the use count of * this module is non-zero, i.e. between inter_module_get and * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. */static void cfi_cmdset_0001(struct map_info *map, int primary, unsigned long base){ struct cfi_private *cfi = map->fldrv_priv; int i; struct cfi_pri_intelext *extp; __u16 adr = primary?cfi->cfiq.P_ADR:cfi->cfiq.A_ADR; printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); if (!adr) return; /* Switch it into Query Mode */ switch(map->buswidth) { case 1: map->write8(map, 0x98, 0x55); break; case 2: map->write16(map, 0x9898, 0xaa); break; case 4: map->write32(map, 0x98989898, 0x154); break; } extp = kmalloc(sizeof(*extp), GFP_KERNEL); if (!extp) { printk("Failed to allocate memory\n"); return; } /* Read in the Extended Query Table */ for (i=0; i<sizeof(*extp); i++) { ((unsigned char *)extp)[i] = map->read8(map, (base+((adr+i)*map->buswidth))); } if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { printk(" Unknown IntelExt Extended Query version %c.%c.\n", extp->MajorVersion, extp->MinorVersion); kfree(extp); return; } /* Do some byteswapping if necessary */ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); extp->BlkStatusRegMask = le32_to_cpu(extp->BlkStatusRegMask); /* Tell the user about it in lots of lovely detail */#if 0 printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); for (i=9; i<32; i++) { if (extp->FeatureSupport & (1<<i)) printk(" - Unknown Bit %X: supported\n", i); } printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); for (i=1; i<8; i++) { if (extp->SuspendCmdSupport & (1<<i)) printk(" - Unknown Bit %X: supported\n", i); } printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); for (i=2; i<16; i++) { if (extp->BlkStatusRegMask & (1<<i)) printk(" - Unknown Bit %X Active: yes\n",i); } printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", extp->VccOptimal >> 8, extp->VccOptimal & 0xf); if (extp->VppOptimal) printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", extp->VppOptimal >> 8, extp->VppOptimal & 0xf);#endif /* OK. We like it. Take over the control of it. */ /* Switch it into Read Mode */ switch(map->buswidth) { case 1: map->write8(map, 0xff, 0x55); break; case 2: map->write16(map, 0xffff, 0xaa); break; case 4: map->write32(map, 0xffffffff, 0x154); break; } /* If there was an old setup function, decrease its use count */ if (cfi->cmdset_setup) inter_module_put(cfi->im_name); if (cfi->cmdset_priv) kfree(cfi->cmdset_priv); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 128; cfi->chips[i].buffer_write_time = 128; cfi->chips[i].erase_time = 1024; } cfi->cmdset_setup = cfi_intelext_setup; cfi->im_name = im_name; cfi->cmdset_priv = extp; return;}static struct mtd_info *cfi_intelext_setup(struct map_info *map){ struct cfi_private *cfi = map->fldrv_priv; struct mtd_info *mtd; mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); printk("number of CFI chips: %d\n", cfi->numchips); if (!mtd) { printk("Failed to allocate memory for MTD device\n"); kfree(cfi->cmdset_priv); return NULL; } memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; mtd->type = MTD_NORFLASH; mtd->erasesize = 0x20000; /* FIXME */ /* Also select the correct geometry setup too */ mtd->size = (1 << cfi->cfiq.DevSize) * cfi->numchips; mtd->erase = cfi_intelext_erase_1_by_16; mtd->read = cfi_intelext_read_1_by_16; mtd->write = cfi_intelext_write_1_by_16; mtd->sync = cfi_intelext_sync; mtd->suspend = cfi_intelext_suspend; mtd->resume = cfi_intelext_resume; mtd->flags = MTD_CAP_NORFLASH; map->fldrv_destroy = cfi_intelext_destroy; mtd->name = map->name; return mtd;}static inline int do_read_1_by_16_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf){ __u16 status; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); adr += chip->start; retry: spin_lock_bh(chip->mutex); /* Check that the chip's ready to talk to us. * Later, we can actually think about interrupting it * if it's in FL_ERASING or FL_WRITING state. * Not just yet, though. */ switch (chip->state) {#if 0 case FL_ERASING: case FL_WRITING: /* Suspend the operation, set state to FL_xxx_SUSPENDED */#endif case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: map->write16(map, cpu_to_le16(0x0070), adr); chip->state = FL_STATUS; case FL_STATUS: status = le16_to_cpu(map->read16(map, adr)); if (!(status & (1<<7))) { static int z=0; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); printk("waiting for chip to be ready timed out in read"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); z++; if ( 0 && !(z % 100 )) printk("chip not ready yet before read. looping\n"); udelay(1); goto retry; } break; default: printk("Waiting for chip, status = %d\n", chip->state); /* Stick ourselves on a wait queue to be woken when someone changes the status */ set_current_state(TASK_INTERRUPTIBLE); 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; } map->write16(map, cpu_to_le16(0x00ff), adr); chip->state = FL_READY; map->copy_from(map, buf, adr, len); if (chip->state == FL_ERASE_SUSPENDED || chip->state == FL_WRITE_SUSPENDED) { printk("Who in hell suspended the pending operation? I didn't write that code yet!\n"); /* Restart it and set the state accordingly */ } wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return 0;}static int cfi_intelext_read_1_by_16 (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs; int chipnum; int ret = 0; /* ofs: offset within the first chip that the first read should start */ chipnum = (from >> cfi->chipshift); ofs = from - (chipnum << cfi->chipshift); *retlen = 0; while (len) { unsigned long thislen; if (chipnum >= cfi->numchips) break; if ((len + ofs -1) >> cfi->chipshift) thislen = (1<<cfi->chipshift) - ofs; else thislen = len; ret = do_read_1_by_16_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); if (ret) break; *retlen += thislen; len -= thislen; buf += thislen; ofs = 0; chipnum++; } return ret;}static inline int do_write_1_by_16_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u16 datum){ __u16 status; unsigned long timeo = jiffies + HZ; DECLARE_WAITQUEUE(wait, current); int z = 0; adr += chip->start; retry: spin_lock_bh(chip->mutex); /* Check that the chip's ready to talk to us. * Later, we can actually think about interrupting it * if it's in FL_ERASING state. * Not just yet, though. */ switch (chip->state) { case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: map->write16(map, cpu_to_le16(0x0070), adr); chip->state = FL_STATUS; timeo = jiffies + HZ; case FL_STATUS: status = le16_to_cpu(map->read16(map, adr)); if (!(status & (1<<7))) { /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); printk("waiting for chip to be ready timed out in read"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); z++; if ( 0 && !(z % 100 )) printk("chip not ready yet before write. looping\n"); udelay(1); goto retry; } break; default: printk("Waiting for chip, status = %d\n", chip->state); /* Stick ourselves on a wait queue to be woken when someone changes the status */ set_current_state(TASK_INTERRUPTIBLE); 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; } map->write16(map, cpu_to_le16(0x0040), adr); map->write16(map, datum, adr); chip->state = FL_WRITING; timeo = jiffies + (HZ/2); spin_unlock_bh(chip->mutex); udelay(chip->word_write_time); spin_lock_bh(chip->mutex); z = 0; while ( !( (status = le16_to_cpu(map->read16(map, adr))) & 0x80 ) ) { if (chip->state != FL_WRITING) { /* Someone's suspended the write. Sleep */ set_current_state(TASK_INTERRUPTIBLE); 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 / 2); /* FIXME */ spin_lock_bh(chip->mutex); continue; } /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; spin_unlock_bh(chip->mutex); printk("waiting for chip to be ready timed out in read"); return -EIO;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -