1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Author: |
4 | * Chuanhong Guo <gch981213@gmail.com> - the main driver logic |
5 | * Martin Kurbanov <mmkurbanov@sberdevices.ru> - OOB layout |
6 | */ |
7 | |
8 | #include <linux/device.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/mtd/spinand.h> |
11 | |
12 | /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ |
13 | #define SPINAND_MFR_ESMT_C8 0xc8 |
14 | |
15 | static SPINAND_OP_VARIANTS(read_cache_variants, |
16 | SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), |
17 | SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), |
18 | SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), |
19 | SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); |
20 | |
21 | static SPINAND_OP_VARIANTS(write_cache_variants, |
22 | SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), |
23 | SPINAND_PROG_LOAD(true, 0, NULL, 0)); |
24 | |
25 | static SPINAND_OP_VARIANTS(update_cache_variants, |
26 | SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), |
27 | SPINAND_PROG_LOAD(false, 0, NULL, 0)); |
28 | |
29 | /* |
30 | * OOB spare area map (64 bytes) |
31 | * |
32 | * Bad Block Markers |
33 | * filled by HW and kernel Reserved |
34 | * | +-----------------------+-----------------------+ |
35 | * | | | | |
36 | * | | OOB free data Area |non ECC protected | |
37 | * | +-------------|-----+-----------------|-----+-----------------|-----+ |
38 | * | | | | | | | | |
39 | * +-|---|----------+--|-----|--------------+--|-----|--------------+--|-----|--------------+ |
40 | * | | | section0 | | | section1 | | | section2 | | | section3 | |
41 | * +-v-+-v-+---+----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+--v--+--v--+-----+-----+ |
42 | * | | | | | | | | | | | | | | | | | |
43 | * |0:1|2:3|4:7|8:15|16:17|18:19|20:23|24:31|32:33|34:35|36:39|40:47|48:49|50:51|52:55|56:63| |
44 | * | | | | | | | | | | | | | | | | | |
45 | * +---+---+-^-+--^-+-----+-----+--^--+--^--+-----+-----+--^--+--^--+-----+-----+--^--+--^--+ |
46 | * | | | | | | | | |
47 | * | +----------------|-----+-----------------|-----+-----------------|-----+ |
48 | * | ECC Area|(Main + Spare) - filled|by ESMT NAND HW | |
49 | * | | | | |
50 | * +---------------------+-----------------------+-----------------------+ |
51 | * OOB ECC protected Area - not used due to |
52 | * partial programming from some filesystems |
53 | * (like JFFS2 with cleanmarkers) |
54 | */ |
55 | |
56 | #define ESMT_OOB_SECTION_COUNT 4 |
57 | #define ESMT_OOB_SECTION_SIZE(nand) \ |
58 | (nanddev_per_page_oobsize(nand) / ESMT_OOB_SECTION_COUNT) |
59 | #define ESMT_OOB_FREE_SIZE(nand) \ |
60 | (ESMT_OOB_SECTION_SIZE(nand) / 2) |
61 | #define ESMT_OOB_ECC_SIZE(nand) \ |
62 | (ESMT_OOB_SECTION_SIZE(nand) - ESMT_OOB_FREE_SIZE(nand)) |
63 | #define ESMT_OOB_BBM_SIZE 2 |
64 | |
65 | static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section, |
66 | struct mtd_oob_region *region) |
67 | { |
68 | struct nand_device *nand = mtd_to_nanddev(mtd); |
69 | |
70 | if (section >= ESMT_OOB_SECTION_COUNT) |
71 | return -ERANGE; |
72 | |
73 | region->offset = section * ESMT_OOB_SECTION_SIZE(nand) + |
74 | ESMT_OOB_FREE_SIZE(nand); |
75 | region->length = ESMT_OOB_ECC_SIZE(nand); |
76 | |
77 | return 0; |
78 | } |
79 | |
80 | static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section, |
81 | struct mtd_oob_region *region) |
82 | { |
83 | struct nand_device *nand = mtd_to_nanddev(mtd); |
84 | |
85 | if (section >= ESMT_OOB_SECTION_COUNT) |
86 | return -ERANGE; |
87 | |
88 | /* |
89 | * Reserve space for bad blocks markers (section0) and |
90 | * reserved bytes (sections 1-3) |
91 | */ |
92 | region->offset = section * ESMT_OOB_SECTION_SIZE(nand) + 2; |
93 | |
94 | /* Use only 2 non-protected ECC bytes per each OOB section */ |
95 | region->length = 2; |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = { |
101 | .ecc = f50l1g41lb_ooblayout_ecc, |
102 | .free = f50l1g41lb_ooblayout_free, |
103 | }; |
104 | |
105 | static const struct spinand_info esmt_c8_spinand_table[] = { |
106 | SPINAND_INFO("F50L1G41LB" , |
107 | SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f, |
108 | 0x7f, 0x7f), |
109 | NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), |
110 | NAND_ECCREQ(1, 512), |
111 | SPINAND_INFO_OP_VARIANTS(&read_cache_variants, |
112 | &write_cache_variants, |
113 | &update_cache_variants), |
114 | 0, |
115 | SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), |
116 | SPINAND_INFO("F50D1G41LB" , |
117 | SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f, |
118 | 0x7f, 0x7f), |
119 | NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), |
120 | NAND_ECCREQ(1, 512), |
121 | SPINAND_INFO_OP_VARIANTS(&read_cache_variants, |
122 | &write_cache_variants, |
123 | &update_cache_variants), |
124 | 0, |
125 | SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), |
126 | SPINAND_INFO("F50D2G41KA" , |
127 | SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f, |
128 | 0x7f, 0x7f), |
129 | NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), |
130 | NAND_ECCREQ(8, 512), |
131 | SPINAND_INFO_OP_VARIANTS(&read_cache_variants, |
132 | &write_cache_variants, |
133 | &update_cache_variants), |
134 | 0, |
135 | SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), |
136 | }; |
137 | |
138 | static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { |
139 | }; |
140 | |
141 | const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { |
142 | .id = SPINAND_MFR_ESMT_C8, |
143 | .name = "ESMT" , |
144 | .chips = esmt_c8_spinand_table, |
145 | .nchips = ARRAY_SIZE(esmt_c8_spinand_table), |
146 | .ops = &esmt_spinand_manuf_ops, |
147 | }; |
148 | |