smc.c
来自「移植到2410开发板上的源代码」· C语言 代码 · 共 1,405 行 · 第 1/3 页
C
1,405 行
*retlen = len; ret = 0; nand_write_err: /* 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); return ret;}/* * NAND write out-of-band */static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ int i, offset, page, status, ret; struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current); DEBUG (MTD_DEBUG_LEVEL3, __FUNCTION__ ": to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Shift to get page */ page = ((int) to) >> this->page_shift; /* Mask to get column */ offset = to & 0x1f; /* Initialize return length value */ *retlen = 0; /* Do not allow write past end of page */ if ((offset + len) > mtd->oobsize) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__": Attempt to write past end of page\n"); return -EINVAL; }retry: /* Grab the lock and see if the device is available */ spin_lock_bh (&this->chip_lock); switch (this->state) { case FL_READY: this->state = FL_WRITING; spin_unlock_bh (&this->chip_lock); break; default: 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 retry; }; /* Select the NAND device */ nand_select (); /* Check the WP bit */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); this->wait_for_ready(); if (!(this->read_data () & SMC_STAT_NOT_WP)) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__ ": Device is write protected!!!\n"); ret = -EPERM; goto nand_write_oob_err; } /* Write out desired data */ nand_command (mtd, NAND_CMD_SEQIN, offset + mtd->oobblock, page); this->hwcontrol(NAND_CTL_DAT_OUT); for (i=0 ; i<len ; i++) this->write_data (buf[i]); this->hwcontrol(NAND_CTL_DAT_IN); /* Send command to program the OOB data */ nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); this->wait_for_ready(); /* * Wait for program operation to complete. This could * take up to 3000us (3ms) on some devices, so we try * and exit as quickly as possible. */ status = 0; for (i=0 ; i<24 ; i++) { /* Delay for 125us */ udelay (125); /* Check the status */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); this->wait_for_ready(); status = (int) this->read_data (); if (status & SMC_STAT_READY) break; } /* See if device thinks it succeeded */ if (status & SMC_STAT_WRITE_ERR) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__ ": Failed write, page 0x%08x\n", page); ret = -EIO; goto nand_write_oob_err; }#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ nand_command (mtd, NAND_CMD_READOOB, offset, page); this->wait_for_ready(); /* Loop through and verify the data */ for (i=0 ; i<len ; i++) { if (buf[i] != this->read_data ()) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__ ": Failed write verify, page 0x%08x\n", page); ret = -EIO; goto nand_write_oob_err; } }#endif /* Return happy */ *retlen = len; ret = 0; nand_write_oob_err: /* 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); return ret;}/* * NAND erase a block */static int nand_erase (struct mtd_info *mtd, struct erase_info *instr){ int i, page, len, status, pages_per_block; struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current); DEBUG (MTD_DEBUG_LEVEL3, __FUNCTION__ ": 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, __FUNCTION__ ": Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & (mtd->erasesize - 1)) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__ ": 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, __FUNCTION__ ": Erase past end of device\n"); return -EINVAL; }retry: /* Grab the lock and see if the device is available */ spin_lock_bh (&this->chip_lock); switch (this->state) { case FL_READY: this->state = FL_ERASING; break; default: 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 retry; }; /* 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 */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); this->wait_for_ready(); if (!(this->read_data () & SMC_STAT_NOT_WP)) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__ ": Device is write protected!!!\n"); nand_deselect (); this->state = FL_READY; spin_unlock_bh (&this->chip_lock); return -EIO; } /* Loop through the pages */ len = instr->len; while (len) { /* Send commands to erase a page */ nand_command(mtd, NAND_CMD_ERASE1, -1, page); nand_command(mtd, NAND_CMD_ERASE2, -1, -1); this->wait_for_ready(); /* * Wait for program operation to complete. This could * take up to 4000us (4ms) on some devices, so we try * and exit as quickly as possible. */ status = 0; for (i=0 ; i<32 ; i++) { /* Delay for 125us */ udelay (125); /* Check the status */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); this->wait_for_ready(); status = (int) this->read_data (); if (status & SMC_STAT_READY) break; } /* See if block erase succeeded */ if (status & SMC_STAT_WRITE_ERR) { DEBUG (MTD_DEBUG_LEVEL0, __FUNCTION__ \ ": Failed erase, page 0x%08x\n", page); nand_deselect (); instr->state = MTD_ERASE_FAILED; if (instr->callback) instr->callback (instr); this->state = FL_READY; spin_unlock_bh (&this->chip_lock); return -EIO; } /* 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: /* Check the state and sleep if it changed */ spin_lock_bh (&this->chip_lock); if (this->state == FL_ERASING) { 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; } } spin_unlock_bh (&this->chip_lock); /* De-select the NAND device */ nand_deselect (); /* Do call back function */ instr->state = MTD_ERASE_DONE; if (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 happy */ return 0;}/* * NAND sync */static void nand_sync (struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current); DEBUG (MTD_DEBUG_LEVEL3, __FUNCTION__ ": 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 SMC device */int smc_scan (struct mtd_info *mtd){ int i, nand_maf_id, nand_dev_id; struct nand_chip *this = mtd->priv; /* Select the device */ nand_select (); /* Send the command for reading device ID */ nand_command (mtd, NAND_CMD_READID, 0x00, -1); this->wait_for_ready(); /* Read manufacturer and device IDs */ nand_maf_id = this->read_data (); nand_dev_id = this->read_data (); /* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (nand_maf_id == nand_flash_ids[i].manufacture_id && nand_dev_id == nand_flash_ids[i].model_id) {#ifdef USE_256BYTE_NAND_FLASH if (!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; } this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)]; }#else if (!(mtd->size) && !(nand_flash_ids[i].page256)) { 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; mtd->oobblock = 512; mtd->oobsize = 16; this->page_shift = 9; this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)]; }#endif printk (KERN_INFO "NAND device: Manufacture ID:" \ " 0x%02x, Chip ID: 0x%02x (%s)\n", nand_maf_id, nand_dev_id, mtd->name); break; } } /* Initialize state and spinlock */ this->state = FL_READY; spin_lock_init(&this->chip_lock); init_waitqueue_head(&(this->wq)); /* 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 = NULL; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; mtd->suspend = NULL; mtd->resume = NULL; /* Return happy */ return 0;}EXPORT_SYMBOL(smc_scan);MODULE_LICENSE("GPL");MODULE_AUTHOR("Yong-iL Joh <tolkien@mizi.com>");MODULE_DESCRIPTION("S3C2410 NAND Flash");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?