linux的NAND Flash驱动位于drivers/mtd/nand子文件夹下:

nand_base.c-->定义通用的nand flash基本操作函数,如读写page,可自己重写这些函数

nand_bbt.c-->与坏块管理有关的函数和结构体

nand_ids.c-->nand_flash_ids[](芯片ID)和nand_manuf_ids[](厂商ID)

nand_ecc.c-->软件ECC代码,若系统支持硬件ECC。则不用理会这个文件

pl353_nand.c-->pl353 nand flash控制器的驱动代码

#include <linux/err.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/memory/pl353-smc.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h> #define PL353_NAND_DRIVER_NAME "pl353-nand" /* NAND flash driver defines */
#define PL353_NAND_CMD_PHASE 1 /* End command valid in command phase */
#define PL353_NAND_DATA_PHASE 2 /* End command valid in data phase */
#define PL353_NAND_ECC_SIZE 512 /* Size of data for ECC operation */ /* Flash memory controller operating parameters */ #define PL353_NAND_ECC_CONFIG (BIT(4) | /* ECC read at end of page */ \
(0 << 5)) /* No Jumping */ /* AXI Address definitions */
#define START_CMD_SHIFT 3
#define END_CMD_SHIFT 11
#define END_CMD_VALID_SHIFT 20
#define ADDR_CYCLES_SHIFT 21
#define CLEAR_CS_SHIFT 21
#define ECC_LAST_SHIFT 10
#define COMMAND_PHASE (0 << 19)
#define DATA_PHASE BIT(19) #define PL353_NAND_ECC_LAST BIT(ECC_LAST_SHIFT) /* Set ECC_Last */
#define PL353_NAND_CLEAR_CS BIT(CLEAR_CS_SHIFT) /* Clear chip select */ #define ONDIE_ECC_FEATURE_ADDR 0x90
#define PL353_NAND_ECC_BUSY_TIMEOUT (1 * HZ)
#define PL353_NAND_DEV_BUSY_TIMEOUT (1 * HZ)
#define PL353_NAND_LAST_TRANSFER_LENGTH 4 /* Inline function for the NAND controller register write */
static inline void pl353_nand_write32(void __iomem *addr, u32 val)
{
writel_relaxed((val), (addr));
} /**
* struct pl353_nand_command_format - Defines NAND flash command format
* @start_cmd: First cycle command (Start command)
* @end_cmd: Second cycle command (Last command)
* @addr_cycles: Number of address cycles required to send the address
* @end_cmd_valid: The second cycle command is valid for cmd or data phase
*/
struct pl353_nand_command_format {
int start_cmd;
int end_cmd;
u8 addr_cycles;
u8 end_cmd_valid;
}; /**
* struct pl353_nand_info - Defines the NAND flash driver instance
* @chip: NAND chip information structure
* @mtd: MTD information structure
* @parts: Pointer to the mtd_partition structure
* @nand_base: Virtual address of the NAND flash device
* @end_cmd_pending: End command is pending
* @end_cmd: End command
*/
struct pl353_nand_info {
struct nand_chip chip;
struct mtd_info mtd;
struct mtd_partition *parts;
void __iomem *nand_base;
unsigned long end_cmd_pending;
unsigned long end_cmd;
}; /*
* The NAND flash operations command format
*/
static const struct pl353_nand_command_format pl353_nand_commands[] = {
{NAND_CMD_READ0, NAND_CMD_READSTART, 5, PL353_NAND_CMD_PHASE}, //read data
{NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, PL353_NAND_CMD_PHASE}, //random Data output
{NAND_CMD_READID, NAND_CMD_NONE, 1, NAND_CMD_NONE}, //read ID
{NAND_CMD_STATUS, NAND_CMD_NONE, 0, NAND_CMD_NONE}, //read status
{NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, PL353_NAND_DATA_PHASE}, //page program
{NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE}, //random data input
{NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, PL353_NAND_CMD_PHASE}, //erase block
{NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE}, //reset
{NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
{NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
/* Add all the flash commands supported by the flash device and Linux */
/*
* The cache program command is not supported by driver because driver
* cant differentiate between page program and cached page program from
* start command, these commands can be differentiated through end
* command, which doesn't fit in to the driver design. The cache program
* command is not supported by NAND subsystem also, look at 1612 line
* number (in nand_write_page function) of nand_base.c file.
* {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, PL353_NAND_YES},
*/
}; /* Define default oob placement schemes for large and small page devices */
//一般一页中每512字节会相应16字节的OOB空间
static struct nand_ecclayout nand_oob_16 = { //16表示OOB大小为16B,即页大小为512B时。使用这个ecclayout
.eccbytes = 3, //对于pl353每512B数据产生3B的ecc校验值
.eccpos = {0, 1, 2}, //ecc校验值在OOB中存放的位置
.oobfree = {
{.offset = 8, //空暇OOB的起始位置
. length = 8} }
}; static struct nand_ecclayout nand_oob_64 = { //页大小为2KB时。使用这个ecclayout
.eccbytes = 12, //3*4
.eccpos = {
52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63},
.oobfree = {
{.offset = 2,
.length = 50} }
};
//ondie类型的nand flash使用这个ecclayout,不太清楚ondie是什么意思?
static struct nand_ecclayout ondie_nand_oob_64 = {
.eccbytes = 32, .eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
24, 25, 26, 27, 28, 29, 30, 31,
40, 41, 42, 43, 44, 45, 46, 47,
56, 57, 58, 59, 60, 61, 62, 63
}, .oobfree = {
{ .offset = 4, .length = 4 },
{ .offset = 20, .length = 4 },
{ .offset = 36, .length = 4 },
{ .offset = 52, .length = 4 }
}
}; /* Generic flash bbt decriptors */
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; static struct nand_bbt_descr bbt_main_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 4,
.len = 4,
.veroffs = 20,
.maxblocks = 4,
.pattern = bbt_pattern
}; static struct nand_bbt_descr bbt_mirror_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 4,
.len = 4,
.veroffs = 20,
.maxblocks = 4,
.pattern = mirror_pattern
}; /**
* pl353_nand_calculate_hwecc - Calculate Hardware ECC
* @mtd: Pointer to the mtd_info structure
* @data: Pointer to the page data
* @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored
*
* This function retrieves the Hardware ECC data from the controller and returns
* ECC data back to the MTD subsystem.
*
* Return: 0 on success or error value on failure
*/
//从ecc寄存器中获取的ecc值会存放在ecc_code地址空间中
static int pl353_nand_calculate_hwecc(struct mtd_info *mtd,
const u8 *data, u8 *ecc_code)
{
u32 ecc_value, ecc_status;
u8 ecc_reg, ecc_byte;
unsigned long timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT; /* Wait till the ECC operation is complete or timeout */
do {
if (pl353_smc_ecc_is_busy())
cpu_relax();
else
break;
} while (!time_after_eq(jiffies, timeout)); if (time_after_eq(jiffies, timeout)) {
pr_err("%s timed out\n", __func__);
return -ETIMEDOUT;
} for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) {
/* Read ECC value for each block */
ecc_value = pl353_smc_get_ecc_val(ecc_reg);
ecc_status = (ecc_value >> 24) & 0xFF; //获取最高8bit的状态标记值
/* ECC value valid */
if (ecc_status & 0x40) { //推断这个ecc寄存器的值是否是有效的
for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) {
/* Copy ECC bytes to MTD buffer */
*ecc_code = ecc_value & 0xFF;
ecc_value = ecc_value >> 8;
ecc_code++;
}
} else {
pr_warn("%s status failed\n", __func__);
return -1;
}
}
return 0;
} /**
* onehot - onehot function
* @value: Value to check for onehot
*
* This function checks whether a value is onehot or not.
* onehot is if and only if onebit is set.
*
* Return: 1 if it is onehot else 0
*/
static int onehot(unsigned short value)
{
return (value & (value - 1)) == 0;
} /**
* pl353_nand_correct_data - ECC correction function
* @mtd: Pointer to the mtd_info structure
* @buf: Pointer to the page data
* @read_ecc: Pointer to the ECC value read from spare data area
* @calc_ecc: Pointer to the calculated ECC value
*
* This function corrects the ECC single bit errors & detects 2-bit errors.
*
* Return: 0 if no ECC errors found
* 1 if single bit error found and corrected.
* -1 if multiple ECC errors found.
*/
static int pl353_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc,
unsigned char *calc_ecc)
{
unsigned char bit_addr;
unsigned int byte_addr;
unsigned short ecc_odd, ecc_even, read_ecc_lower, read_ecc_upper;
unsigned short calc_ecc_lower, calc_ecc_upper; read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff;
read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff; calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff;
calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff; ecc_odd = read_ecc_lower ^ calc_ecc_lower; //异或:不同为1,同样为0
ecc_even = read_ecc_upper ^ calc_ecc_upper; //假设异或结果都为0,则说明read_ecc和calc_ecc是相等的,没有出错,直接退出
if ((ecc_odd == 0) && (ecc_even == 0))
return 0; /* no error */ if (ecc_odd == (~ecc_even & 0xfff)) {
/* bits [11:3] of error code is byte offset */
byte_addr = (ecc_odd >> 3) & 0x1ff; //第几个字节出错
/* bits [2:0] of error code is bit offset */
bit_addr = ecc_odd & 0x7; //第几个bit出错
/* Toggling error bit */
buf[byte_addr] ^= (1 << bit_addr); //纠正出错的bit位
return 1;
} if (onehot(ecc_odd | ecc_even) == 1)
return 1; /* one error in parity */ return -1; /* Uncorrectable error */
} /**
* pl353_nand_read_oob - [REPLACABLE] the most common OOB data read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to read
*
* Return: Always return zero
*/
//将page中的OOB数据读入chip->oob_poi中
static int pl353_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
unsigned long data_phase_addr;
uint8_t *p; //发送读取OOB数据的命令
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); //在读取最后4个字节数据时。须要将data_phase_addr的clear_cs位置1,
//通知控制器这是最后的数据读取
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL353_NAND_CLEAR_CS;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); return 0;
} /**
* pl353_nand_write_oob - [REPLACABLE] the most common OOB data write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @page: Page number to write
*
* Return: Zero on success and EIO on failure
*/
static int pl353_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
int status = 0;
const uint8_t *buf = chip->oob_poi;
unsigned long data_phase_addr; //发送写命令
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); chip->write_buf(mtd, buf,
(mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH));
buf += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); //在写最后4B数据时,须要设置end command valid。这样在写全然部的数据后,
//写命令的第2个命令NAND_CMD_PAGEPROG才会有效,将数据真正地写入相应的OOB中。
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL353_NAND_CLEAR_CS; //设置clear cs位为1
data_phase_addr |= (1 << END_CMD_VALID_SHIFT); //设置end command valid
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, buf, PL353_NAND_LAST_TRANSFER_LENGTH); /* Send command to program the OOB data */
//这条代码不须要,由于NAND_CMD_PAGEPROG已经在data_phase_addr中了
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip); return status & NAND_STATUS_FAIL ? -EIO : 0;
} /**
* pl353_nand_read_page_raw - [Intern] read raw page data without ecc
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
//将一个页中的数据读入buf中,将OOB数据读入chip->oob_poi中
static int pl353_nand_read_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
unsigned long data_phase_addr;
uint8_t *p; chip->read_buf(mtd, buf, mtd->writesize); p = chip->oob_poi;
chip->read_buf(mtd, p,
(mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL353_NAND_CLEAR_CS;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; chip->read_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH);
return 0;
} /**
* pl353_nand_write_page_raw - [Intern] raw page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
*
* Return: Always return zero
*/
//将buf中一页的数据写入页中。并将chip->oob_poi中的数据写入oob区域
static int pl353_nand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
unsigned long data_phase_addr;
uint8_t *p; chip->write_buf(mtd, buf, mtd->writesize); p = chip->oob_poi;
chip->write_buf(mtd, p,
(mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH));
p += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH); data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL353_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; chip->write_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); return 0;
} /**
* nand_write_page_hwecc - Hardware ECC based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
*
* This functions writes data and hardware generated ECC values in to the page.
*
* Return: Always return zero
*/
//将buf中一页的数据写入页中,并将硬件生成的ecc值写入oob区域
static int pl353_nand_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required)
{
int i, eccsize = chip->ecc.size;//为512,即一次ecc计算能处理多少字节数据
int eccsteps = chip->ecc.steps; //一页数据须要多少次ecc计算,如:2KB页大小,要4次ecc计算
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf; //要写入页的数据
uint32_t *eccpos = chip->ecc.layout->eccpos; //ecc在OOB中存放的位置
unsigned long data_phase_addr;
uint8_t *oob_ptr; //每次写512B数据,
for ( ; (eccsteps - 1); eccsteps--) {
chip->write_buf(mtd, p, eccsize);
p += eccsize;
}
//在写最后一个512B的最后4B数据是,须要设置ECC_LAST来
//通知硬件ecc模块当前是最后一个訪问
chip->write_buf(mtd, p, (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH));
p += (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH); /* Set ECC Last bit to 1 */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL353_NAND_ECC_LAST;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
chip->write_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); /* Wait for ECC to be calculated and read the error values */
p = buf;
chip->ecc.calculate(mtd, p, &ecc_calc[0]);//获取刚刚硬件计算的ecc值 for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]); /* Clear ECC last bit */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr &= ~PL353_NAND_ECC_LAST;
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr; /* Write the spare area with ECC bytes *///写ecc到OOB区域
oob_ptr = chip->oob_poi;
chip->write_buf(mtd, oob_ptr,
(mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
data_phase_addr |= PL353_NAND_CLEAR_CS;
data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
chip->IO_ADDR_W = (void __iomem * __force)data_phase_addr;
oob_ptr += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH);
chip->write_buf(mtd, oob_ptr, PL353_NAND_LAST_TRANSFER_LENGTH); return 0;
} /**
* pl353_nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the data buffer
* @oob_required: Caller requires OOB data read to chip->oob_poi
*
* Return: Always return zero
*/
static int pl353_nand_write_page_swecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos; /* Software ecc calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]); for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->ecc.write_page_raw(mtd, chip, buf, 1); return 0;
} /**
* pl353_nand_read_page_hwecc - Hardware ECC based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* This functions reads data and checks the data integrity by comparing hardware
* generated ECC values and read ECC values from spare area.
*
* Return: 0 always and updates ECC operation status in to MTD structure
*/
static int pl353_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, stat, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
unsigned long data_phase_addr;
uint8_t *oob_ptr; for ( ; (eccsteps - 1); eccsteps--) {
chip->read_buf(mtd, p, eccsize);
p += eccsize;
}
chip->read_buf(mtd, p, (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH));
p += (eccsize - PL353_NAND_LAST_TRANSFER_LENGTH); /* Set ECC Last bit to 1 */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL353_NAND_ECC_LAST;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->read_buf(mtd, p, PL353_NAND_LAST_TRANSFER_LENGTH); /* Read the calculated ECC value */
p = buf;
chip->ecc.calculate(mtd, p, &ecc_calc[0]); /* Clear ECC last bit */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr &= ~PL353_NAND_ECC_LAST;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; /* Read the stored ECC value */
oob_ptr = chip->oob_poi;
chip->read_buf(mtd, oob_ptr,
(mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH)); /* de-assert chip select */
data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
data_phase_addr |= PL353_NAND_CLEAR_CS;
chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr; oob_ptr += (mtd->oobsize - PL353_NAND_LAST_TRANSFER_LENGTH);
chip->read_buf(mtd, oob_ptr, PL353_NAND_LAST_TRANSFER_LENGTH); for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = ~(chip->oob_poi[eccpos[i]]); eccsteps = chip->ecc.steps;
p = buf; /* Check ECC error for all blocks and correct if it is correctable */
//使用ecc对读出来的数据进行检查,假设发现错误就尝试纠正
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0) //假设返回值为-1。表示发生了不可纠正的错误
mtd->ecc_stats.failed++;
else //返回0或者1。表示没有错误或发生了可纠正的错误
mtd->ecc_stats.corrected += stat;
}
return 0;
} /**
* pl353_nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
* @buf: Pointer to the buffer to store read data
* @oob_required: Caller requires OOB data read to chip->oob_poi
* @page: Page number to read
*
* Return: Always return zero
*/
static int pl353_nand_read_page_swecc(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos; chip->ecc.read_page_raw(mtd, chip, buf, page, 1); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]); for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]]; eccsteps = chip->ecc.steps;
p = buf; for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
} /**
* pl353_nand_select_chip - Select the flash device
* @mtd: Pointer to the mtd info structure
* @chip: Pointer to the NAND chip info structure
*
* This function is empty as the NAND controller handles chip select line
* internally based on the chip address passed in command and data phase.
*/
static void pl353_nand_select_chip(struct mtd_info *mtd, int chip)
{
return;
} /**
* pl353_nand_cmd_function - Send command to NAND device
* @mtd: Pointer to the mtd_info structure
* @command: The command to be sent to the flash device
* @column: The column address for this command, -1 if none
* @page_addr: The page address for this command, -1 if none
*/
static void pl353_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
struct nand_chip *chip = mtd->priv;
const struct pl353_nand_command_format *curr_cmd = NULL;
struct pl353_nand_info *xnand =
container_of(mtd, struct pl353_nand_info, mtd);
void __iomem *cmd_addr;
unsigned long cmd_data = 0, end_cmd_valid = 0;
unsigned long cmd_phase_addr, data_phase_addr, end_cmd, i;
unsigned long timeout = jiffies + PL353_NAND_DEV_BUSY_TIMEOUT; if (xnand->end_cmd_pending) {//假设end_cmd_pending为1,表示前一次调用这个函数的命令有end command
/*
* Check for end command if this command request is same as the
* pending command then return
*/
if (xnand->end_cmd == command) {
xnand->end_cmd = 0;
xnand->end_cmd_pending = 0;
return;
}
} /* Emulate NAND_CMD_READOOB for large page device */
if ((mtd->writesize > PL353_NAND_ECC_SIZE) &&
(command == NAND_CMD_READOOB)) {
column += mtd->writesize;
command = NAND_CMD_READ0;
} /* Get the command format */
for (i = 0; (pl353_nand_commands[i].start_cmd != NAND_CMD_NONE ||
pl353_nand_commands[i].end_cmd != NAND_CMD_NONE); i++)
if (command == pl353_nand_commands[i].start_cmd)
curr_cmd = &pl353_nand_commands[i]; if (curr_cmd == NULL)//假设为空,表示当前命令是无效的,退出
return; /* Clear interrupt */
pl353_smc_clr_nand_int(); /* Get the command phase address */
if (curr_cmd->end_cmd_valid == PL353_NAND_CMD_PHASE)
end_cmd_valid = 1; if (curr_cmd->end_cmd == NAND_CMD_NONE)
end_cmd = 0x0;
else
end_cmd = curr_cmd->end_cmd;
//cmd_phase_addr和data_phase_addr的意义须要看pl353数据手冊
//在pl353中命令參数是包括在命令阶段地址和数据阶段地址中的
cmd_phase_addr = (unsigned long __force)xnand->nand_base |
(curr_cmd->addr_cycles << ADDR_CYCLES_SHIFT) | //地址周期的个数
(end_cmd_valid << END_CMD_VALID_SHIFT) |
(COMMAND_PHASE) | //为0,表示这是命令阶段地址
(end_cmd << END_CMD_SHIFT) |
(curr_cmd->start_cmd << START_CMD_SHIFT); cmd_addr = (void __iomem * __force)cmd_phase_addr; /* Get the data phase address */
end_cmd_valid = 0; data_phase_addr = (unsigned long __force)xnand->nand_base |
(0x0 << CLEAR_CS_SHIFT) |
(end_cmd_valid << END_CMD_VALID_SHIFT) |
(DATA_PHASE) | //为1。表示这个数据阶段地址
(end_cmd << END_CMD_SHIFT) |
(0x0 << ECC_LAST_SHIFT); chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
chip->IO_ADDR_W = chip->IO_ADDR_R; /* Command phase AXI write */
/* Read & Write */
//依据column和page_addr等參数,生成要发送的地址cmd_data
if (column != -1 && page_addr != -1) {
/* Adjust columns for 16 bit bus width */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
cmd_data = column;
if (mtd->writesize > PL353_NAND_ECC_SIZE) {
cmd_data |= page_addr << 16;
/* Another address cycle for devices > 128MiB */
if (chip->chipsize > (128 << 20)) {
pl353_nand_write32(cmd_addr, cmd_data);//假设是大页,在这里先发送前4个周期的地址
cmd_data = (page_addr >> 16);
}
} else {
cmd_data |= page_addr << 8;
}
} else if (page_addr != -1) {
/* Erase */
cmd_data = page_addr;
} else if (column != -1) {
/*
* Change read/write column, read id etc
* Adjust columns for 16 bit bus width
*/
if ((chip->options & NAND_BUSWIDTH_16) &&
((command == NAND_CMD_READ0) ||
(command == NAND_CMD_SEQIN) ||
(command == NAND_CMD_RNDOUT) ||
(command == NAND_CMD_RNDIN)))
column >>= 1;
cmd_data = column;
}
//发送小页的4个周期地址或者大页的第5个周期地址
pl353_nand_write32(cmd_addr, cmd_data); //假设当前命令有end command,则将xnand->end_cmd_pending置1
if (curr_cmd->end_cmd_valid) {
xnand->end_cmd = curr_cmd->end_cmd;
xnand->end_cmd_pending = 1;
}
//延时。等待命令完毕
ndelay(100); if ((command == NAND_CMD_READ0) ||
(command == NAND_CMD_RESET) ||
(command == NAND_CMD_PARAM) ||
(command == NAND_CMD_GET_FEATURES)) { /* Wait till the device is ready or timeout */
do {
if (chip->dev_ready(mtd))
break;
else
cpu_relax();
} while (!time_after_eq(jiffies, timeout)); if (time_after_eq(jiffies, timeout))
pr_err("%s timed out\n", __func__);
return;
}
} /**
* pl353_nand_read_buf - read chip data into buffer
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to read
*/
static void pl353_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
unsigned long *ptr = (unsigned long *)buf; len >>= 2;
for (i = 0; i < len; i++)
ptr[i] = readl(chip->IO_ADDR_R);//通过IO_ADDR_R地址读取数据
} /**
* pl353_nand_write_buf - write buffer to chip
* @mtd: Pointer to the mtd info structure
* @buf: Pointer to the buffer to store read data
* @len: Number of bytes to write
*/
static void pl353_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
int i;
struct nand_chip *chip = mtd->priv;
unsigned long *ptr = (unsigned long *)buf; len >>= 2; for (i = 0; i < len; i++)
writel(ptr[i], chip->IO_ADDR_W);
} /**
* pl353_nand_device_ready - Check device ready/busy line
* @mtd: Pointer to the mtd_info structure
*
* Return: 0 on busy or 1 on ready state
*/
//查询设备是否处于空暇状态
static int pl353_nand_device_ready(struct mtd_info *mtd)
{
if (pl353_smc_get_nand_int_status_raw()) {
pl353_smc_clr_nand_int();
return 1;
}
return 0;
} /**
* pl353_nand_detect_ondie_ecc - Get the flash ondie ecc state
* @mtd: Pointer to the mtd_info structure
*
* This function enables the ondie ecc for the Micron ondie ecc capable devices
*
* Return: 1 on detect, 0 if fail to detect
*/
static int pl353_nand_detect_ondie_ecc(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
u8 maf_id, dev_id, i, get_feature;
u8 set_feature[4] = { 0x08, 0x00, 0x00, 0x00 }; /* Check if On-Die ECC flash */
nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
//发送ReadID的命令:0x90去取得nand 芯片的ID信息
nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */
maf_id = readb(nand_chip->IO_ADDR_R);//第一个字节是厂商ID
dev_id = readb(nand_chip->IO_ADDR_R);//第二个字节是芯片ID if ((maf_id == NAND_MFR_MICRON) &&
((dev_id == 0xf1) || (dev_id == 0xa1) ||
(dev_id == 0xb1) || (dev_id == 0xaa) ||
(dev_id == 0xba) || (dev_id == 0xda) ||
(dev_id == 0xca) || (dev_id == 0xac) ||
(dev_id == 0xbc) || (dev_id == 0xdc) ||
(dev_id == 0xcc) || (dev_id == 0xa3) ||
(dev_id == 0xb3) ||
(dev_id == 0xd3) || (dev_id == 0xc3))) { nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
get_feature = readb(nand_chip->IO_ADDR_R); if (get_feature & 0x08) {
return 1;
} else {
nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
for (i = 0; i < 4; i++)
writeb(set_feature[i], nand_chip->IO_ADDR_W); ndelay(1000); nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
ONDIE_ECC_FEATURE_ADDR, -1);
get_feature = readb(nand_chip->IO_ADDR_R); if (get_feature & 0x08)
return 1; }
} return 0;
} /**
* pl353_nand_ecc_init - Initialize the ecc information as per the ecc mode
* @mtd: Pointer to the mtd_info structure
* @ondie_ecc_state: ondie ecc status
*
* This function initializes the ecc block and functional pointers as per the
* ecc mode
*/
static void pl353_nand_ecc_init(struct mtd_info *mtd, int ondie_ecc_state)
{
struct nand_chip *nand_chip = mtd->priv; nand_chip->ecc.mode = NAND_ECC_HW;//使用硬件ecc
nand_chip->ecc.read_oob = pl353_nand_read_oob;
nand_chip->ecc.read_page_raw = pl353_nand_read_page_raw;
nand_chip->ecc.strength = 1;
nand_chip->ecc.write_oob = pl353_nand_write_oob;
nand_chip->ecc.write_page_raw = pl353_nand_write_page_raw; if (ondie_ecc_state) {
/* bypass the controller ECC block */
pl353_smc_set_ecc_mode(PL353_SMC_ECCMODE_BYPASS); /*
* The software ECC routines won't work with the
* SMC controller
*/
nand_chip->ecc.bytes = 0;
nand_chip->ecc.layout = &ondie_nand_oob_64;
nand_chip->ecc.read_page = pl353_nand_read_page_raw;
nand_chip->ecc.write_page = pl353_nand_write_page_raw;
nand_chip->ecc.size = mtd->writesize;
/*
* On-Die ECC spare bytes offset 8 is used for ECC codes
* Use the BBT pattern descriptors
*/
nand_chip->bbt_td = &bbt_main_descr;
nand_chip->bbt_md = &bbt_mirror_descr;
} else {
/* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
nand_chip->ecc.bytes = 3;
nand_chip->ecc.calculate = pl353_nand_calculate_hwecc;
nand_chip->ecc.correct = pl353_nand_correct_data;
nand_chip->ecc.hwctl = NULL;
nand_chip->ecc.read_page = pl353_nand_read_page_hwecc;
nand_chip->ecc.size = PL353_NAND_ECC_SIZE;
nand_chip->ecc.write_page = pl353_nand_write_page_hwecc; pl353_smc_set_ecc_pg_size(mtd->writesize);
switch (mtd->writesize) {
case 512:
case 1024:
case 2048:
pl353_smc_set_ecc_mode(PL353_SMC_ECCMODE_APB);
break;
default:
/*
* The software ECC routines won't work with the
* SMC controller
*/
nand_chip->ecc.calculate = nand_calculate_ecc;
nand_chip->ecc.correct = nand_correct_data;
nand_chip->ecc.read_page = pl353_nand_read_page_swecc;
nand_chip->ecc.write_page = pl353_nand_write_page_swecc;
nand_chip->ecc.size = 256;
break;
}
//假设这里没有设置layout的值。将会在nand_scan_tail中再次依据oobsize的
//值来设置一个默认的layout
if (mtd->oobsize == 16) //一般页中每512字节会分配16字节的OOB空间
nand_chip->ecc.layout = &nand_oob_16;
else if (mtd->oobsize == 64)
nand_chip->ecc.layout = &nand_oob_64;
}
} /**
* pl353_nand_probe - Probe method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function initializes the driver data structures and the hardware.
*
* Return: 0 on success or error value on failure
*/
static int pl353_nand_probe(struct platform_device *pdev)
{
struct pl353_nand_info *xnand;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct resource *res;
struct mtd_part_parser_data ppdata;
int ondie_ecc_state; xnand = devm_kzalloc(&pdev->dev, sizeof(*xnand), GFP_KERNEL);
if (!xnand)
return -ENOMEM; /* Map physical address of NAND flash */
//映射nand flash的基地值,这个值在pl353数据手冊中设定
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
xnand->nand_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(xnand->nand_base))
return PTR_ERR(xnand->nand_base); /* Link the private data with the MTD structure */
mtd = &xnand->mtd;
nand_chip = &xnand->chip; nand_chip->priv = xnand;
mtd->priv = nand_chip;
mtd->owner = THIS_MODULE;
mtd->name = PL353_NAND_DRIVER_NAME; /* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = xnand->nand_base;
nand_chip->IO_ADDR_W = xnand->nand_base; //假设nand_chip的一些函数没有在这里实现,则会在nand_base.c文件的
//nand_set_defaults函数中将他们设置为nand_base.c中通用的函数
/* Set the driver entry points for MTD */
nand_chip->cmdfunc = pl353_nand_cmd_function;
nand_chip->dev_ready = pl353_nand_device_ready;
nand_chip->select_chip = pl353_nand_select_chip; /* If we don't set this delay driver sets 20us by default */
nand_chip->chip_delay = 30; /* Buffer read/write routines */
nand_chip->read_buf = pl353_nand_read_buf;
nand_chip->write_buf = pl353_nand_write_buf; /* Set the device option and flash width */
nand_chip->options = NAND_BUSWIDTH_AUTO;//依据硬件,自己主动设置nand总线宽度
nand_chip->bbt_options = NAND_BBT_USE_FLASH;//bbt存放在nand中 platform_set_drvdata(pdev, xnand); ondie_ecc_state = pl353_nand_detect_ondie_ecc(mtd); /* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
return -ENXIO;
} pl353_nand_ecc_init(mtd, ondie_ecc_state);
if (nand_chip->options & NAND_BUSWIDTH_16)
pl353_smc_set_buswidth(PL353_SMC_MEM_WIDTH_16); /* second phase scan */
//在nand_scan_tail函数功能:对nand_chip->ecc和mtd余下部分赋值,建立坏块表等
if (nand_scan_tail(mtd)) {
dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
return -ENXIO;
} ppdata.of_node = pdev->dev.of_node; mtd_device_parse_register(&xnand->mtd, NULL, &ppdata, NULL, 0); return 0;
} /**
* pl353_nand_remove - Remove method for the NAND driver
* @pdev: Pointer to the platform_device structure
*
* This function is called if the driver module is being unloaded. It frees all
* resources allocated to the device.
*
* Return: 0 on success or error value on failure
*/
static int pl353_nand_remove(struct platform_device *pdev)
{
struct pl353_nand_info *xnand = platform_get_drvdata(pdev); /* Release resources, unregister device */
nand_release(&xnand->mtd);
/* kfree(NULL) is safe */
kfree(xnand->parts); return 0;
} /* Match table for device tree binding */
static const struct of_device_id pl353_nand_of_match[] = {
{ .compatible = "arm,pl353-nand-r2p1" },
{},
};
MODULE_DEVICE_TABLE(of, pl353_nand_of_match); /*
* pl353_nand_driver - This structure defines the NAND subsystem platform driver
*/
static struct platform_driver pl353_nand_driver = {
.probe = pl353_nand_probe,
.remove = pl353_nand_remove,
.driver = {
.name = PL353_NAND_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = pl353_nand_of_match,
},
}; module_platform_driver(pl353_nand_driver); MODULE_AUTHOR("Xilinx, Inc.");
MODULE_ALIAS("platform:" PL353_NAND_DRIVER_NAME);
MODULE_DESCRIPTION("ARM PL353 NAND Flash Driver");
MODULE_LICENSE("GPL"); //在nand_scan_ident函数中会调用nand_get_flash_type函数。这个函数非常重要。主要是获取
//nand flash一些基本參数。如页大小,块大小等。
/*
* Get the flash and manufacturer id and lookup if the type is supported.
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int *maf_id, int *dev_id,
struct nand_flash_dev *type)
{
int busw;//硬件的总线宽度
int i, maf_idx;
u8 id_data[8]; /* Select the device */
chip->select_chip(mtd, 0);//选中芯片,才干对其操作 /*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up.
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd); /*
* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
*/ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
//获取整个ID数据,总共同拥有8字节
/* Read entire ID string */
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);
//推断第二次读取的ID是否与第一次读取的一样,不一样则说明设备有问题
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
*maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
} if (!type)
type = nand_flash_ids; for (; type->name != NULL; type++) {
if (is_full_id_nand(type)) {
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
goto ident_done;
} else if (*dev_id == type->dev_id) {//找到这个芯片ID相应的nand_flash_dev
break;
}
} chip->onfi_version = 0;
if (!type->name || !type->pagesize) {
/* Check if the chip is ONFI compliant */
if (nand_flash_detect_onfi(mtd, chip, &busw))
goto ident_done; /* Check if the chip is JEDEC compliant */
if (nand_flash_detect_jedec(mtd, chip, &busw))
goto ident_done;
}
//name为空,表示没有在nand_flash_ids数组中找到此芯片ID相应的设备
if (!type->name)
return ERR_PTR(-ENODEV); if (!mtd->name)
mtd->name = type->name; chip->chipsize = (uint64_t)type->chipsize << 20; if (!type->pagesize && chip->init_size) {
/* Set the pagesize, oobsize, erasesize by the driver */
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
/* Decode parameters from extended ID */
nand_decode_ext_id(mtd, chip, id_data, &busw);
} else {
//假设type->pagesize不为0。则使用type的參数来设置
//mtd中芯片writesize,oobsize,erasesize的值
nand_decode_id(mtd, chip, type, id_data, &busw);
}
/* Get chip options */
chip->options |= type->options; /*
* Check if chip is not a Samsung device. Do not clear the
* options for chips which do not have an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done: /* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
break;
} if (chip->options & NAND_BUSWIDTH_AUTO) {//依据硬件。自己主动设在总线宽度
WARN_ON(chip->options & NAND_BUSWIDTH_16);
chip->options |= busw;
nand_set_defaults(chip, busw);
} else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
//否则检測驱动中关于位宽的定义是否和硬件一致
/*
* Check, if buswidth is correct. Hardware drivers should set
* chip correct!
*/
pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
*maf_id, *dev_id);
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name);
pr_warn("bus width %d instead %d bit\n",
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
return ERR_PTR(-EINVAL);
} nand_decode_bbm_options(mtd, chip, id_data); /* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1;
/* Convert chipsize to number of pages per chip -1 */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; chip->bbt_erase_shift = chip->phys_erase_shift =
ffs(mtd->erasesize) - 1;
if (chip->chipsize & 0xffffffff)//推断是否超过4GB,预计是ffs函数最多支持32位
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
else {
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
chip->chip_shift += 32 - 1;
} chip->badblockbits = 8;
chip->erase = single_erase; /* Do not replace user supplied command function! */
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp;//大页使用的默认命令函数 pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
*maf_id, *dev_id); if (chip->onfi_version)
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
chip->onfi_params.model);
else if (chip->jedec_version)
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
chip->jedec_params.model);
else
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
type->name); pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
mtd->writesize, mtd->oobsize);
return type;
}

linux下Pl353 NAND Flash驱动分析的更多相关文章

  1. NAND FLASH 驱动分析

    NAND FLASH是一个存储芯片 那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A" 问1. 原理图上NAND FLASH和S3C2440之间只有数据线,       ...

  2. linux驱动基础系列--Linux下Spi接口Wifi驱动分析

    前言 本文纯粹的纸上谈兵,我并未在实际开发过程中遇到需要编写或调试这类驱动的时候,本文仅仅是根据源码分析后的记录!基于内核版本:2.6.35.6 .主要是想对spi接口的wifi驱动框架有一个整体的把 ...

  3. 如何编写linux下nand flash驱动-4

    2.       软件方面 如果想要在Linux下编写Nand Flash驱动,那么就先要搞清楚Linux下,关于此部分的整个框架.弄明白,系统是如何管理你的nand flash的,以及,系统都帮你做 ...

  4. 如何编写linux下nand flash驱动-2

    [Nand Flash引脚(Pin)的说明] 图3.Nand Flash引脚功能说明 上图是常见的Nand Flash所拥有的引脚(Pin)所对应的功能,简单翻译如下: 1.       I/O0 ~ ...

  5. Nand Flash驱动(实现初始化以及读操作)

    简单制作一个Nand Flash驱动(只需要初始化Flash以及读Flash) 打开2440芯片手册,K9F2G08U0M芯片手册(因为2440中Nand Flash是用的256MB(2Gb)内存,8 ...

  6. 十八、Nand Flash驱动和Nor Flash驱动

    在读者学习本章之前,最好了解Nand Flash读写过程和操作,可以参考:Nand Flash裸机操作. 一开始想在本章写eMMC框架和设备驱动,但是没有找到关于eMMC设备驱动具体写法,所以本章仍继 ...

  7. 15.1 linux操作系统下nand flash驱动框架2

    当我们需要在操作系统上读写普通文件的时候,总是需要一层层往下,最终到达硬件相关操作,当然底层设备大多数都是块设备 NAND FLASH就作为一个最底层的块设备. 而写驱动,就是要构建硬件与操作系统之间 ...

  8. Linux 下 Nand Flash 驱动说明

    注册 driver_register 通过 module_init(s3c2410_nand_init);注册 Nand Flash 驱动. 在 s3c2410_nand_init ()中通过 dri ...

  9. Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

随机推荐

  1. haproxy配置tcp负载均衡

    #---------------------------------------------------------------------# 监控页面 #---------------------- ...

  2. c# 字符串排序 (面试题)

    将一些字符串,如: "bc", "ad", "ac", "hello", "xman", " ...

  3. Jquery学习笔记(9)--注册验证复习(未用到ajax)

    纯复习,在$(this).val()这里浪费了时间,val()只适合input里面的value值,如果是span等标签里包裹的文本要用text()!! <!DOCTYPE html> &l ...

  4. JVM Specification 9th Edition (4) Chapter 4. The class File Format

    Chapter 4. The class File Format Table of Contents 4.1. The ClassFile Structure 4.2. Names 4.2.1. Bi ...

  5. a5调试

    1 generating rsa key...[    4.452000] mmc0: error -110 whilst initialising SD card[    5.602000] mmc ...

  6. 在OrangePI上搭建homeassitant过程记录

    1.更换Python版本 由于在新版的homeassistant当中需要使用python3.5.3及以后的版本,但由于apt源中只包含3.5.2的版本,所以需要升级到python3.6. 具体更换方式 ...

  7. 元器件封装标准IPC-7351

    IPC-7351依赖久经考验的数学算法,综合考虑制造.组装和元件容差,从而精确计算焊盘图形.该标准以IPC-SM-782研发概念为基础进一步提高,对每一个元件都建立了三个焊盘图形几何形状,对每一系列元 ...

  8. VM克隆之后启动eth0找不到eth0:unknown interface:no such device

    问题出现:VMware 克隆之后,ifconfig命令执行找不到eth0,报错 eth0:unknown interface:no such device 是因为/etc/sysconf/networ ...

  9. 2017 ACM区域赛(西安) 参赛流水账

    day 0: 周五, 鸽了概统课,早上和紫金港的几位小伙伴一起打车去萧山机场,从咸阳机场到西北工业大学坐了五十多个站的公交车,感觉身体被掏空.晚上在宾馆本来打算补之前训练的一个题,想想还是先花个十来分 ...

  10. Alpha matting算法发展

    一.抠图算法简介 Alpha matting算法研究的是如何将一幅图像中的前景信息和背景信息分离的问题,即抠图.这类问题是数字图像处理与数字图像编辑领域中的一类经典问题,广泛应用于视频编缉与视频分割领 ...