Skip to content

Commit 862bded

Browse files
Ansuelmiquelraynal
authored andcommitted
mtd: nand: raw: qcom_nandc: add support for unprotected spare data pages
IPQ8064 nand have special pages where a different layout scheme is used. These special page are used by boot partition and on reading them lots of warning are reported about wrong ECC data and if written to results in broken data and not bootable device. The layout scheme used by these special page consist in using 512 bytes as the codeword size (even for the last codeword) while writing to CFG0 register. This forces the NAND controller to unprotect the 4 bytes of spare data. Since the kernel is unaware of this different layout for these special page, it does try to protect the spare data too during read/write and warn about CRC errors. Add support for this by permitting the user to declare these special pages in dts by declaring offset and size of the partition. The driver internally will convert these value to nand pages. On user read/write the page is checked and if it's a boot page the correct layout is used. Signed-off-by: Christian Marangi <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]> Signed-off-by: Miquel Raynal <[email protected]> Link: https://lore.kernel.org/linux-mtd/[email protected]
1 parent b360514 commit 862bded

File tree

1 file changed

+194
-5
lines changed

1 file changed

+194
-5
lines changed

drivers/mtd/nand/raw/qcom_nandc.c

Lines changed: 194 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,10 @@
8080
#define DISABLE_STATUS_AFTER_WRITE 4
8181
#define CW_PER_PAGE 6
8282
#define UD_SIZE_BYTES 9
83+
#define UD_SIZE_BYTES_MASK GENMASK(18, 9)
8384
#define ECC_PARITY_SIZE_BYTES_RS 19
8485
#define SPARE_SIZE_BYTES 23
86+
#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
8587
#define NUM_ADDR_CYCLES 27
8688
#define STATUS_BFR_READ 30
8789
#define SET_RD_MODE_AFTER_STATUS 31
@@ -102,6 +104,7 @@
102104
#define ECC_MODE 4
103105
#define ECC_PARITY_SIZE_BYTES_BCH 8
104106
#define ECC_NUM_DATA_BYTES 16
107+
#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
105108
#define ECC_FORCE_CLK_OPEN 30
106109

107110
/* NAND_DEV_CMD1 bits */
@@ -431,13 +434,32 @@ struct qcom_nand_controller {
431434
u32 cmd1, vld;
432435
};
433436

437+
/*
438+
* NAND special boot partitions
439+
*
440+
* @page_offset: offset of the partition where spare data is not protected
441+
* by ECC (value in pages)
442+
* @page_offset: size of the partition where spare data is not protected
443+
* by ECC (value in pages)
444+
*/
445+
struct qcom_nand_boot_partition {
446+
u32 page_offset;
447+
u32 page_size;
448+
};
449+
434450
/*
435451
* NAND chip structure
436452
*
453+
* @boot_partitions: array of boot partitions where offset and size of the
454+
* boot partitions are stored
455+
*
437456
* @chip: base NAND chip structure
438457
* @node: list node to add itself to host_list in
439458
* qcom_nand_controller
440459
*
460+
* @nr_boot_partitions: count of the boot partitions where spare data is not
461+
* protected by ECC
462+
*
441463
* @cs: chip select value for this chip
442464
* @cw_size: the number of bytes in a single step/codeword
443465
* of a page, consisting of all data, ecc, spare
@@ -456,14 +478,20 @@ struct qcom_nand_controller {
456478
*
457479
* @status: value to be returned if NAND_CMD_STATUS command
458480
* is executed
481+
* @codeword_fixup: keep track of the current layout used by
482+
* the driver for read/write operation.
459483
* @use_ecc: request the controller to use ECC for the
460484
* upcoming read/write
461485
* @bch_enabled: flag to tell whether BCH ECC mode is used
462486
*/
463487
struct qcom_nand_host {
488+
struct qcom_nand_boot_partition *boot_partitions;
489+
464490
struct nand_chip chip;
465491
struct list_head node;
466492

493+
int nr_boot_partitions;
494+
467495
int cs;
468496
int cw_size;
469497
int cw_data;
@@ -481,6 +509,7 @@ struct qcom_nand_host {
481509
u32 clrreadstatus;
482510

483511
u8 status;
512+
bool codeword_fixup;
484513
bool use_ecc;
485514
bool bch_enabled;
486515
};
@@ -493,13 +522,15 @@ struct qcom_nand_host {
493522
* @is_bam - whether NAND controller is using BAM
494523
* @is_qpic - whether NAND CTRL is part of qpic IP
495524
* @qpic_v2 - flag to indicate QPIC IP version 2
525+
* @use_codeword_fixup - whether NAND has different layout for boot partitions
496526
*/
497527
struct qcom_nandc_props {
498528
u32 ecc_modes;
499529
u32 dev_cmd_reg_start;
500530
bool is_bam;
501531
bool is_qpic;
502532
bool qpic_v2;
533+
bool use_codeword_fixup;
503534
};
504535

505536
/* Frees the BAM transaction memory */
@@ -1718,7 +1749,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
17181749
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
17191750
oob_size1 = host->bbm_size;
17201751

1721-
if (qcom_nandc_is_last_cw(ecc, cw)) {
1752+
if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
17221753
data_size2 = ecc->size - data_size1 -
17231754
((ecc->steps - 1) * 4);
17241755
oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
@@ -1799,7 +1830,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
17991830
}
18001831

18011832
for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
1802-
if (qcom_nandc_is_last_cw(ecc, cw)) {
1833+
if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
18031834
data_size = ecc->size - ((ecc->steps - 1) * 4);
18041835
oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
18051836
} else {
@@ -1957,7 +1988,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
19571988
for (i = 0; i < ecc->steps; i++) {
19581989
int data_size, oob_size;
19591990

1960-
if (qcom_nandc_is_last_cw(ecc, i)) {
1991+
if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
19611992
data_size = ecc->size - ((ecc->steps - 1) << 2);
19621993
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
19631994
host->spare_bytes;
@@ -2054,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
20542085
return ret;
20552086
}
20562087

2088+
static bool qcom_nandc_is_boot_partition(struct qcom_nand_host *host, int page)
2089+
{
2090+
struct qcom_nand_boot_partition *boot_partition;
2091+
u32 start, end;
2092+
int i;
2093+
2094+
/*
2095+
* Since the frequent access will be to the non-boot partitions like rootfs,
2096+
* optimize the page check by:
2097+
*
2098+
* 1. Checking if the page lies after the last boot partition.
2099+
* 2. Checking from the boot partition end.
2100+
*/
2101+
2102+
/* First check the last boot partition */
2103+
boot_partition = &host->boot_partitions[host->nr_boot_partitions - 1];
2104+
start = boot_partition->page_offset;
2105+
end = start + boot_partition->page_size;
2106+
2107+
/* Page is after the last boot partition end. This is NOT a boot partition */
2108+
if (page > end)
2109+
return false;
2110+
2111+
/* Actually check if it's a boot partition */
2112+
if (page < end && page >= start)
2113+
return true;
2114+
2115+
/* Check the other boot partitions starting from the second-last partition */
2116+
for (i = host->nr_boot_partitions - 2; i >= 0; i--) {
2117+
boot_partition = &host->boot_partitions[i];
2118+
start = boot_partition->page_offset;
2119+
end = start + boot_partition->page_size;
2120+
2121+
if (page < end && page >= start)
2122+
return true;
2123+
}
2124+
2125+
return false;
2126+
}
2127+
2128+
static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page)
2129+
{
2130+
bool codeword_fixup = qcom_nandc_is_boot_partition(host, page);
2131+
2132+
/* Skip conf write if we are already in the correct mode */
2133+
if (codeword_fixup == host->codeword_fixup)
2134+
return;
2135+
2136+
host->codeword_fixup = codeword_fixup;
2137+
2138+
host->cw_data = codeword_fixup ? 512 : 516;
2139+
host->spare_bytes = host->cw_size - host->ecc_bytes_hw -
2140+
host->bbm_size - host->cw_data;
2141+
2142+
host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK);
2143+
host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES |
2144+
host->cw_data << UD_SIZE_BYTES;
2145+
2146+
host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK;
2147+
host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES;
2148+
host->ecc_buf_cfg = (host->cw_data - 1) << NUM_STEPS;
2149+
}
2150+
20572151
/* implements ecc->read_page() */
20582152
static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
20592153
int oob_required, int page)
@@ -2062,6 +2156,9 @@ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
20622156
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
20632157
u8 *data_buf, *oob_buf = NULL;
20642158

2159+
if (host->nr_boot_partitions)
2160+
qcom_nandc_codeword_fixup(host, page);
2161+
20652162
nand_read_page_op(chip, page, 0, NULL, 0);
20662163
data_buf = buf;
20672164
oob_buf = oob_required ? chip->oob_poi : NULL;
@@ -2081,6 +2178,9 @@ static int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
20812178
int cw, ret;
20822179
u8 *data_buf = buf, *oob_buf = chip->oob_poi;
20832180

2181+
if (host->nr_boot_partitions)
2182+
qcom_nandc_codeword_fixup(host, page);
2183+
20842184
for (cw = 0; cw < ecc->steps; cw++) {
20852185
ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
20862186
page, cw);
@@ -2101,6 +2201,9 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page)
21012201
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
21022202
struct nand_ecc_ctrl *ecc = &chip->ecc;
21032203

2204+
if (host->nr_boot_partitions)
2205+
qcom_nandc_codeword_fixup(host, page);
2206+
21042207
clear_read_regs(nandc);
21052208
clear_bam_transaction(nandc);
21062209

@@ -2121,6 +2224,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
21212224
u8 *data_buf, *oob_buf;
21222225
int i, ret;
21232226

2227+
if (host->nr_boot_partitions)
2228+
qcom_nandc_codeword_fixup(host, page);
2229+
21242230
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
21252231

21262232
clear_read_regs(nandc);
@@ -2136,7 +2242,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
21362242
for (i = 0; i < ecc->steps; i++) {
21372243
int data_size, oob_size;
21382244

2139-
if (qcom_nandc_is_last_cw(ecc, i)) {
2245+
if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
21402246
data_size = ecc->size - ((ecc->steps - 1) << 2);
21412247
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
21422248
host->spare_bytes;
@@ -2193,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
21932299
u8 *data_buf, *oob_buf;
21942300
int i, ret;
21952301

2302+
if (host->nr_boot_partitions)
2303+
qcom_nandc_codeword_fixup(host, page);
2304+
21962305
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
21972306
clear_read_regs(nandc);
21982307
clear_bam_transaction(nandc);
@@ -2211,7 +2320,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
22112320
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
22122321
oob_size1 = host->bbm_size;
22132322

2214-
if (qcom_nandc_is_last_cw(ecc, i)) {
2323+
if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
22152324
data_size2 = ecc->size - data_size1 -
22162325
((ecc->steps - 1) << 2);
22172326
oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
@@ -2271,6 +2380,9 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page)
22712380
int data_size, oob_size;
22722381
int ret;
22732382

2383+
if (host->nr_boot_partitions)
2384+
qcom_nandc_codeword_fixup(host, page);
2385+
22742386
host->use_ecc = true;
22752387
clear_bam_transaction(nandc);
22762388

@@ -2932,6 +3044,74 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
29323044

29333045
static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL };
29343046

3047+
static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nandc,
3048+
struct qcom_nand_host *host,
3049+
struct device_node *dn)
3050+
{
3051+
struct nand_chip *chip = &host->chip;
3052+
struct mtd_info *mtd = nand_to_mtd(chip);
3053+
struct qcom_nand_boot_partition *boot_partition;
3054+
struct device *dev = nandc->dev;
3055+
int partitions_count, i, j, ret;
3056+
3057+
if (!of_find_property(dn, "qcom,boot-partitions", NULL))
3058+
return 0;
3059+
3060+
partitions_count = of_property_count_u32_elems(dn, "qcom,boot-partitions");
3061+
if (partitions_count <= 0) {
3062+
dev_err(dev, "Error parsing boot partition\n");
3063+
return partitions_count ? partitions_count : -EINVAL;
3064+
}
3065+
3066+
host->nr_boot_partitions = partitions_count / 2;
3067+
host->boot_partitions = devm_kcalloc(dev, host->nr_boot_partitions,
3068+
sizeof(*host->boot_partitions), GFP_KERNEL);
3069+
if (!host->boot_partitions) {
3070+
host->nr_boot_partitions = 0;
3071+
return -ENOMEM;
3072+
}
3073+
3074+
for (i = 0, j = 0; i < host->nr_boot_partitions; i++, j += 2) {
3075+
boot_partition = &host->boot_partitions[i];
3076+
3077+
ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j,
3078+
&boot_partition->page_offset);
3079+
if (ret) {
3080+
dev_err(dev, "Error parsing boot partition offset at index %d\n", i);
3081+
host->nr_boot_partitions = 0;
3082+
return ret;
3083+
}
3084+
3085+
if (boot_partition->page_offset % mtd->writesize) {
3086+
dev_err(dev, "Boot partition offset not multiple of writesize at index %i\n",
3087+
i);
3088+
host->nr_boot_partitions = 0;
3089+
return -EINVAL;
3090+
}
3091+
/* Convert offset to nand pages */
3092+
boot_partition->page_offset /= mtd->writesize;
3093+
3094+
ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j + 1,
3095+
&boot_partition->page_size);
3096+
if (ret) {
3097+
dev_err(dev, "Error parsing boot partition size at index %d\n", i);
3098+
host->nr_boot_partitions = 0;
3099+
return ret;
3100+
}
3101+
3102+
if (boot_partition->page_size % mtd->writesize) {
3103+
dev_err(dev, "Boot partition size not multiple of writesize at index %i\n",
3104+
i);
3105+
host->nr_boot_partitions = 0;
3106+
return -EINVAL;
3107+
}
3108+
/* Convert size to nand pages */
3109+
boot_partition->page_size /= mtd->writesize;
3110+
}
3111+
3112+
return 0;
3113+
}
3114+
29353115
static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
29363116
struct qcom_nand_host *host,
29373117
struct device_node *dn)
@@ -2989,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
29893169
if (ret)
29903170
nand_cleanup(chip);
29913171

3172+
if (nandc->props->use_codeword_fixup) {
3173+
ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn);
3174+
if (ret) {
3175+
nand_cleanup(chip);
3176+
return ret;
3177+
}
3178+
}
3179+
29923180
return ret;
29933181
}
29943182

@@ -3154,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev)
31543342
static const struct qcom_nandc_props ipq806x_nandc_props = {
31553343
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
31563344
.is_bam = false,
3345+
.use_codeword_fixup = true,
31573346
.dev_cmd_reg_start = 0x0,
31583347
};
31593348

0 commit comments

Comments
 (0)