Merge https://gitlab.denx.de/u-boot/custodians/u-boot-spi
[platform/kernel/u-boot.git] / drivers / mtd / nand / spi / toshiba.c
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 #ifndef __UBOOT__
10 #include <malloc.h>
11 #include <linux/device.h>
12 #include <linux/kernel.h>
13 #endif
14 #include <linux/mtd/spinand.h>
15
16 #define SPINAND_MFR_TOSHIBA             0x98
17 #define TOSH_STATUS_ECC_HAS_BITFLIPS_T  (3 << 4)
18
19 static SPINAND_OP_VARIANTS(read_cache_variants,
20                 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
21                 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
22                 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
23                 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
24
25 static SPINAND_OP_VARIANTS(write_cache_variants,
26                 SPINAND_PROG_LOAD(true, 0, NULL, 0));
27
28 static SPINAND_OP_VARIANTS(update_cache_variants,
29                 SPINAND_PROG_LOAD(false, 0, NULL, 0));
30
31 static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section,
32                                      struct mtd_oob_region *region)
33 {
34         if (section > 0)
35                 return -ERANGE;
36
37         region->offset = mtd->oobsize / 2;
38         region->length = mtd->oobsize / 2;
39
40         return 0;
41 }
42
43 static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section,
44                                       struct mtd_oob_region *region)
45 {
46         if (section > 0)
47                 return -ERANGE;
48
49         /* 2 bytes reserved for BBM */
50         region->offset = 2;
51         region->length = (mtd->oobsize / 2) - 2;
52
53         return 0;
54 }
55
56 static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = {
57         .ecc = tc58cxgxsx_ooblayout_ecc,
58         .rfree = tc58cxgxsx_ooblayout_free,
59 };
60
61 static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand,
62                                       u8 status)
63 {
64         struct nand_device *nand = spinand_to_nand(spinand);
65         u8 mbf = 0;
66         struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
67
68         switch (status & STATUS_ECC_MASK) {
69         case STATUS_ECC_NO_BITFLIPS:
70                 return 0;
71
72         case STATUS_ECC_UNCOR_ERROR:
73                 return -EBADMSG;
74
75         case STATUS_ECC_HAS_BITFLIPS:
76         case TOSH_STATUS_ECC_HAS_BITFLIPS_T:
77                 /*
78                  * Let's try to retrieve the real maximum number of bitflips
79                  * in order to avoid forcing the wear-leveling layer to move
80                  * data around if it's not necessary.
81                  */
82                 if (spi_mem_exec_op(spinand->slave, &op))
83                         return nand->eccreq.strength;
84
85                 mbf >>= 4;
86
87                 if (WARN_ON(mbf > nand->eccreq.strength || !mbf))
88                         return nand->eccreq.strength;
89
90                 return mbf;
91
92         default:
93                 break;
94         }
95
96         return -EINVAL;
97 }
98
99 static const struct spinand_info toshiba_spinand_table[] = {
100         /* 3.3V 1Gb */
101         SPINAND_INFO("TC58CVG0S3", 0xC2,
102                      NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
103                      NAND_ECCREQ(8, 512),
104                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
105                                               &write_cache_variants,
106                                               &update_cache_variants),
107                      0,
108                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
109                                      tc58cxgxsx_ecc_get_status)),
110         /* 3.3V 2Gb */
111         SPINAND_INFO("TC58CVG1S3", 0xCB,
112                      NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
113                      NAND_ECCREQ(8, 512),
114                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
115                                               &write_cache_variants,
116                                               &update_cache_variants),
117                      0,
118                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
119                                      tc58cxgxsx_ecc_get_status)),
120         /* 3.3V 4Gb */
121         SPINAND_INFO("TC58CVG2S0", 0xCD,
122                      NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
123                      NAND_ECCREQ(8, 512),
124                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
125                                               &write_cache_variants,
126                                               &update_cache_variants),
127                      0,
128                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
129                                      tc58cxgxsx_ecc_get_status)),
130         /* 3.3V 4Gb */
131         SPINAND_INFO("TC58CVG2S0", 0xED,
132                      NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
133                      NAND_ECCREQ(8, 512),
134                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
135                                               &write_cache_variants,
136                                               &update_cache_variants),
137                      0,
138                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
139                                      tc58cxgxsx_ecc_get_status)),
140         /* 1.8V 1Gb */
141         SPINAND_INFO("TC58CYG0S3", 0xB2,
142                      NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
143                      NAND_ECCREQ(8, 512),
144                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
145                                               &write_cache_variants,
146                                               &update_cache_variants),
147                      0,
148                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
149                                      tc58cxgxsx_ecc_get_status)),
150         /* 1.8V 2Gb */
151         SPINAND_INFO("TC58CYG1S3", 0xBB,
152                      NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
153                      NAND_ECCREQ(8, 512),
154                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
155                                               &write_cache_variants,
156                                               &update_cache_variants),
157                      0,
158                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
159                                      tc58cxgxsx_ecc_get_status)),
160         /* 1.8V 4Gb */
161         SPINAND_INFO("TC58CYG2S0", 0xBD,
162                      NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
163                      NAND_ECCREQ(8, 512),
164                      SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
165                                               &write_cache_variants,
166                                               &update_cache_variants),
167                      0,
168                      SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
169                                      tc58cxgxsx_ecc_get_status)),
170 };
171
172 static int toshiba_spinand_detect(struct spinand_device *spinand)
173 {
174         u8 *id = spinand->id.data;
175         int ret;
176
177         /*
178          * Toshiba SPI NAND read ID needs a dummy byte,
179          * so the first byte in id is garbage.
180          */
181         if (id[1] != SPINAND_MFR_TOSHIBA)
182                 return 0;
183
184         ret = spinand_match_and_init(spinand, toshiba_spinand_table,
185                                      ARRAY_SIZE(toshiba_spinand_table),
186                                      id[2]);
187         if (ret)
188                 return ret;
189
190         return 1;
191 }
192
193 static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
194         .detect = toshiba_spinand_detect,
195 };
196
197 const struct spinand_manufacturer toshiba_spinand_manufacturer = {
198         .id = SPINAND_MFR_TOSHIBA,
199         .name = "Toshiba",
200         .ops = &toshiba_spinand_manuf_ops,
201 };