Skip to content

Commit 246bbe3

Browse files
Pratyush Yadavgregkh
authored andcommitted
mtd: spi-nor: core: avoid odd length/address writes in 8D-8D-8D mode
commit 17926cd upstream. On Octal DTR capable flashes like Micron Xcella the writes cannot start or end at an odd address in Octal DTR mode. Extra 0xff bytes need to be appended or prepended to make sure the start address and end address are even. 0xff is used because on NOR flashes a program operation can only flip bits from 1 to 0, not the other way round. 0 to 1 flip needs to happen via erases. Signed-off-by: Pratyush Yadav <p.yadav@ti.com> Reviewed-by: Michael Walle <michael@walle.cc> Signed-off-by: Luke Wang <ziniu.wang_1@nxp.com> Signed-off-by: Pratyush Yadav <pratyush@kernel.org> Link: https://lore.kernel.org/r/20250708091646.292-2-ziniu.wang_1@nxp.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent a056393 commit 246bbe3

1 file changed

Lines changed: 68 additions & 1 deletion

File tree

drivers/mtd/spi-nor/core.c

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,68 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
21512151
return ret;
21522152
}
21532153

2154+
/*
2155+
* On Octal DTR capable flashes, writes cannot start or end at an odd address
2156+
* in Octal DTR mode. Extra 0xff bytes need to be appended or prepended to
2157+
* make sure the start address and end address are even. 0xff is used because
2158+
* on NOR flashes a program operation can only flip bits from 1 to 0, not the
2159+
* other way round. 0 to 1 flip needs to happen via erases.
2160+
*/
2161+
static int spi_nor_octal_dtr_write(struct spi_nor *nor, loff_t to, size_t len,
2162+
const u8 *buf)
2163+
{
2164+
u8 *tmp_buf;
2165+
size_t bytes_written;
2166+
loff_t start, end;
2167+
int ret;
2168+
2169+
if (IS_ALIGNED(to, 2) && IS_ALIGNED(len, 2))
2170+
return spi_nor_write_data(nor, to, len, buf);
2171+
2172+
tmp_buf = kmalloc(nor->params->page_size, GFP_KERNEL);
2173+
if (!tmp_buf)
2174+
return -ENOMEM;
2175+
2176+
memset(tmp_buf, 0xff, nor->params->page_size);
2177+
2178+
start = round_down(to, 2);
2179+
end = round_up(to + len, 2);
2180+
2181+
memcpy(tmp_buf + (to - start), buf, len);
2182+
2183+
ret = spi_nor_write_data(nor, start, end - start, tmp_buf);
2184+
if (ret == 0) {
2185+
ret = -EIO;
2186+
goto out;
2187+
}
2188+
if (ret < 0)
2189+
goto out;
2190+
2191+
/*
2192+
* More bytes are written than actually requested, but that number can't
2193+
* be reported to the calling function or it will confuse its
2194+
* calculations. Calculate how many of the _requested_ bytes were
2195+
* written.
2196+
*/
2197+
bytes_written = ret;
2198+
2199+
if (to != start)
2200+
ret -= to - start;
2201+
2202+
/*
2203+
* Only account for extra bytes at the end if they were actually
2204+
* written. For example, if for some reason the controller could only
2205+
* complete a partial write then the adjustment for the extra bytes at
2206+
* the end is not needed.
2207+
*/
2208+
if (start + bytes_written == end)
2209+
ret -= end - (to + len);
2210+
2211+
out:
2212+
kfree(tmp_buf);
2213+
return ret;
2214+
}
2215+
21542216
/*
21552217
* Write an address range to the nor chip. Data must be written in
21562218
* FLASH_PAGESIZE chunks. The address range may be any size provided
@@ -2187,7 +2249,12 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
21872249
goto write_err;
21882250
}
21892251

2190-
ret = spi_nor_write_data(nor, addr, page_remain, buf + i);
2252+
if (nor->write_proto == SNOR_PROTO_8_8_8_DTR)
2253+
ret = spi_nor_octal_dtr_write(nor, addr, page_remain,
2254+
buf + i);
2255+
else
2256+
ret = spi_nor_write_data(nor, addr, page_remain,
2257+
buf + i);
21912258
spi_nor_unlock_device(nor);
21922259
if (ret < 0)
21932260
goto write_err;

0 commit comments

Comments
 (0)