Skip to content

Commit 535f30d

Browse files
committed
mtd: spinand: winbond: Enable high-speed modes on w35n0xjw
w35n0xjw chips can run at up to 166MHz in octal mode, but this is only possible after programming various VCR registers. Implement the new ->configure_chip() hook for this purpose. Signed-off-by: Miquel Raynal <[email protected]>
1 parent f1a9117 commit 535f30d

File tree

3 files changed

+94
-4
lines changed

3 files changed

+94
-4
lines changed

drivers/mtd/nand/spi/core.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status)
360360
engine_conf->status = status;
361361
}
362362

363-
static int spinand_write_enable_op(struct spinand_device *spinand)
363+
int spinand_write_enable_op(struct spinand_device *spinand)
364364
{
365365
struct spi_mem_op op = SPINAND_WR_EN_DIS_1S_0_0_OP(true);
366366

drivers/mtd/nand/spi/winbond.c

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/kernel.h>
1212
#include <linux/mtd/spinand.h>
1313
#include <linux/units.h>
14+
#include <linux/delay.h>
1415

1516
#define SPINAND_MFR_WINBOND 0xEF
1617

@@ -21,14 +22,26 @@
2122
#define W25N0XJW_SR4 0xD0
2223
#define W25N0XJW_SR4_HS BIT(2)
2324

25+
#define W35N01JW_VCR_IO_MODE 0x00
26+
#define W35N01JW_VCR_IO_MODE_SINGLE_SDR 0xFF
27+
#define W35N01JW_VCR_IO_MODE_OCTAL_SDR 0xDF
28+
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR_DS 0xE7
29+
#define W35N01JW_VCR_IO_MODE_OCTAL_DDR 0xC7
30+
#define W35N01JW_VCR_DUMMY_CLOCK_REG 0x01
31+
2432
/*
2533
* "X2" in the core is equivalent to "dual output" in the datasheets,
2634
* "X4" in the core is equivalent to "quad output" in the datasheets.
2735
*/
2836

2937
static SPINAND_OP_VARIANTS(read_cache_octal_variants,
38+
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ),
3039
SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ),
40+
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0),
3141
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 16, NULL, 0, 162 * HZ_PER_MHZ),
42+
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 12, NULL, 0, 124 * HZ_PER_MHZ),
43+
SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 8, NULL, 0, 86 * HZ_PER_MHZ),
44+
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_8S_OP(0, 2, NULL, 0, 0),
3245
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_8S_OP(0, 1, NULL, 0, 133 * HZ_PER_MHZ),
3346
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
3447
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
@@ -269,6 +282,79 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand)
269282
return 0;
270283
}
271284

285+
static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val)
286+
{
287+
struct spi_mem_op op =
288+
SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1),
289+
SPI_MEM_OP_ADDR(3, reg, 1),
290+
SPI_MEM_OP_NO_DUMMY,
291+
SPI_MEM_OP_DATA_OUT(1, spinand->scratchbuf, 1));
292+
int ret;
293+
294+
*spinand->scratchbuf = val;
295+
296+
ret = spinand_write_enable_op(spinand);
297+
if (ret)
298+
return ret;
299+
300+
ret = spi_mem_exec_op(spinand->spimem, &op);
301+
if (ret)
302+
return ret;
303+
304+
/*
305+
* Write VCR operation doesn't set the busy bit in SR, which means we
306+
* cannot perform a status poll. Minimum time of 50ns is needed to
307+
* complete the write.
308+
*/
309+
ndelay(50);
310+
311+
return 0;
312+
}
313+
314+
static int w35n0xjw_vcr_cfg(struct spinand_device *spinand)
315+
{
316+
const struct spi_mem_op *op;
317+
unsigned int dummy_cycles;
318+
bool dtr, single;
319+
u8 io_mode;
320+
int ret;
321+
322+
op = spinand->op_templates.read_cache;
323+
324+
single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1);
325+
dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr);
326+
if (single && !dtr)
327+
io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR;
328+
else if (!single && !dtr)
329+
io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR;
330+
else if (!single && dtr)
331+
io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR;
332+
else
333+
return -EINVAL;
334+
335+
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE, io_mode);
336+
if (ret)
337+
return ret;
338+
339+
dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
340+
switch (dummy_cycles) {
341+
case 8:
342+
case 12:
343+
case 16:
344+
case 20:
345+
case 24:
346+
case 28:
347+
break;
348+
default:
349+
return -EINVAL;
350+
}
351+
ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_DUMMY_CLOCK_REG, dummy_cycles);
352+
if (ret)
353+
return ret;
354+
355+
return 0;
356+
}
357+
272358
static const struct spinand_info winbond_spinand_table[] = {
273359
/* 512M-bit densities */
274360
SPINAND_INFO("W25N512GW", /* 1.8V */
@@ -326,7 +412,8 @@ static const struct spinand_info winbond_spinand_table[] = {
326412
&write_cache_octal_variants,
327413
&update_cache_octal_variants),
328414
0,
329-
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL)),
415+
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
416+
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
330417
SPINAND_INFO("W35N02JW", /* 1.8V */
331418
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22),
332419
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1),
@@ -335,7 +422,8 @@ static const struct spinand_info winbond_spinand_table[] = {
335422
&write_cache_octal_variants,
336423
&update_cache_octal_variants),
337424
0,
338-
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL)),
425+
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
426+
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
339427
SPINAND_INFO("W35N04JW", /* 1.8V */
340428
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
341429
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
@@ -344,7 +432,8 @@ static const struct spinand_info winbond_spinand_table[] = {
344432
&write_cache_octal_variants,
345433
&update_cache_octal_variants),
346434
0,
347-
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL)),
435+
SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL),
436+
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)),
348437
/* 2G-bit densities */
349438
SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */
350439
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),

include/linux/mtd/spinand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
741741
int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
742742
int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val);
743743
int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val);
744+
int spinand_write_enable_op(struct spinand_device *spinand);
744745
int spinand_select_target(struct spinand_device *spinand, unsigned int target);
745746

746747
int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,

0 commit comments

Comments
 (0)