root/drivers/mtd/nand/raw/nand_onfi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. onfi_crc16
  2. nand_flash_detect_ext_param_page
  3. nand_bit_wise_majority
  4. nand_onfi_detect

   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->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 }

/* [<][>][^][v][top][bottom][index][help] */