[PATCH][OneNAND] Write-while-program support
Adrian Hunter
ext-adrian.hunter at nokia.com
Mon Nov 17 02:57:33 EST 2008
Kyungmin Park wrote:
> On Fri, Nov 14, 2008 at 7:32 PM, Adrian Hunter
> <ext-adrian.hunter at nokia.com> wrote:
>> Kyungmin Park wrote:
>>> This is from Adrian hunter and modified for write_ops operation.
>> Thanks for looking at write-while-program. I have a few questions though.
>> I have copied the whole function below because the diff is too confusing:
>>
>>> /**
>>> * onenand_write_ops_nolock - [OneNAND Interface] write main and/or
>>> out-of-band
>>> * @param mtd MTD device structure
>>> * @param to offset to write to
>>> * @param ops oob operation description structure
>>> *
>>> * Write main and/or oob with ECC
>>> */
>>> static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
>>> struct mtd_oob_ops *ops)
>>> {
>>> struct onenand_chip *this = mtd->priv;
>>> int written = 0, column, thislen = 0, subpage = 0;
>>> int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
>>> int oobwritten = 0, oobcolumn, thisooblen, oobsize;
>>> size_t len = ops->len;
>>> size_t ooblen = ops->ooblen;
>>> const u_char *buf = ops->datbuf;
>>> const u_char *oob = ops->oobbuf;
>>> u_char *oobbuf;
>>> int ret = 0;
>>>
>>> DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len
>>> = %i\n", (unsigned int) to, (int) len);
>>>
>>> /* Initialize retlen, in case of early exit */
>>> ops->retlen = 0;
>>> ops->oobretlen = 0;
>>>
>>> /* Do not allow writes past end of device */
>>> if (unlikely((to + len) > mtd->size)) {
>>> printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to
>>> past end of device\n");
>>> return -EINVAL;
>>> }
>>>
>>> /* Reject writes, which are not page aligned */
>>> if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
>>> printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write
>>> not page aligned data\n");
>>> return -EINVAL;
>>> }
>>>
>> The loop cannot handle the case when len is zero so I had:
>>
>> if (!len)
>> return 0;
>
> Yes there's length check. I wonder is it possible to write with zero?
> Is it handle at driver level or upper level such as filesystem?
> But no problem to just add length check here at this time.
>
>>> if (ops->mode == MTD_OOB_AUTO)
>>> oobsize = this->ecclayout->oobavail;
>>> else
>>> oobsize = mtd->oobsize;
>>>
>>> oobcolumn = to & (mtd->oobsize - 1);
>>>
>>> column = to & (mtd->writesize - 1);
>>>
>>> /* Loop until all data write */
>>> while (1) {
>>> if (written < len) {
>>> u_char *wbuf = (u_char *) buf;
>>>
>>> thislen = min_t(int, mtd->writesize - column, len -
>>> written);
>>> thisooblen = min_t(int, oobsize - oobcolumn, ooblen
>>> - oobwritten);
>>>
>>> cond_resched();
>>>
>>> this->command(mtd, ONENAND_CMD_BUFFERRAM, to,
>>> thislen);
>>>
>>> /* Partial page write */
>>> subpage = thislen < mtd->writesize;
>>> if (subpage) {
>>> memset(this->page_buf, 0xff,
>>> mtd->writesize);
>>> memcpy(this->page_buf + column, buf,
>>> thislen);
>>> wbuf = this->page_buf;
>>> }
>>>
>>> this->write_bufferram(mtd, ONENAND_DATARAM, wbuf,
>>> 0, mtd->writesize);
>>>
>>> if (oob) {
>>> oobbuf = this->oob_buf;
>>>
>>> /* We send data to spare ram with oobsize
>>> * to prevent byte access */
>>> memset(oobbuf, 0xff, mtd->oobsize);
>>> if (ops->mode == MTD_OOB_AUTO)
>>> onenand_fill_auto_oob(mtd, oobbuf,
>>> oob, oobcolumn, thisooblen);
>>> else
>>> memcpy(oobbuf + oobcolumn, oob,
>>> thisooblen);
>>>
>>> oobwritten += thisooblen;
>>> oob += thisooblen;
>>> oobcolumn = 0;
>>> } else
>>> oobbuf = (u_char *) ffchars;
>>>
>>> this->write_bufferram(mtd, ONENAND_SPARERAM,
>>> oobbuf, 0, mtd->oobsize);
>>> } else
>>> ONENAND_SET_NEXT_BUFFERRAM(this);
>>>
>>> if (!first) {
>>> ONENAND_SET_PREV_BUFFERRAM(this);
>>>
>>> ret = this->wait(mtd, FL_WRITING);
>>>
>>> /* In partial page write we don't update bufferram
>>> */
>>> onenand_update_bufferram(mtd, prev, !ret &&
>>> !prev_subpage);
>>> if (ONENAND_IS_2PLANE(this)) {
>> I do not understand how 2PLANE is compatible with write-while-program
>> because
>> 2PLANE uses both buffers so we cannot start transferring to the second
>> buffer
>> while the program is ongoing. Does it work?
>>
>> Won't MLC and FlexOneNAND have that problem too?
>
> Agree, I'm not yet tested the 2PLANE features and it doesn't support
> write-while-program.
> If the 2plane and flex-onenand, it used the previous one.
>
>>> ONENAND_SET_BUFFERRAM1(this);
>>> onenand_update_bufferram(mtd, prev +
>>> this->writesize, !ret && !prev_subpage);
>>> }
>>>
>>> if (ret) {
>> My original patch had "written -= prevlen" here.
>
> Agreed.
>
Also we need to invalidate the other bufferram because we transferred the
data but did not write it. i.e.
if (written < len)
onenand_update_bufferram(mtd, to, 0);
>>> printk(KERN_ERR "onenand_write_ops_nolock:
>>> write filaed %d\n", ret);
>>> break;
>>> }
>>>
>>> if (written == len) {
>>> /* Only check verify write turn on */
>>> ret = onenand_verify(mtd, buf - len, to -
>>> len, len);
>>> if (ret) {
>>> printk(KERN_ERR
>>> "onenand_write_ops_nolock: verify failed %d\n", ret);
>>> break;
>>> }
>> Why not just break here?
>
> E.g., In original version, write bufferram 0 and it changes bufferam 1
> automatically.
> Same as if break here, it points out the previous bufferram.
The previous bufferram is the last one that was used, so it should
point to the previous one.
>>> }
>>> ONENAND_SET_NEXT_BUFFERRAM(this);
>>>
>>> if (written == len)
>>> break;
>>> }
>>>
>>> this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
>>>
>>> written += thislen;
>>>
>>> column = 0;
>>> prev_subpage = subpage;
>>> prev = to;
>>> prevlen = thislen;
>>> to += thislen;
>>> buf += thislen;
>>> first = 0;
>>> }
>>>
>>> ops->retlen = written;
>> Is ops->oobretlen needed here also?
>
> Okay I will added. I'm not sure YAFFS handle this one correctly. Maybe
> it used the retlen in ops operation (data + spare).
>
>>> return ret;
>>> }
>
> Thank you,
> Kyungmin Park
>
More information about the linux-mtd
mailing list