| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2017 Free Electrons |
|---|
| 3 | 4 | * Copyright (C) 2017 NextThing Co |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Author: Boris Brezillon <boris.brezillon@free-electrons.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 9 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | | - * (at your option) any later version. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | | - * GNU General Public License for more details. |
|---|
| 16 | 7 | */ |
|---|
| 17 | 8 | |
|---|
| 18 | | -#include <linux/mtd/rawnand.h> |
|---|
| 19 | 9 | #include <linux/slab.h> |
|---|
| 10 | + |
|---|
| 11 | +#include "internals.h" |
|---|
| 20 | 12 | |
|---|
| 21 | 13 | /* |
|---|
| 22 | 14 | * Special Micron status bit 3 indicates that the block has been |
|---|
| .. | .. |
|---|
| 74 | 66 | struct micron_on_die_ecc ecc; |
|---|
| 75 | 67 | }; |
|---|
| 76 | 68 | |
|---|
| 77 | | -static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) |
|---|
| 69 | +static int micron_nand_setup_read_retry(struct nand_chip *chip, int retry_mode) |
|---|
| 78 | 70 | { |
|---|
| 79 | | - struct nand_chip *chip = mtd_to_nand(mtd); |
|---|
| 80 | 71 | u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; |
|---|
| 81 | 72 | |
|---|
| 82 | 73 | return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); |
|---|
| .. | .. |
|---|
| 93 | 84 | struct nand_onfi_vendor_micron *micron = (void *)p->onfi->vendor; |
|---|
| 94 | 85 | |
|---|
| 95 | 86 | chip->read_retries = micron->read_retry_options; |
|---|
| 96 | | - chip->setup_read_retry = micron_nand_setup_read_retry; |
|---|
| 87 | + chip->ops.setup_read_retry = micron_nand_setup_read_retry; |
|---|
| 97 | 88 | } |
|---|
| 98 | 89 | |
|---|
| 99 | 90 | if (p->supports_set_get_features) { |
|---|
| .. | .. |
|---|
| 201 | 192 | struct micron_nand *micron = nand_get_manufacturer_data(chip); |
|---|
| 202 | 193 | struct mtd_info *mtd = nand_to_mtd(chip); |
|---|
| 203 | 194 | unsigned int step, max_bitflips = 0; |
|---|
| 195 | + bool use_datain = false; |
|---|
| 204 | 196 | int ret; |
|---|
| 205 | 197 | |
|---|
| 206 | 198 | if (!(status & NAND_ECC_STATUS_WRITE_RECOMMENDED)) { |
|---|
| .. | .. |
|---|
| 220 | 212 | * in non-raw mode, even if the user did not request those bytes. |
|---|
| 221 | 213 | */ |
|---|
| 222 | 214 | if (!oob_required) { |
|---|
| 223 | | - ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, |
|---|
| 224 | | - false); |
|---|
| 215 | + /* |
|---|
| 216 | + * We first check which operation is supported by the controller |
|---|
| 217 | + * before running it. This trick makes it possible to support |
|---|
| 218 | + * all controllers, even the most constraints, without almost |
|---|
| 219 | + * any performance hit. |
|---|
| 220 | + * |
|---|
| 221 | + * TODO: could be enhanced to avoid repeating the same check |
|---|
| 222 | + * over and over in the fast path. |
|---|
| 223 | + */ |
|---|
| 224 | + if (!nand_has_exec_op(chip) || |
|---|
| 225 | + !nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, |
|---|
| 226 | + true)) |
|---|
| 227 | + use_datain = true; |
|---|
| 228 | + |
|---|
| 229 | + if (use_datain) |
|---|
| 230 | + ret = nand_read_data_op(chip, chip->oob_poi, |
|---|
| 231 | + mtd->oobsize, false, false); |
|---|
| 232 | + else |
|---|
| 233 | + ret = nand_change_read_column_op(chip, mtd->writesize, |
|---|
| 234 | + chip->oob_poi, |
|---|
| 235 | + mtd->oobsize, false); |
|---|
| 225 | 236 | if (ret) |
|---|
| 226 | 237 | return ret; |
|---|
| 227 | 238 | } |
|---|
| .. | .. |
|---|
| 290 | 301 | } |
|---|
| 291 | 302 | |
|---|
| 292 | 303 | static int |
|---|
| 293 | | -micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, |
|---|
| 294 | | - uint8_t *buf, int oob_required, |
|---|
| 295 | | - int page) |
|---|
| 304 | +micron_nand_read_page_on_die_ecc(struct nand_chip *chip, uint8_t *buf, |
|---|
| 305 | + int oob_required, int page) |
|---|
| 296 | 306 | { |
|---|
| 307 | + struct mtd_info *mtd = nand_to_mtd(chip); |
|---|
| 308 | + bool use_datain = false; |
|---|
| 297 | 309 | u8 status; |
|---|
| 298 | 310 | int ret, max_bitflips = 0; |
|---|
| 299 | 311 | |
|---|
| .. | .. |
|---|
| 309 | 321 | if (ret) |
|---|
| 310 | 322 | goto out; |
|---|
| 311 | 323 | |
|---|
| 312 | | - ret = nand_exit_status_op(chip); |
|---|
| 313 | | - if (ret) |
|---|
| 314 | | - goto out; |
|---|
| 324 | + /* |
|---|
| 325 | + * We first check which operation is supported by the controller before |
|---|
| 326 | + * running it. This trick makes it possible to support all controllers, |
|---|
| 327 | + * even the most constraints, without almost any performance hit. |
|---|
| 328 | + * |
|---|
| 329 | + * TODO: could be enhanced to avoid repeating the same check over and |
|---|
| 330 | + * over in the fast path. |
|---|
| 331 | + */ |
|---|
| 332 | + if (!nand_has_exec_op(chip) || |
|---|
| 333 | + !nand_read_data_op(chip, buf, mtd->writesize, false, true)) |
|---|
| 334 | + use_datain = true; |
|---|
| 315 | 335 | |
|---|
| 316 | | - ret = nand_read_data_op(chip, buf, mtd->writesize, false); |
|---|
| 317 | | - if (!ret && oob_required) |
|---|
| 318 | | - ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, |
|---|
| 336 | + if (use_datain) { |
|---|
| 337 | + ret = nand_exit_status_op(chip); |
|---|
| 338 | + if (ret) |
|---|
| 339 | + goto out; |
|---|
| 340 | + |
|---|
| 341 | + ret = nand_read_data_op(chip, buf, mtd->writesize, false, |
|---|
| 319 | 342 | false); |
|---|
| 343 | + if (!ret && oob_required) |
|---|
| 344 | + ret = nand_read_data_op(chip, chip->oob_poi, |
|---|
| 345 | + mtd->oobsize, false, false); |
|---|
| 346 | + } else { |
|---|
| 347 | + ret = nand_change_read_column_op(chip, 0, buf, mtd->writesize, |
|---|
| 348 | + false); |
|---|
| 349 | + if (!ret && oob_required) |
|---|
| 350 | + ret = nand_change_read_column_op(chip, mtd->writesize, |
|---|
| 351 | + chip->oob_poi, |
|---|
| 352 | + mtd->oobsize, false); |
|---|
| 353 | + } |
|---|
| 320 | 354 | |
|---|
| 321 | 355 | if (chip->ecc.strength == 4) |
|---|
| 322 | 356 | max_bitflips = micron_nand_on_die_ecc_status_4(chip, status, |
|---|
| .. | .. |
|---|
| 332 | 366 | } |
|---|
| 333 | 367 | |
|---|
| 334 | 368 | static int |
|---|
| 335 | | -micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, |
|---|
| 336 | | - const uint8_t *buf, int oob_required, |
|---|
| 337 | | - int page) |
|---|
| 369 | +micron_nand_write_page_on_die_ecc(struct nand_chip *chip, const uint8_t *buf, |
|---|
| 370 | + int oob_required, int page) |
|---|
| 338 | 371 | { |
|---|
| 339 | 372 | int ret; |
|---|
| 340 | 373 | |
|---|
| .. | .. |
|---|
| 342 | 375 | if (ret) |
|---|
| 343 | 376 | return ret; |
|---|
| 344 | 377 | |
|---|
| 345 | | - ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); |
|---|
| 378 | + ret = nand_write_page_raw(chip, buf, oob_required, page); |
|---|
| 346 | 379 | micron_nand_on_die_ecc_setup(chip, false); |
|---|
| 347 | 380 | |
|---|
| 348 | 381 | return ret; |
|---|
| .. | .. |
|---|
| 380 | 413 | */ |
|---|
| 381 | 414 | static int micron_supports_on_die_ecc(struct nand_chip *chip) |
|---|
| 382 | 415 | { |
|---|
| 416 | + const struct nand_ecc_props *requirements = |
|---|
| 417 | + nanddev_get_ecc_requirements(&chip->base); |
|---|
| 383 | 418 | u8 id[5]; |
|---|
| 384 | 419 | int ret; |
|---|
| 385 | 420 | |
|---|
| 386 | 421 | if (!chip->parameters.onfi) |
|---|
| 387 | 422 | return MICRON_ON_DIE_UNSUPPORTED; |
|---|
| 388 | 423 | |
|---|
| 389 | | - if (chip->bits_per_cell != 1) |
|---|
| 424 | + if (nanddev_bits_per_cell(&chip->base) != 1) |
|---|
| 390 | 425 | return MICRON_ON_DIE_UNSUPPORTED; |
|---|
| 391 | 426 | |
|---|
| 392 | 427 | /* |
|---|
| 393 | 428 | * We only support on-die ECC of 4/512 or 8/512 |
|---|
| 394 | 429 | */ |
|---|
| 395 | | - if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8) |
|---|
| 430 | + if (requirements->strength != 4 && requirements->strength != 8) |
|---|
| 396 | 431 | return MICRON_ON_DIE_UNSUPPORTED; |
|---|
| 397 | 432 | |
|---|
| 398 | 433 | /* 0x2 means on-die ECC is available. */ |
|---|
| .. | .. |
|---|
| 433 | 468 | /* |
|---|
| 434 | 469 | * We only support on-die ECC of 4/512 or 8/512 |
|---|
| 435 | 470 | */ |
|---|
| 436 | | - if (chip->ecc_strength_ds != 4 && chip->ecc_strength_ds != 8) |
|---|
| 471 | + if (requirements->strength != 4 && requirements->strength != 8) |
|---|
| 437 | 472 | return MICRON_ON_DIE_UNSUPPORTED; |
|---|
| 438 | 473 | |
|---|
| 439 | 474 | return MICRON_ON_DIE_SUPPORTED; |
|---|
| .. | .. |
|---|
| 441 | 476 | |
|---|
| 442 | 477 | static int micron_nand_init(struct nand_chip *chip) |
|---|
| 443 | 478 | { |
|---|
| 479 | + struct nand_device *base = &chip->base; |
|---|
| 480 | + const struct nand_ecc_props *requirements = |
|---|
| 481 | + nanddev_get_ecc_requirements(base); |
|---|
| 444 | 482 | struct mtd_info *mtd = nand_to_mtd(chip); |
|---|
| 445 | 483 | struct micron_nand *micron; |
|---|
| 446 | 484 | int ondie; |
|---|
| .. | .. |
|---|
| 456 | 494 | if (ret) |
|---|
| 457 | 495 | goto err_free_manuf_data; |
|---|
| 458 | 496 | |
|---|
| 497 | + chip->options |= NAND_BBM_FIRSTPAGE; |
|---|
| 498 | + |
|---|
| 459 | 499 | if (mtd->writesize == 2048) |
|---|
| 460 | | - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; |
|---|
| 500 | + chip->options |= NAND_BBM_SECONDPAGE; |
|---|
| 461 | 501 | |
|---|
| 462 | 502 | ondie = micron_supports_on_die_ecc(chip); |
|---|
| 463 | 503 | |
|---|
| 464 | 504 | if (ondie == MICRON_ON_DIE_MANDATORY && |
|---|
| 465 | | - chip->ecc.mode != NAND_ECC_ON_DIE) { |
|---|
| 505 | + chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_DIE) { |
|---|
| 466 | 506 | pr_err("On-die ECC forcefully enabled, not supported\n"); |
|---|
| 467 | 507 | ret = -EINVAL; |
|---|
| 468 | 508 | goto err_free_manuf_data; |
|---|
| 469 | 509 | } |
|---|
| 470 | 510 | |
|---|
| 471 | | - if (chip->ecc.mode == NAND_ECC_ON_DIE) { |
|---|
| 511 | + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE) { |
|---|
| 472 | 512 | if (ondie == MICRON_ON_DIE_UNSUPPORTED) { |
|---|
| 473 | 513 | pr_err("On-die ECC selected but not supported\n"); |
|---|
| 474 | 514 | ret = -EINVAL; |
|---|
| .. | .. |
|---|
| 488 | 528 | * That's not needed for 8-bit ECC, because the status expose |
|---|
| 489 | 529 | * a better approximation of the number of bitflips in a page. |
|---|
| 490 | 530 | */ |
|---|
| 491 | | - if (chip->ecc_strength_ds == 4) { |
|---|
| 531 | + if (requirements->strength == 4) { |
|---|
| 492 | 532 | micron->ecc.rawbuf = kmalloc(mtd->writesize + |
|---|
| 493 | 533 | mtd->oobsize, |
|---|
| 494 | 534 | GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 498 | 538 | } |
|---|
| 499 | 539 | } |
|---|
| 500 | 540 | |
|---|
| 501 | | - if (chip->ecc_strength_ds == 4) |
|---|
| 541 | + if (requirements->strength == 4) |
|---|
| 502 | 542 | mtd_set_ooblayout(mtd, |
|---|
| 503 | 543 | µn_nand_on_die_4_ooblayout_ops); |
|---|
| 504 | 544 | else |
|---|
| 505 | 545 | mtd_set_ooblayout(mtd, |
|---|
| 506 | 546 | µn_nand_on_die_8_ooblayout_ops); |
|---|
| 507 | 547 | |
|---|
| 508 | | - chip->ecc.bytes = chip->ecc_strength_ds * 2; |
|---|
| 548 | + chip->ecc.bytes = requirements->strength * 2; |
|---|
| 509 | 549 | chip->ecc.size = 512; |
|---|
| 510 | | - chip->ecc.strength = chip->ecc_strength_ds; |
|---|
| 511 | | - chip->ecc.algo = NAND_ECC_BCH; |
|---|
| 550 | + chip->ecc.strength = requirements->strength; |
|---|
| 551 | + chip->ecc.algo = NAND_ECC_ALGO_BCH; |
|---|
| 512 | 552 | chip->ecc.read_page = micron_nand_read_page_on_die_ecc; |
|---|
| 513 | 553 | chip->ecc.write_page = micron_nand_write_page_on_die_ecc; |
|---|
| 514 | 554 | |
|---|
| .. | .. |
|---|
| 516 | 556 | chip->ecc.read_page_raw = nand_read_page_raw_notsupp; |
|---|
| 517 | 557 | chip->ecc.write_page_raw = nand_write_page_raw_notsupp; |
|---|
| 518 | 558 | } else { |
|---|
| 519 | | - chip->ecc.read_page_raw = nand_read_page_raw; |
|---|
| 520 | | - chip->ecc.write_page_raw = nand_write_page_raw; |
|---|
| 559 | + if (!chip->ecc.read_page_raw) |
|---|
| 560 | + chip->ecc.read_page_raw = nand_read_page_raw; |
|---|
| 561 | + if (!chip->ecc.write_page_raw) |
|---|
| 562 | + chip->ecc.write_page_raw = nand_write_page_raw; |
|---|
| 521 | 563 | } |
|---|
| 522 | 564 | } |
|---|
| 523 | 565 | |
|---|