80
80
#define DISABLE_STATUS_AFTER_WRITE 4
81
81
#define CW_PER_PAGE 6
82
82
#define UD_SIZE_BYTES 9
83
+ #define UD_SIZE_BYTES_MASK GENMASK(18, 9)
83
84
#define ECC_PARITY_SIZE_BYTES_RS 19
84
85
#define SPARE_SIZE_BYTES 23
86
+ #define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
85
87
#define NUM_ADDR_CYCLES 27
86
88
#define STATUS_BFR_READ 30
87
89
#define SET_RD_MODE_AFTER_STATUS 31
102
104
#define ECC_MODE 4
103
105
#define ECC_PARITY_SIZE_BYTES_BCH 8
104
106
#define ECC_NUM_DATA_BYTES 16
107
+ #define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
105
108
#define ECC_FORCE_CLK_OPEN 30
106
109
107
110
/* NAND_DEV_CMD1 bits */
@@ -431,13 +434,32 @@ struct qcom_nand_controller {
431
434
u32 cmd1 , vld ;
432
435
};
433
436
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
+
434
450
/*
435
451
* NAND chip structure
436
452
*
453
+ * @boot_partitions: array of boot partitions where offset and size of the
454
+ * boot partitions are stored
455
+ *
437
456
* @chip: base NAND chip structure
438
457
* @node: list node to add itself to host_list in
439
458
* qcom_nand_controller
440
459
*
460
+ * @nr_boot_partitions: count of the boot partitions where spare data is not
461
+ * protected by ECC
462
+ *
441
463
* @cs: chip select value for this chip
442
464
* @cw_size: the number of bytes in a single step/codeword
443
465
* of a page, consisting of all data, ecc, spare
@@ -456,14 +478,20 @@ struct qcom_nand_controller {
456
478
*
457
479
* @status: value to be returned if NAND_CMD_STATUS command
458
480
* is executed
481
+ * @codeword_fixup: keep track of the current layout used by
482
+ * the driver for read/write operation.
459
483
* @use_ecc: request the controller to use ECC for the
460
484
* upcoming read/write
461
485
* @bch_enabled: flag to tell whether BCH ECC mode is used
462
486
*/
463
487
struct qcom_nand_host {
488
+ struct qcom_nand_boot_partition * boot_partitions ;
489
+
464
490
struct nand_chip chip ;
465
491
struct list_head node ;
466
492
493
+ int nr_boot_partitions ;
494
+
467
495
int cs ;
468
496
int cw_size ;
469
497
int cw_data ;
@@ -481,6 +509,7 @@ struct qcom_nand_host {
481
509
u32 clrreadstatus ;
482
510
483
511
u8 status ;
512
+ bool codeword_fixup ;
484
513
bool use_ecc ;
485
514
bool bch_enabled ;
486
515
};
@@ -493,13 +522,15 @@ struct qcom_nand_host {
493
522
* @is_bam - whether NAND controller is using BAM
494
523
* @is_qpic - whether NAND CTRL is part of qpic IP
495
524
* @qpic_v2 - flag to indicate QPIC IP version 2
525
+ * @use_codeword_fixup - whether NAND has different layout for boot partitions
496
526
*/
497
527
struct qcom_nandc_props {
498
528
u32 ecc_modes ;
499
529
u32 dev_cmd_reg_start ;
500
530
bool is_bam ;
501
531
bool is_qpic ;
502
532
bool qpic_v2 ;
533
+ bool use_codeword_fixup ;
503
534
};
504
535
505
536
/* Frees the BAM transaction memory */
@@ -1718,7 +1749,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
1718
1749
data_size1 = mtd -> writesize - host -> cw_size * (ecc -> steps - 1 );
1719
1750
oob_size1 = host -> bbm_size ;
1720
1751
1721
- if (qcom_nandc_is_last_cw (ecc , cw )) {
1752
+ if (qcom_nandc_is_last_cw (ecc , cw ) && ! host -> codeword_fixup ) {
1722
1753
data_size2 = ecc -> size - data_size1 -
1723
1754
((ecc -> steps - 1 ) * 4 );
1724
1755
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,
1799
1830
}
1800
1831
1801
1832
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 ) {
1803
1834
data_size = ecc -> size - ((ecc -> steps - 1 ) * 4 );
1804
1835
oob_size = (ecc -> steps * 4 ) + host -> ecc_bytes_hw ;
1805
1836
} else {
@@ -1957,7 +1988,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
1957
1988
for (i = 0 ; i < ecc -> steps ; i ++ ) {
1958
1989
int data_size , oob_size ;
1959
1990
1960
- if (qcom_nandc_is_last_cw (ecc , i )) {
1991
+ if (qcom_nandc_is_last_cw (ecc , i ) && ! host -> codeword_fixup ) {
1961
1992
data_size = ecc -> size - ((ecc -> steps - 1 ) << 2 );
1962
1993
oob_size = (ecc -> steps << 2 ) + host -> ecc_bytes_hw +
1963
1994
host -> spare_bytes ;
@@ -2054,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
2054
2085
return ret ;
2055
2086
}
2056
2087
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
+
2057
2151
/* implements ecc->read_page() */
2058
2152
static int qcom_nandc_read_page (struct nand_chip * chip , uint8_t * buf ,
2059
2153
int oob_required , int page )
@@ -2062,6 +2156,9 @@ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
2062
2156
struct qcom_nand_controller * nandc = get_qcom_nand_controller (chip );
2063
2157
u8 * data_buf , * oob_buf = NULL ;
2064
2158
2159
+ if (host -> nr_boot_partitions )
2160
+ qcom_nandc_codeword_fixup (host , page );
2161
+
2065
2162
nand_read_page_op (chip , page , 0 , NULL , 0 );
2066
2163
data_buf = buf ;
2067
2164
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,
2081
2178
int cw , ret ;
2082
2179
u8 * data_buf = buf , * oob_buf = chip -> oob_poi ;
2083
2180
2181
+ if (host -> nr_boot_partitions )
2182
+ qcom_nandc_codeword_fixup (host , page );
2183
+
2084
2184
for (cw = 0 ; cw < ecc -> steps ; cw ++ ) {
2085
2185
ret = qcom_nandc_read_cw_raw (mtd , chip , data_buf , oob_buf ,
2086
2186
page , cw );
@@ -2101,6 +2201,9 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page)
2101
2201
struct qcom_nand_controller * nandc = get_qcom_nand_controller (chip );
2102
2202
struct nand_ecc_ctrl * ecc = & chip -> ecc ;
2103
2203
2204
+ if (host -> nr_boot_partitions )
2205
+ qcom_nandc_codeword_fixup (host , page );
2206
+
2104
2207
clear_read_regs (nandc );
2105
2208
clear_bam_transaction (nandc );
2106
2209
@@ -2121,6 +2224,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
2121
2224
u8 * data_buf , * oob_buf ;
2122
2225
int i , ret ;
2123
2226
2227
+ if (host -> nr_boot_partitions )
2228
+ qcom_nandc_codeword_fixup (host , page );
2229
+
2124
2230
nand_prog_page_begin_op (chip , page , 0 , NULL , 0 );
2125
2231
2126
2232
clear_read_regs (nandc );
@@ -2136,7 +2242,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
2136
2242
for (i = 0 ; i < ecc -> steps ; i ++ ) {
2137
2243
int data_size , oob_size ;
2138
2244
2139
- if (qcom_nandc_is_last_cw (ecc , i )) {
2245
+ if (qcom_nandc_is_last_cw (ecc , i ) && ! host -> codeword_fixup ) {
2140
2246
data_size = ecc -> size - ((ecc -> steps - 1 ) << 2 );
2141
2247
oob_size = (ecc -> steps << 2 ) + host -> ecc_bytes_hw +
2142
2248
host -> spare_bytes ;
@@ -2193,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
2193
2299
u8 * data_buf , * oob_buf ;
2194
2300
int i , ret ;
2195
2301
2302
+ if (host -> nr_boot_partitions )
2303
+ qcom_nandc_codeword_fixup (host , page );
2304
+
2196
2305
nand_prog_page_begin_op (chip , page , 0 , NULL , 0 );
2197
2306
clear_read_regs (nandc );
2198
2307
clear_bam_transaction (nandc );
@@ -2211,7 +2320,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
2211
2320
data_size1 = mtd -> writesize - host -> cw_size * (ecc -> steps - 1 );
2212
2321
oob_size1 = host -> bbm_size ;
2213
2322
2214
- if (qcom_nandc_is_last_cw (ecc , i )) {
2323
+ if (qcom_nandc_is_last_cw (ecc , i ) && ! host -> codeword_fixup ) {
2215
2324
data_size2 = ecc -> size - data_size1 -
2216
2325
((ecc -> steps - 1 ) << 2 );
2217
2326
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)
2271
2380
int data_size , oob_size ;
2272
2381
int ret ;
2273
2382
2383
+ if (host -> nr_boot_partitions )
2384
+ qcom_nandc_codeword_fixup (host , page );
2385
+
2274
2386
host -> use_ecc = true;
2275
2387
clear_bam_transaction (nandc );
2276
2388
@@ -2932,6 +3044,74 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
2932
3044
2933
3045
static const char * const probes [] = { "cmdlinepart" , "ofpart" , "qcomsmem" , NULL };
2934
3046
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
+
2935
3115
static int qcom_nand_host_init_and_register (struct qcom_nand_controller * nandc ,
2936
3116
struct qcom_nand_host * host ,
2937
3117
struct device_node * dn )
@@ -2989,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
2989
3169
if (ret )
2990
3170
nand_cleanup (chip );
2991
3171
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
+
2992
3180
return ret ;
2993
3181
}
2994
3182
@@ -3154,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev)
3154
3342
static const struct qcom_nandc_props ipq806x_nandc_props = {
3155
3343
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT ),
3156
3344
.is_bam = false,
3345
+ .use_codeword_fixup = true,
3157
3346
.dev_cmd_reg_start = 0x0 ,
3158
3347
};
3159
3348
0 commit comments