Skip to content

Commit 10949af

Browse files
fschrempfmiquelraynal
authored andcommitted
mtd: spinand: Add initial support for Toshiba TC58CVG2S0H
Add minimal support for the Toshiba TC58CVG2S0H SPI NAND chip. Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de> Acked-by: Clément Péron <peron.clem@gmail.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
1 parent cfd7401 commit 10949af

File tree

4 files changed

+140
-1
lines changed

4 files changed

+140
-1
lines changed

drivers/mtd/nand/spi/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# SPDX-License-Identifier: GPL-2.0
2-
spinand-objs := core.o macronix.o micron.o winbond.o
2+
spinand-objs := core.o macronix.o micron.o toshiba.o winbond.o
33
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

drivers/mtd/nand/spi/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ static const struct nand_ops spinand_ops = {
766766
static const struct spinand_manufacturer *spinand_manufacturers[] = {
767767
&macronix_spinand_manufacturer,
768768
&micron_spinand_manufacturer,
769+
&toshiba_spinand_manufacturer,
769770
&winbond_spinand_manufacturer,
770771
};
771772

drivers/mtd/nand/spi/toshiba.c

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2018 exceet electronics GmbH
4+
* Copyright (c) 2018 Kontron Electronics GmbH
5+
*
6+
* Author: Frieder Schrempf <frieder.schrempf@kontron.de>
7+
*/
8+
9+
#include <linux/device.h>
10+
#include <linux/kernel.h>
11+
#include <linux/mtd/spinand.h>
12+
13+
#define SPINAND_MFR_TOSHIBA 0x98
14+
#define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4)
15+
16+
static SPINAND_OP_VARIANTS(read_cache_variants,
17+
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
18+
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
19+
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
20+
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
21+
22+
static SPINAND_OP_VARIANTS(write_cache_variants,
23+
SPINAND_PROG_LOAD(true, 0, NULL, 0));
24+
25+
static SPINAND_OP_VARIANTS(update_cache_variants,
26+
SPINAND_PROG_LOAD(false, 0, NULL, 0));
27+
28+
static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section,
29+
struct mtd_oob_region *region)
30+
{
31+
if (section > 7)
32+
return -ERANGE;
33+
34+
region->offset = 128 + 16 * section;
35+
region->length = 16;
36+
37+
return 0;
38+
}
39+
40+
static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section,
41+
struct mtd_oob_region *region)
42+
{
43+
if (section > 0)
44+
return -ERANGE;
45+
46+
/* 2 bytes reserved for BBM */
47+
region->offset = 2;
48+
region->length = 126;
49+
50+
return 0;
51+
}
52+
53+
static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = {
54+
.ecc = tc58cvg2s0h_ooblayout_ecc,
55+
.free = tc58cvg2s0h_ooblayout_free,
56+
};
57+
58+
static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand,
59+
u8 status)
60+
{
61+
struct nand_device *nand = spinand_to_nand(spinand);
62+
u8 mbf = 0;
63+
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
64+
65+
switch (status & STATUS_ECC_MASK) {
66+
case STATUS_ECC_NO_BITFLIPS:
67+
return 0;
68+
69+
case STATUS_ECC_UNCOR_ERROR:
70+
return -EBADMSG;
71+
72+
case STATUS_ECC_HAS_BITFLIPS:
73+
case TOSH_STATUS_ECC_HAS_BITFLIPS_T:
74+
/*
75+
* Let's try to retrieve the real maximum number of bitflips
76+
* in order to avoid forcing the wear-leveling layer to move
77+
* data around if it's not necessary.
78+
*/
79+
if (spi_mem_exec_op(spinand->spimem, &op))
80+
return nand->eccreq.strength;
81+
82+
mbf >>= 4;
83+
84+
if (WARN_ON(mbf > nand->eccreq.strength || !mbf))
85+
return nand->eccreq.strength;
86+
87+
return mbf;
88+
89+
default:
90+
break;
91+
}
92+
93+
return -EINVAL;
94+
}
95+
96+
static const struct spinand_info toshiba_spinand_table[] = {
97+
SPINAND_INFO("TC58CVG2S0H", 0xCD,
98+
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
99+
NAND_ECCREQ(8, 512),
100+
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
101+
&write_cache_variants,
102+
&update_cache_variants),
103+
SPINAND_HAS_QE_BIT,
104+
SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout,
105+
tc58cvg2s0h_ecc_get_status)),
106+
};
107+
108+
static int toshiba_spinand_detect(struct spinand_device *spinand)
109+
{
110+
u8 *id = spinand->id.data;
111+
int ret;
112+
113+
/*
114+
* Toshiba SPI NAND read ID needs a dummy byte,
115+
* so the first byte in id is garbage.
116+
*/
117+
if (id[1] != SPINAND_MFR_TOSHIBA)
118+
return 0;
119+
120+
ret = spinand_match_and_init(spinand, toshiba_spinand_table,
121+
ARRAY_SIZE(toshiba_spinand_table),
122+
id[2]);
123+
if (ret)
124+
return ret;
125+
126+
return 1;
127+
}
128+
129+
static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
130+
.detect = toshiba_spinand_detect,
131+
};
132+
133+
const struct spinand_manufacturer toshiba_spinand_manufacturer = {
134+
.id = SPINAND_MFR_TOSHIBA,
135+
.name = "Toshiba",
136+
.ops = &toshiba_spinand_manuf_ops,
137+
};

include/linux/mtd/spinand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ struct spinand_manufacturer {
196196
/* SPI NAND manufacturers */
197197
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
198198
extern const struct spinand_manufacturer micron_spinand_manufacturer;
199+
extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
199200
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
200201

201202
/**

0 commit comments

Comments
 (0)