ipq806x: 6.1: copy patches, files and config from 5.15
[openwrt/staging/ldir.git] / target / linux / ipq806x / patches-6.1 / 116-v6.0-02-mtd-nand-raw-qcom_nandc-add-support-for-unprotected-.patch
1 From 862bdedd7f4b8aebf00fdb422062e64896e97809 Mon Sep 17 00:00:00 2001
2 From: Christian Marangi <ansuelsmth@gmail.com>
3 Date: Thu, 16 Jun 2022 02:18:34 +0200
4 Subject: [PATCH 2/2] mtd: nand: raw: qcom_nandc: add support for unprotected
5 spare data pages
6
7 IPQ8064 nand have special pages where a different layout scheme is used.
8 These special page are used by boot partition and on reading them
9 lots of warning are reported about wrong ECC data and if written to
10 results in broken data and not bootable device.
11
12 The layout scheme used by these special page consist in using 512 bytes
13 as the codeword size (even for the last codeword) while writing to CFG0
14 register. This forces the NAND controller to unprotect the 4 bytes of
15 spare data.
16
17 Since the kernel is unaware of this different layout for these special
18 page, it does try to protect the spare data too during read/write and
19 warn about CRC errors.
20
21 Add support for this by permitting the user to declare these special
22 pages in dts by declaring offset and size of the partition. The driver
23 internally will convert these value to nand pages.
24
25 On user read/write the page is checked and if it's a boot page the
26 correct layout is used.
27
28 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
29 Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
30 Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
31 Link: https://lore.kernel.org/linux-mtd/20220616001835.24393-3-ansuelsmth@gmail.com
32 ---
33 drivers/mtd/nand/raw/qcom_nandc.c | 199 +++++++++++++++++++++++++++++-
34 1 file changed, 194 insertions(+), 5 deletions(-)
35
36 --- a/drivers/mtd/nand/raw/qcom_nandc.c
37 +++ b/drivers/mtd/nand/raw/qcom_nandc.c
38 @@ -79,8 +79,10 @@
39 #define DISABLE_STATUS_AFTER_WRITE 4
40 #define CW_PER_PAGE 6
41 #define UD_SIZE_BYTES 9
42 +#define UD_SIZE_BYTES_MASK GENMASK(18, 9)
43 #define ECC_PARITY_SIZE_BYTES_RS 19
44 #define SPARE_SIZE_BYTES 23
45 +#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
46 #define NUM_ADDR_CYCLES 27
47 #define STATUS_BFR_READ 30
48 #define SET_RD_MODE_AFTER_STATUS 31
49 @@ -101,6 +103,7 @@
50 #define ECC_MODE 4
51 #define ECC_PARITY_SIZE_BYTES_BCH 8
52 #define ECC_NUM_DATA_BYTES 16
53 +#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
54 #define ECC_FORCE_CLK_OPEN 30
55
56 /* NAND_DEV_CMD1 bits */
57 @@ -431,12 +434,31 @@ struct qcom_nand_controller {
58 };
59
60 /*
61 + * NAND special boot partitions
62 + *
63 + * @page_offset: offset of the partition where spare data is not protected
64 + * by ECC (value in pages)
65 + * @page_offset: size of the partition where spare data is not protected
66 + * by ECC (value in pages)
67 + */
68 +struct qcom_nand_boot_partition {
69 + u32 page_offset;
70 + u32 page_size;
71 +};
72 +
73 +/*
74 * NAND chip structure
75 *
76 + * @boot_partitions: array of boot partitions where offset and size of the
77 + * boot partitions are stored
78 + *
79 * @chip: base NAND chip structure
80 * @node: list node to add itself to host_list in
81 * qcom_nand_controller
82 *
83 + * @nr_boot_partitions: count of the boot partitions where spare data is not
84 + * protected by ECC
85 + *
86 * @cs: chip select value for this chip
87 * @cw_size: the number of bytes in a single step/codeword
88 * of a page, consisting of all data, ecc, spare
89 @@ -455,14 +477,20 @@ struct qcom_nand_controller {
90 *
91 * @status: value to be returned if NAND_CMD_STATUS command
92 * is executed
93 + * @codeword_fixup: keep track of the current layout used by
94 + * the driver for read/write operation.
95 * @use_ecc: request the controller to use ECC for the
96 * upcoming read/write
97 * @bch_enabled: flag to tell whether BCH ECC mode is used
98 */
99 struct qcom_nand_host {
100 + struct qcom_nand_boot_partition *boot_partitions;
101 +
102 struct nand_chip chip;
103 struct list_head node;
104
105 + int nr_boot_partitions;
106 +
107 int cs;
108 int cw_size;
109 int cw_data;
110 @@ -480,6 +508,7 @@ struct qcom_nand_host {
111 u32 clrreadstatus;
112
113 u8 status;
114 + bool codeword_fixup;
115 bool use_ecc;
116 bool bch_enabled;
117 };
118 @@ -492,6 +521,7 @@ struct qcom_nand_host {
119 * @is_bam - whether NAND controller is using BAM
120 * @is_qpic - whether NAND CTRL is part of qpic IP
121 * @qpic_v2 - flag to indicate QPIC IP version 2
122 + * @use_codeword_fixup - whether NAND has different layout for boot partitions
123 */
124 struct qcom_nandc_props {
125 u32 ecc_modes;
126 @@ -499,6 +529,7 @@ struct qcom_nandc_props {
127 bool is_bam;
128 bool is_qpic;
129 bool qpic_v2;
130 + bool use_codeword_fixup;
131 };
132
133 /* Frees the BAM transaction memory */
134 @@ -1708,7 +1739,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *
135 data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
136 oob_size1 = host->bbm_size;
137
138 - if (qcom_nandc_is_last_cw(ecc, cw)) {
139 + if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
140 data_size2 = ecc->size - data_size1 -
141 ((ecc->steps - 1) * 4);
142 oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
143 @@ -1789,7 +1820,7 @@ check_for_erased_page(struct qcom_nand_h
144 }
145
146 for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
147 - if (qcom_nandc_is_last_cw(ecc, cw)) {
148 + if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
149 data_size = ecc->size - ((ecc->steps - 1) * 4);
150 oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
151 } else {
152 @@ -1947,7 +1978,7 @@ static int read_page_ecc(struct qcom_nan
153 for (i = 0; i < ecc->steps; i++) {
154 int data_size, oob_size;
155
156 - if (qcom_nandc_is_last_cw(ecc, i)) {
157 + if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
158 data_size = ecc->size - ((ecc->steps - 1) << 2);
159 oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
160 host->spare_bytes;
161 @@ -2044,6 +2075,69 @@ static int copy_last_cw(struct qcom_nand
162 return ret;
163 }
164
165 +static bool qcom_nandc_is_boot_partition(struct qcom_nand_host *host, int page)
166 +{
167 + struct qcom_nand_boot_partition *boot_partition;
168 + u32 start, end;
169 + int i;
170 +
171 + /*
172 + * Since the frequent access will be to the non-boot partitions like rootfs,
173 + * optimize the page check by:
174 + *
175 + * 1. Checking if the page lies after the last boot partition.
176 + * 2. Checking from the boot partition end.
177 + */
178 +
179 + /* First check the last boot partition */
180 + boot_partition = &host->boot_partitions[host->nr_boot_partitions - 1];
181 + start = boot_partition->page_offset;
182 + end = start + boot_partition->page_size;
183 +
184 + /* Page is after the last boot partition end. This is NOT a boot partition */
185 + if (page > end)
186 + return false;
187 +
188 + /* Actually check if it's a boot partition */
189 + if (page < end && page >= start)
190 + return true;
191 +
192 + /* Check the other boot partitions starting from the second-last partition */
193 + for (i = host->nr_boot_partitions - 2; i >= 0; i--) {
194 + boot_partition = &host->boot_partitions[i];
195 + start = boot_partition->page_offset;
196 + end = start + boot_partition->page_size;
197 +
198 + if (page < end && page >= start)
199 + return true;
200 + }
201 +
202 + return false;
203 +}
204 +
205 +static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page)
206 +{
207 + bool codeword_fixup = qcom_nandc_is_boot_partition(host, page);
208 +
209 + /* Skip conf write if we are already in the correct mode */
210 + if (codeword_fixup == host->codeword_fixup)
211 + return;
212 +
213 + host->codeword_fixup = codeword_fixup;
214 +
215 + host->cw_data = codeword_fixup ? 512 : 516;
216 + host->spare_bytes = host->cw_size - host->ecc_bytes_hw -
217 + host->bbm_size - host->cw_data;
218 +
219 + host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK);
220 + host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES |
221 + host->cw_data << UD_SIZE_BYTES;
222 +
223 + host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK;
224 + host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES;
225 + host->ecc_buf_cfg = (host->cw_data - 1) << NUM_STEPS;
226 +}
227 +
228 /* implements ecc->read_page() */
229 static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
230 int oob_required, int page)
231 @@ -2052,6 +2146,9 @@ static int qcom_nandc_read_page(struct n
232 struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
233 u8 *data_buf, *oob_buf = NULL;
234
235 + if (host->nr_boot_partitions)
236 + qcom_nandc_codeword_fixup(host, page);
237 +
238 nand_read_page_op(chip, page, 0, NULL, 0);
239 data_buf = buf;
240 oob_buf = oob_required ? chip->oob_poi : NULL;
241 @@ -2071,6 +2168,9 @@ static int qcom_nandc_read_page_raw(stru
242 int cw, ret;
243 u8 *data_buf = buf, *oob_buf = chip->oob_poi;
244
245 + if (host->nr_boot_partitions)
246 + qcom_nandc_codeword_fixup(host, page);
247 +
248 for (cw = 0; cw < ecc->steps; cw++) {
249 ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
250 page, cw);
251 @@ -2091,6 +2191,9 @@ static int qcom_nandc_read_oob(struct na
252 struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
253 struct nand_ecc_ctrl *ecc = &chip->ecc;
254
255 + if (host->nr_boot_partitions)
256 + qcom_nandc_codeword_fixup(host, page);
257 +
258 clear_read_regs(nandc);
259 clear_bam_transaction(nandc);
260
261 @@ -2111,6 +2214,9 @@ static int qcom_nandc_write_page(struct
262 u8 *data_buf, *oob_buf;
263 int i, ret;
264
265 + if (host->nr_boot_partitions)
266 + qcom_nandc_codeword_fixup(host, page);
267 +
268 nand_prog_page_begin_op(chip, page, 0, NULL, 0);
269
270 clear_read_regs(nandc);
271 @@ -2126,7 +2232,7 @@ static int qcom_nandc_write_page(struct
272 for (i = 0; i < ecc->steps; i++) {
273 int data_size, oob_size;
274
275 - if (qcom_nandc_is_last_cw(ecc, i)) {
276 + if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
277 data_size = ecc->size - ((ecc->steps - 1) << 2);
278 oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
279 host->spare_bytes;
280 @@ -2183,6 +2289,9 @@ static int qcom_nandc_write_page_raw(str
281 u8 *data_buf, *oob_buf;
282 int i, ret;
283
284 + if (host->nr_boot_partitions)
285 + qcom_nandc_codeword_fixup(host, page);
286 +
287 nand_prog_page_begin_op(chip, page, 0, NULL, 0);
288 clear_read_regs(nandc);
289 clear_bam_transaction(nandc);
290 @@ -2201,7 +2310,7 @@ static int qcom_nandc_write_page_raw(str
291 data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
292 oob_size1 = host->bbm_size;
293
294 - if (qcom_nandc_is_last_cw(ecc, i)) {
295 + if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
296 data_size2 = ecc->size - data_size1 -
297 ((ecc->steps - 1) << 2);
298 oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
299 @@ -2261,6 +2370,9 @@ static int qcom_nandc_write_oob(struct n
300 int data_size, oob_size;
301 int ret;
302
303 + if (host->nr_boot_partitions)
304 + qcom_nandc_codeword_fixup(host, page);
305 +
306 host->use_ecc = true;
307 clear_bam_transaction(nandc);
308
309 @@ -2922,6 +3034,74 @@ static int qcom_nandc_setup(struct qcom_
310
311 static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL };
312
313 +static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nandc,
314 + struct qcom_nand_host *host,
315 + struct device_node *dn)
316 +{
317 + struct nand_chip *chip = &host->chip;
318 + struct mtd_info *mtd = nand_to_mtd(chip);
319 + struct qcom_nand_boot_partition *boot_partition;
320 + struct device *dev = nandc->dev;
321 + int partitions_count, i, j, ret;
322 +
323 + if (!of_find_property(dn, "qcom,boot-partitions", NULL))
324 + return 0;
325 +
326 + partitions_count = of_property_count_u32_elems(dn, "qcom,boot-partitions");
327 + if (partitions_count <= 0) {
328 + dev_err(dev, "Error parsing boot partition\n");
329 + return partitions_count ? partitions_count : -EINVAL;
330 + }
331 +
332 + host->nr_boot_partitions = partitions_count / 2;
333 + host->boot_partitions = devm_kcalloc(dev, host->nr_boot_partitions,
334 + sizeof(*host->boot_partitions), GFP_KERNEL);
335 + if (!host->boot_partitions) {
336 + host->nr_boot_partitions = 0;
337 + return -ENOMEM;
338 + }
339 +
340 + for (i = 0, j = 0; i < host->nr_boot_partitions; i++, j += 2) {
341 + boot_partition = &host->boot_partitions[i];
342 +
343 + ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j,
344 + &boot_partition->page_offset);
345 + if (ret) {
346 + dev_err(dev, "Error parsing boot partition offset at index %d\n", i);
347 + host->nr_boot_partitions = 0;
348 + return ret;
349 + }
350 +
351 + if (boot_partition->page_offset % mtd->writesize) {
352 + dev_err(dev, "Boot partition offset not multiple of writesize at index %i\n",
353 + i);
354 + host->nr_boot_partitions = 0;
355 + return -EINVAL;
356 + }
357 + /* Convert offset to nand pages */
358 + boot_partition->page_offset /= mtd->writesize;
359 +
360 + ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j + 1,
361 + &boot_partition->page_size);
362 + if (ret) {
363 + dev_err(dev, "Error parsing boot partition size at index %d\n", i);
364 + host->nr_boot_partitions = 0;
365 + return ret;
366 + }
367 +
368 + if (boot_partition->page_size % mtd->writesize) {
369 + dev_err(dev, "Boot partition size not multiple of writesize at index %i\n",
370 + i);
371 + host->nr_boot_partitions = 0;
372 + return -EINVAL;
373 + }
374 + /* Convert size to nand pages */
375 + boot_partition->page_size /= mtd->writesize;
376 + }
377 +
378 + return 0;
379 +}
380 +
381 static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
382 struct qcom_nand_host *host,
383 struct device_node *dn)
384 @@ -2979,6 +3159,14 @@ static int qcom_nand_host_init_and_regis
385 if (ret)
386 nand_cleanup(chip);
387
388 + if (nandc->props->use_codeword_fixup) {
389 + ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn);
390 + if (ret) {
391 + nand_cleanup(chip);
392 + return ret;
393 + }
394 + }
395 +
396 return ret;
397 }
398
399 @@ -3144,6 +3332,7 @@ static int qcom_nandc_remove(struct plat
400 static const struct qcom_nandc_props ipq806x_nandc_props = {
401 .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
402 .is_bam = false,
403 + .use_codeword_fixup = true,
404 .dev_cmd_reg_start = 0x0,
405 };
406