📄 yynf_nand.c
字号:
/* Grab the lock and see if the device is available */ nand_get_chip (this, mtd, FL_WRITING, NULL); /* Select the NAND device */ nand_select (); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); if (!(readb (this->IO_ADDR_R) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n"); ret = -EIO; goto out; } /* Loop until all iovecs' data has been written */ cnt = col; len = 0; while (count) { /* * Check, if we write from offset 0 and if the tuple * gives us not enough data for a full page write. Then we * can use the iov direct, else we have to copy into * data_buf. */ if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) { cnt = mtd->oobblock; this->data_poi = (u_char *) vecs->iov_base; this->data_poi += len; len += mtd->oobblock; /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; } } else { /* * Read data out of each tuple until we have a full page * to write or we've read all the tuples. */ while ((cnt < mtd->oobblock) && count) { if (vecs->iov_base != NULL && vecs->iov_len) { this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; } /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; } } this->data_poi = this->data_buf; } /* We use the same function for write and writev !) */ ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel); if (ret) goto out; /* Update written bytes count */ written += (cnt - col); /* Reset written byte counter and column */ col = cnt = 0; /* Increment page address */ page++; }out: /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); *retlen = written; return ret;}/* * NAND erase a block */static int nand_erase (struct mtd_info *mtd, struct erase_info *instr){ int page, len, status, pages_per_block, ret; struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE (wait, current); DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); /* Start address must align on block boundary */ if (instr->addr & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_chip (this, mtd, FL_ERASING, NULL); /* Shift to get first page */ page = (int) (instr->addr >> this->page_shift); /* Calculate pages in each block */ pages_per_block = mtd->erasesize / mtd->oobblock; /* Select the NAND device */ nand_select (); /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); if (!(readb (this->IO_ADDR_R) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* Loop through the pages */ len = instr->len; instr->state = MTD_ERASING; while (len) { /* Check if we have a bad block, we do not erase bad blocks ! */ this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); if (readb (this->IO_ADDR_R) != 0xff) { printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* Send commands to erase a page */ this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); spin_unlock_bh (&this->chip_lock); status = this->waitfunc (mtd, this, FL_ERASING); /* Get spinlock, in case we exit */ spin_lock_bh (&this->chip_lock); /* See if block erase succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } /* Check, if we were interupted */ if (this->state == FL_ERASING) { /* Increment page address and decrement length */ len -= mtd->erasesize; page += pages_per_block; } /* Release the spin lock */ spin_unlock_bh (&this->chip_lock);erase_retry: spin_lock_bh (&this->chip_lock); /* Check the state and sleep if it changed */ if (this->state == FL_ERASING || this->state == FL_READY) { /* Select the NAND device again, if we were interrupted */ this->state = FL_ERASING; nand_select (); continue; } else { set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule (); remove_wait_queue (&this->wq, &wait); goto erase_retry; } } instr->state = MTD_ERASE_DONE;erase_exit: /* De-select the NAND device */ nand_deselect (); spin_unlock_bh (&this->chip_lock); ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;; /* Do call back function */ if (!ret && instr->callback) instr->callback (instr); /* The device is ready */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; spin_unlock_bh (&this->chip_lock); /* Return more or less happy */ return ret;}/* * NAND sync */static void nand_sync (struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE (wait, current); DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");retry: /* Grab the spinlock */ spin_lock_bh (&this->chip_lock); /* See what's going on */ switch (this->state) { case FL_READY: case FL_SYNCING: this->state = FL_SYNCING; spin_unlock_bh (&this->chip_lock); break; default: /* Not an idle state */ add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule (); remove_wait_queue (&this->wq, &wait); goto retry; } /* Lock the device */ spin_lock_bh (&this->chip_lock); /* Set the device to be ready again */ if (this->state == FL_SYNCING) { this->state = FL_READY; wake_up (&this->wq); } /* Unlock the device */ spin_unlock_bh (&this->chip_lock);}/* * Scan for the NAND device */static int yuyin_nand_scan (struct mtd_info *mtd){ int i, nand_maf_id, nand_dev_id; struct nand_chip *this = mtd->priv; /* check for proper chip_delay setup, set 20us if not */ if (!this->chip_delay) this->chip_delay = 20; /* check, if a user supplied command function given */ if (this->cmdfunc == NULL) this->cmdfunc = nand_command; /* check, if a user supplied wait function given */ if (this->waitfunc == NULL) this->waitfunc = nand_wait; /* Select the device */ nand_select (); /* Send the command for reading device ID */ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ nand_maf_id = readb (this->IO_ADDR_R); nand_dev_id = readb (this->IO_ADDR_R); /* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) { mtd->name = nand_flash_ids[i].name; mtd->erasesize = nand_flash_ids[i].erasesize; mtd->size = (1 << nand_flash_ids[i].chipshift); mtd->eccsize = 256; if (nand_flash_ids[i].page256) { mtd->oobblock = 256; mtd->oobsize = 8; this->page_shift = 8; } else { mtd->oobblock = 512; mtd->oobsize = 16; this->page_shift = 9; } /* Try to identify manufacturer */ for (i = 0; nand_manuf_ids[i].id != 0x0; i++) { if (nand_manuf_ids[i].id == nand_maf_id) break; } printk (KERN_INFO "NAND device: Manufacture ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, nand_manuf_ids[i].name , mtd->name); break; } } /* * check ECC mode, default to software * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ switch (this->eccmode) { case NAND_ECC_HW3_512: if (mtd->oobblock == 256) { printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; } else this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ case NAND_ECC_HW3_256: if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break; printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); BUG(); case NAND_ECC_NONE: this->eccmode = NAND_ECC_NONE; break; case NAND_ECC_SOFT: this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; break; default: printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); BUG(); } /* Initialize state, waitqueue and spinlock */ this->state = FL_READY; init_waitqueue_head (&this->wq); spin_lock_init (&this->chip_lock); /* De-select the device */ nand_deselect (); /* Print warning message for no device */ if (!mtd->size) { printk (KERN_WARNING "No NAND device found!!!\n"); return 1; } /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; mtd->module = THIS_MODULE; mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; mtd->read_ecc = nand_read_ecc; mtd->write_ecc = nand_write_ecc; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->readv = NULL; mtd->writev = nand_writev; mtd->writev_ecc = nand_writev_ecc; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; mtd->suspend = NULL; mtd->resume = NULL; /* Return happy */ return 0;}/* * MTD structure for YUYINBOARD NAND FLASH 1 */static struct mtd_info *yuyinnf1_mtd = NULL;/* * Define partitions for flash device */const static struct mtd_partition partition_info[] = { { name: "YuyinP1", offset: 0, size: 64*1024*1024 }};#define NUM_PARTITIONS 1/* * hardware specific access to control-lines*/void yynf1_hwcontrol(int cmd){ switch(cmd){ case NAND_CTL_SETCLE: (*(volatile unsigned short *) (0xfa000032)) = 1; break; case NAND_CTL_CLRCLE: (*(volatile unsigned short *) (0xfa000032)) = 0; break; case NAND_CTL_SETALE: (*(volatile unsigned short *) (0xfa000030)) = 1; break; case NAND_CTL_CLRALE: (*(volatile unsigned short *) (0xfa000030)) = 0; break; case NAND_CTL_SETNCE: (*(volatile unsigned short *) (0xfa000034)) = 1; break; case NAND_CTL_CLRNCE: (*(volatile unsigned short *) (0xfa000034)) = 0; break; }}/* * Main initialization routine */int __init yynf1_init (void){ struct nand_chip *this; /* Allocate memory for MTD device structure and private data */ yuyinnf1_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); if (!yuyinnf1_mtd) { printk ("Unable to allocate NAND MTD device structure.\n"); return -ENOMEM; } /* Get pointer to private data */ this = (struct nand_chip *) (&yuyinnf1_mtd[1]); /* Initialize structures */ memset((char *) yuyinnf1_mtd, 0, sizeof(struct mtd_info)); memset((char *) this, 0, sizeof(struct nand_chip)); /* Link the private data with the MTD structure */ yuyinnf1_mtd->priv = this; /* Set address of NAND IO lines */ this->IO_ADDR_R = 0xfa000036; this->IO_ADDR_W = 0xfa000036; /* Set address of hardware control function */ this->hwcontrol = yynf1_hwcontrol; /* 15 us command delay time */ this->chip_delay = 15; /* Scan to find existence of the device */ if (yuyin_nand_scan (yuyinnf1_mtd)) { kfree (yuyinnf1_mtd); return -ENXIO; } /* Allocate memory for internal data buffer */ this->data_buf = kmalloc (sizeof(u_char) * (yuyinnf1_mtd->oobblock + yuyinnf1_mtd->oobsize), GFP_KERNEL); if (!this->data_buf) { printk ("Unable to allocate NAND data buffer.\n"); kfree (yuyinnf1_mtd); return -ENOMEM; } /* Allocate memory for internal data buffer */ this->data_cache = kmalloc (sizeof(u_char) * (yuyinnf1_mtd->oobblock + yuyinnf1_mtd->oobsize), GFP_KERNEL); if (!this->data_cache) { printk ("Unable to allocate NAND data cache for.\n"); kfree (this->data_buf); kfree (yuyinnf1_mtd); return -ENOMEM; } this->cache_page = -1; /* Register the partitions */ add_mtd_partitions(yuyinnf1_mtd, partition_info, NUM_PARTITIONS); /* Return happy */ return 0;}module_init(yynf1_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -