I need to implement ONFI commands SET FEATURES / GET FEATURES in order to enable on-die ECC for a Micron NAND (MT29F4G08ABADA) and custom P2020 board running linux.
However, I'm having a hard time implementing that using the FCM.
I'm hoping that I simply haven't understood some feature of FCM programming, and that you will be able to spot my mistake.
The sequence to enable internal ECC with SET FEATURES is EFh(cmd)-90h(addr)-08h(data)-00h(data)-00h(data)-00h(data)-wait(tFEAT).
The GET FEATURES command is EEh.
Does the following implementation look reasonable?:
fsl_elbc_nand.c, fsl_elbc_cmdfunc():
...
case NAND_CMD_SET_FEATURES: // 0xEF
out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
(FIR_OP_UA << FIR_OP1_SHIFT) |
(FIR_OP_WB << FIR_OP2_SHIFT));
out_be32(&lbc->fcr, NAND_CMD_SET_FEATURES << FCR_CMD0_SHIFT);
out_be32(&lbc->fbcr, 4);
ctrl->read_bytes = 0;
ctrl->use_mdr = 1;
ctrl->mdr = column; // 0x90
set_addr(mtd, 0, 0, 0);
fsl_elbc_run_command(mtd);
//after this, 4 bytes [0x08,0x00,0x00,0x00] are written with fsl_elbc_write_buf()
break;
case NAND_CMD_GET_FEATURES: // 0xEE
out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
(FIR_OP_UA << FIR_OP1_SHIFT) |
(FIR_OP_RBW << FIR_OP2_SHIFT));
out_be32(&lbc->fcr, NAND_CMD_GET_FEATURES << FCR_CMD0_SHIFT);
out_be32(&lbc->fbcr, 4);
ctrl->read_bytes = 4;
ctrl->use_mdr = 1;
ctrl->mdr = column; // 0x90
set_addr(mtd, 0, 0, 0);
fsl_elbc_run_command(mtd);
//after this, 4 bytes are read with fsl_elbc_read_buf()
break;
...
Hello Magnus Nilsson,
The code looks OK.
Regards
Lunmin
I can't get SET_FEATURES to work properly.
I believe GET_FEATURES is working - out of reset i read [0,0,0,0] as expected ( http://datasheet.octopart.com/MT29F4G08ABBDAH4%3AD-Micron-datasheet-11758868.pdf page 50). I can also replace it with another operation e.g. READ_ID and read the expected data.
However, when I try to write [8,0,0,0] with SET_FEATURES (address 0x90), I end up writing [F,0,0,0].
With different delays (unfortunately I don't have specifics - I'm experimenting with that right now) I have also read back [A,0,0,0].
Code:
static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
int status;
if (!chip->onfi_version)
return -EINVAL;
chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
printk(KERN_ERR "nand_onfi_set_features addr: %x subfeature_param: %x %x %x %x\n",addr,subfeature_param[0],subfeature_param[1],subfeature_param[2],subfeature_param[3]);
ndelay(100); //tADL=min 70ns
chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
ndelay(1000); //tFEAT=max 1us=1000ns
status = chip->waitfunc(mtd, chip);
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
}
static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
int addr, uint8_t *subfeature_param)
{
if (!chip->onfi_version)
return -EINVAL;
chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
//chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //reads 2c dc 90 95 (correct, this is MT29F4G08ABADA)
//chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); //reads 'O' 'N' 'F' 'I' (correct)
ndelay(1000); //tFEAT=max 1us=1000ns
chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
printk(KERN_ERR "nand_onfi_get_features addr: %x subfeature_param: %x %x %x %x\n",addr,subfeature_param[0],subfeature_param[1],subfeature_param[2],subfeature_param[3]);
return 0;
}
Debug output:
nand_onfi_set_features addr: 90 subfeature_param: 8 0 0 0
nand_onfi_get_features addr: 90 subfeature_param: f 0 0 0
Well, I found a workaround - using MDR to write the 4 byte array (the last byte is thankfully is 0x00, since MDR only contains 4 bytes and the address 0x90 already uses one).
I still don't understand quite how fsl_elbc_write_buf() works - when I use it and then try to write with FIR_OP_WB, it just writes garbage.
In any case, if someone else is stuck with the same problem, here's how I solved it for now:
case NAND_CMD_SET_FEATURES:
out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
(FIR_OP_UA << FIR_OP1_SHIFT) |
(FIR_OP_WS << FIR_OP2_SHIFT) |
(FIR_OP_WS << FIR_OP3_SHIFT) |
(FIR_OP_WS << FIR_OP4_SHIFT) |
(FIR_OP_WS << FIR_OP5_SHIFT));
out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
out_be32(&lbc->fbcr, 0);
ctrl->use_mdr = 1;
//page_addr=0x00 or 0x08, so MDR = 0x00000090 or 0x00000890 to enable/disable on-die ECC
ctrl->mdr = (page_addr<<8) + column;
set_addr(mtd, 0, 0, 0);
fsl_elbc_run_command(mtd);
break;