b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) |
| 4 | * 2002-2006 Thomas Gleixner (tglx@linutronix.de) |
| 5 | * |
| 6 | * Credits: |
| 7 | * David Woodhouse for adding multichip support |
| 8 | * |
| 9 | * Aleph One Ltd. and Toby Churchill Ltd. for supporting the |
| 10 | * rework for 2K page size chips |
| 11 | * |
| 12 | * This file contains all ONFI helpers. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/slab.h> |
| 16 | |
| 17 | #include "internals.h" |
| 18 | |
| 19 | u16 onfi_crc16(u16 crc, u8 const *p, size_t len) |
| 20 | { |
| 21 | int i; |
| 22 | while (len--) { |
| 23 | crc ^= *p++ << 8; |
| 24 | for (i = 0; i < 8; i++) |
| 25 | crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); |
| 26 | } |
| 27 | |
| 28 | return crc; |
| 29 | } |
| 30 | |
| 31 | /* Parse the Extended Parameter Page. */ |
| 32 | static int nand_flash_detect_ext_param_page(struct nand_chip *chip, |
| 33 | struct nand_onfi_params *p) |
| 34 | { |
| 35 | struct onfi_ext_param_page *ep; |
| 36 | struct onfi_ext_section *s; |
| 37 | struct onfi_ext_ecc_info *ecc; |
| 38 | uint8_t *cursor; |
| 39 | int ret; |
| 40 | int len; |
| 41 | int i; |
| 42 | |
| 43 | len = le16_to_cpu(p->ext_param_page_length) * 16; |
| 44 | ep = kmalloc(len, GFP_KERNEL); |
| 45 | if (!ep) |
| 46 | return -ENOMEM; |
| 47 | |
| 48 | /* Send our own NAND_CMD_PARAM. */ |
| 49 | ret = nand_read_param_page_op(chip, 0, NULL, 0); |
| 50 | if (ret) |
| 51 | goto ext_out; |
| 52 | |
| 53 | /* Use the Change Read Column command to skip the ONFI param pages. */ |
| 54 | ret = nand_change_read_column_op(chip, |
| 55 | sizeof(*p) * p->num_of_param_pages, |
| 56 | ep, len, true); |
| 57 | if (ret) |
| 58 | goto ext_out; |
| 59 | |
| 60 | ret = -EINVAL; |
| 61 | if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) |
| 62 | != le16_to_cpu(ep->crc))) { |
| 63 | pr_debug("fail in the CRC.\n"); |
| 64 | goto ext_out; |
| 65 | } |
| 66 | |
| 67 | /* |
| 68 | * Check the signature. |
| 69 | * Do not strictly follow the ONFI spec, maybe changed in future. |
| 70 | */ |
| 71 | if (strncmp(ep->sig, "EPPS", 4)) { |
| 72 | pr_debug("The signature is invalid.\n"); |
| 73 | goto ext_out; |
| 74 | } |
| 75 | |
| 76 | /* find the ECC section. */ |
| 77 | cursor = (uint8_t *)(ep + 1); |
| 78 | for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { |
| 79 | s = ep->sections + i; |
| 80 | if (s->type == ONFI_SECTION_TYPE_2) |
| 81 | break; |
| 82 | cursor += s->length * 16; |
| 83 | } |
| 84 | if (i == ONFI_EXT_SECTION_MAX) { |
| 85 | pr_debug("We can not find the ECC section.\n"); |
| 86 | goto ext_out; |
| 87 | } |
| 88 | |
| 89 | /* get the info we want. */ |
| 90 | ecc = (struct onfi_ext_ecc_info *)cursor; |
| 91 | |
| 92 | if (!ecc->codeword_size) { |
| 93 | pr_debug("Invalid codeword size\n"); |
| 94 | goto ext_out; |
| 95 | } |
| 96 | |
| 97 | chip->base.eccreq.strength = ecc->ecc_bits; |
| 98 | chip->base.eccreq.step_size = 1 << ecc->codeword_size; |
| 99 | ret = 0; |
| 100 | |
| 101 | ext_out: |
| 102 | kfree(ep); |
| 103 | return ret; |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | * Recover data with bit-wise majority |
| 108 | */ |
| 109 | static void nand_bit_wise_majority(const void **srcbufs, |
| 110 | unsigned int nsrcbufs, |
| 111 | void *dstbuf, |
| 112 | unsigned int bufsize) |
| 113 | { |
| 114 | int i, j, k; |
| 115 | |
| 116 | for (i = 0; i < bufsize; i++) { |
| 117 | u8 val = 0; |
| 118 | |
| 119 | for (j = 0; j < 8; j++) { |
| 120 | unsigned int cnt = 0; |
| 121 | |
| 122 | for (k = 0; k < nsrcbufs; k++) { |
| 123 | const u8 *srcbuf = srcbufs[k]; |
| 124 | |
| 125 | if (srcbuf[i] & BIT(j)) |
| 126 | cnt++; |
| 127 | } |
| 128 | |
| 129 | if (cnt > nsrcbufs / 2) |
| 130 | val |= BIT(j); |
| 131 | } |
| 132 | |
| 133 | ((u8 *)dstbuf)[i] = val; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 | * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. |
| 139 | */ |
| 140 | int nand_onfi_detect(struct nand_chip *chip) |
| 141 | { |
| 142 | struct mtd_info *mtd = nand_to_mtd(chip); |
| 143 | struct nand_memory_organization *memorg; |
| 144 | struct nand_onfi_params *p; |
| 145 | struct onfi_params *onfi; |
| 146 | int onfi_version = 0; |
| 147 | char id[4]; |
| 148 | int i, ret, val; |
| 149 | |
| 150 | memorg = nanddev_get_memorg(&chip->base); |
| 151 | |
| 152 | /* Try ONFI for unknown chip or LP */ |
| 153 | ret = nand_readid_op(chip, 0x20, id, sizeof(id)); |
| 154 | if (ret || strncmp(id, "ONFI", 4)) |
| 155 | return 0; |
| 156 | |
| 157 | /* ONFI chip: allocate a buffer to hold its parameter page */ |
| 158 | p = kzalloc((sizeof(*p) * 3), GFP_KERNEL); |
| 159 | if (!p) |
| 160 | return -ENOMEM; |
| 161 | |
| 162 | ret = nand_read_param_page_op(chip, 0, NULL, 0); |
| 163 | if (ret) { |
| 164 | ret = 0; |
| 165 | goto free_onfi_param_page; |
| 166 | } |
| 167 | |
| 168 | for (i = 0; i < 3; i++) { |
| 169 | ret = nand_read_data_op(chip, &p[i], sizeof(*p), true); |
| 170 | if (ret) { |
| 171 | ret = 0; |
| 172 | goto free_onfi_param_page; |
| 173 | } |
| 174 | |
| 175 | if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) == |
| 176 | le16_to_cpu(p[i].crc)) { |
| 177 | if (i) |
| 178 | memcpy(p, &p[i], sizeof(*p)); |
| 179 | break; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | if (i == 3) { |
| 184 | const void *srcbufs[3] = {p, p + 1, p + 2}; |
| 185 | |
| 186 | pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n"); |
| 187 | nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p, |
| 188 | sizeof(*p)); |
| 189 | |
| 190 | if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) != |
| 191 | le16_to_cpu(p->crc)) { |
| 192 | pr_err("ONFI parameter recovery failed, aborting\n"); |
| 193 | goto free_onfi_param_page; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | if (chip->manufacturer.desc && chip->manufacturer.desc->ops && |
| 198 | chip->manufacturer.desc->ops->fixup_onfi_param_page) |
| 199 | chip->manufacturer.desc->ops->fixup_onfi_param_page(chip, p); |
| 200 | |
| 201 | /* Check version */ |
| 202 | val = le16_to_cpu(p->revision); |
| 203 | if (val & ONFI_VERSION_2_3) |
| 204 | onfi_version = 23; |
| 205 | else if (val & ONFI_VERSION_2_2) |
| 206 | onfi_version = 22; |
| 207 | else if (val & ONFI_VERSION_2_1) |
| 208 | onfi_version = 21; |
| 209 | else if (val & ONFI_VERSION_2_0) |
| 210 | onfi_version = 20; |
| 211 | else if (val & ONFI_VERSION_1_0) |
| 212 | onfi_version = 10; |
| 213 | |
| 214 | if (!onfi_version) { |
| 215 | pr_info("unsupported ONFI version: %d\n", val); |
| 216 | goto free_onfi_param_page; |
| 217 | } |
| 218 | |
| 219 | sanitize_string(p->manufacturer, sizeof(p->manufacturer)); |
| 220 | sanitize_string(p->model, sizeof(p->model)); |
| 221 | chip->parameters.model = kstrdup(p->model, GFP_KERNEL); |
| 222 | if (!chip->parameters.model) { |
| 223 | ret = -ENOMEM; |
| 224 | goto free_onfi_param_page; |
| 225 | } |
| 226 | |
| 227 | memorg->pagesize = le32_to_cpu(p->byte_per_page); |
| 228 | mtd->writesize = memorg->pagesize; |
| 229 | |
| 230 | /* |
| 231 | * pages_per_block and blocks_per_lun may not be a power-of-2 size |
| 232 | * (don't ask me who thought of this...). MTD assumes that these |
| 233 | * dimensions will be power-of-2, so just truncate the remaining area. |
| 234 | */ |
| 235 | memorg->pages_per_eraseblock = |
| 236 | 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); |
| 237 | mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize; |
| 238 | |
| 239 | memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page); |
| 240 | mtd->oobsize = memorg->oobsize; |
| 241 | |
| 242 | memorg->luns_per_target = p->lun_count; |
| 243 | memorg->planes_per_lun = 1 << p->interleaved_bits; |
| 244 | |
| 245 | /* See erasesize comment */ |
| 246 | memorg->eraseblocks_per_lun = |
| 247 | 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); |
| 248 | memorg->max_bad_eraseblocks_per_lun = le32_to_cpu(p->blocks_per_lun); |
| 249 | memorg->bits_per_cell = p->bits_per_cell; |
| 250 | |
| 251 | if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) |
| 252 | chip->options |= NAND_BUSWIDTH_16; |
| 253 | |
| 254 | if (p->ecc_bits != 0xff) { |
| 255 | chip->base.eccreq.strength = p->ecc_bits; |
| 256 | chip->base.eccreq.step_size = 512; |
| 257 | } else if (onfi_version >= 21 && |
| 258 | (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { |
| 259 | |
| 260 | /* |
| 261 | * The nand_flash_detect_ext_param_page() uses the |
| 262 | * Change Read Column command which maybe not supported |
| 263 | * by the chip->legacy.cmdfunc. So try to update the |
| 264 | * chip->legacy.cmdfunc now. We do not replace user supplied |
| 265 | * command function. |
| 266 | */ |
| 267 | nand_legacy_adjust_cmdfunc(chip); |
| 268 | |
| 269 | /* The Extended Parameter Page is supported since ONFI 2.1. */ |
| 270 | if (nand_flash_detect_ext_param_page(chip, p)) |
| 271 | pr_warn("Failed to detect ONFI extended param page\n"); |
| 272 | } else { |
| 273 | pr_warn("Could not retrieve ONFI ECC requirements\n"); |
| 274 | } |
| 275 | |
| 276 | /* Save some parameters from the parameter page for future use */ |
| 277 | if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) { |
| 278 | chip->parameters.supports_set_get_features = true; |
| 279 | bitmap_set(chip->parameters.get_feature_list, |
| 280 | ONFI_FEATURE_ADDR_TIMING_MODE, 1); |
| 281 | bitmap_set(chip->parameters.set_feature_list, |
| 282 | ONFI_FEATURE_ADDR_TIMING_MODE, 1); |
| 283 | } |
| 284 | |
| 285 | onfi = kzalloc(sizeof(*onfi), GFP_KERNEL); |
| 286 | if (!onfi) { |
| 287 | ret = -ENOMEM; |
| 288 | goto free_model; |
| 289 | } |
| 290 | |
| 291 | onfi->version = onfi_version; |
| 292 | onfi->tPROG = le16_to_cpu(p->t_prog); |
| 293 | onfi->tBERS = le16_to_cpu(p->t_bers); |
| 294 | onfi->tR = le16_to_cpu(p->t_r); |
| 295 | onfi->tCCS = le16_to_cpu(p->t_ccs); |
| 296 | onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode); |
| 297 | onfi->vendor_revision = le16_to_cpu(p->vendor_revision); |
| 298 | memcpy(onfi->vendor, p->vendor, sizeof(p->vendor)); |
| 299 | chip->parameters.onfi = onfi; |
| 300 | |
| 301 | /* Identification done, free the full ONFI parameter page and exit */ |
| 302 | kfree(p); |
| 303 | |
| 304 | return 1; |
| 305 | |
| 306 | free_model: |
| 307 | kfree(chip->parameters.model); |
| 308 | free_onfi_param_page: |
| 309 | kfree(p); |
| 310 | |
| 311 | return ret; |
| 312 | } |