linux/drivers/mtd/nand/raw/sm_common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright © 2009 - Maxim Levitsky
   4 * Common routines & support for xD format
   5 */
   6#include <linux/kernel.h>
   7#include <linux/mtd/rawnand.h>
   8#include <linux/module.h>
   9#include <linux/sizes.h>
  10#include "sm_common.h"
  11
  12static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section,
  13                                struct mtd_oob_region *oobregion)
  14{
  15        if (section > 1)
  16                return -ERANGE;
  17
  18        oobregion->length = 3;
  19        oobregion->offset = ((section + 1) * 8) - 3;
  20
  21        return 0;
  22}
  23
  24static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section,
  25                                 struct mtd_oob_region *oobregion)
  26{
  27        switch (section) {
  28        case 0:
  29                /* reserved */
  30                oobregion->offset = 0;
  31                oobregion->length = 4;
  32                break;
  33        case 1:
  34                /* LBA1 */
  35                oobregion->offset = 6;
  36                oobregion->length = 2;
  37                break;
  38        case 2:
  39                /* LBA2 */
  40                oobregion->offset = 11;
  41                oobregion->length = 2;
  42                break;
  43        default:
  44                return -ERANGE;
  45        }
  46
  47        return 0;
  48}
  49
  50static const struct mtd_ooblayout_ops oob_sm_ops = {
  51        .ecc = oob_sm_ooblayout_ecc,
  52        .free = oob_sm_ooblayout_free,
  53};
  54
  55/* NOTE: This layout is is not compatabable with SmartMedia, */
  56/* because the 256 byte devices have page depenent oob layout */
  57/* However it does preserve the bad block markers */
  58/* If you use smftl, it will bypass this and work correctly */
  59/* If you not, then you break SmartMedia compliance anyway */
  60
  61static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section,
  62                                      struct mtd_oob_region *oobregion)
  63{
  64        if (section)
  65                return -ERANGE;
  66
  67        oobregion->length = 3;
  68        oobregion->offset = 0;
  69
  70        return 0;
  71}
  72
  73static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section,
  74                                       struct mtd_oob_region *oobregion)
  75{
  76        switch (section) {
  77        case 0:
  78                /* reserved */
  79                oobregion->offset = 3;
  80                oobregion->length = 2;
  81                break;
  82        case 1:
  83                /* LBA1 */
  84                oobregion->offset = 6;
  85                oobregion->length = 2;
  86                break;
  87        default:
  88                return -ERANGE;
  89        }
  90
  91        return 0;
  92}
  93
  94static const struct mtd_ooblayout_ops oob_sm_small_ops = {
  95        .ecc = oob_sm_small_ooblayout_ecc,
  96        .free = oob_sm_small_ooblayout_free,
  97};
  98
  99static int sm_block_markbad(struct nand_chip *chip, loff_t ofs)
 100{
 101        struct mtd_info *mtd = nand_to_mtd(chip);
 102        struct mtd_oob_ops ops;
 103        struct sm_oob oob;
 104        int ret;
 105
 106        memset(&oob, -1, SM_OOB_SIZE);
 107        oob.block_status = 0x0F;
 108
 109        /* As long as this function is called on erase block boundaries
 110                it will work correctly for 256 byte nand */
 111        ops.mode = MTD_OPS_PLACE_OOB;
 112        ops.ooboffs = 0;
 113        ops.ooblen = mtd->oobsize;
 114        ops.oobbuf = (void *)&oob;
 115        ops.datbuf = NULL;
 116
 117
 118        ret = mtd_write_oob(mtd, ofs, &ops);
 119        if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
 120                pr_notice("sm_common: can't mark sector at %i as bad\n",
 121                          (int)ofs);
 122                return -EIO;
 123        }
 124
 125        return 0;
 126}
 127
 128static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
 129        LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM",   0x5d, 2,   SZ_8K, NAND_ROM),
 130        LEGACY_ID_NAND("SmartMedia 4MiB 3,3V",       0xe3, 4,   SZ_8K, 0),
 131        LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V",     0xe5, 4,   SZ_8K, 0),
 132        LEGACY_ID_NAND("SmartMedia 4MiB 5V",         0x6b, 4,   SZ_8K, 0),
 133        LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM",   0xd5, 4,   SZ_8K, NAND_ROM),
 134        LEGACY_ID_NAND("SmartMedia 8MiB 3,3V",       0xe6, 8,   SZ_8K, 0),
 135        LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM",   0xd6, 8,   SZ_8K, NAND_ROM),
 136        LEGACY_ID_NAND("SmartMedia 16MiB 3,3V",      0x73, 16,  SZ_16K, 0),
 137        LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM",  0x57, 16,  SZ_16K, NAND_ROM),
 138        LEGACY_ID_NAND("SmartMedia 32MiB 3,3V",      0x75, 32,  SZ_16K, 0),
 139        LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM",  0x58, 32,  SZ_16K, NAND_ROM),
 140        LEGACY_ID_NAND("SmartMedia 64MiB 3,3V",      0x76, 64,  SZ_16K, 0),
 141        LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM",  0xd9, 64,  SZ_16K, NAND_ROM),
 142        LEGACY_ID_NAND("SmartMedia 128MiB 3,3V",     0x79, 128, SZ_16K, 0),
 143        LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM),
 144        LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V",    0x71, 256, SZ_16K, 0),
 145        LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM),
 146        {NULL}
 147};
 148
 149static struct nand_flash_dev nand_xd_flash_ids[] = {
 150        LEGACY_ID_NAND("xD 16MiB 3,3V",  0x73, 16,   SZ_16K, 0),
 151        LEGACY_ID_NAND("xD 32MiB 3,3V",  0x75, 32,   SZ_16K, 0),
 152        LEGACY_ID_NAND("xD 64MiB 3,3V",  0x76, 64,   SZ_16K, 0),
 153        LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128,  SZ_16K, 0),
 154        LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256,  SZ_16K, NAND_BROKEN_XD),
 155        LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512,  SZ_16K, NAND_BROKEN_XD),
 156        LEGACY_ID_NAND("xD 1GiB 3,3V",   0xd3, 1024, SZ_16K, NAND_BROKEN_XD),
 157        LEGACY_ID_NAND("xD 2GiB 3,3V",   0xd5, 2048, SZ_16K, NAND_BROKEN_XD),
 158        {NULL}
 159};
 160
 161static int sm_attach_chip(struct nand_chip *chip)
 162{
 163        struct mtd_info *mtd = nand_to_mtd(chip);
 164
 165        /* Bad block marker position */
 166        chip->badblockpos = 0x05;
 167        chip->badblockbits = 7;
 168        chip->legacy.block_markbad = sm_block_markbad;
 169
 170        /* ECC layout */
 171        if (mtd->writesize == SM_SECTOR_SIZE)
 172                mtd_set_ooblayout(mtd, &oob_sm_ops);
 173        else if (mtd->writesize == SM_SMALL_PAGE)
 174                mtd_set_ooblayout(mtd, &oob_sm_small_ops);
 175        else
 176                return -ENODEV;
 177
 178        return 0;
 179}
 180
 181static const struct nand_controller_ops sm_controller_ops = {
 182        .attach_chip = sm_attach_chip,
 183};
 184
 185int sm_register_device(struct mtd_info *mtd, int smartmedia)
 186{
 187        struct nand_chip *chip = mtd_to_nand(mtd);
 188        struct nand_flash_dev *flash_ids;
 189        int ret;
 190
 191        chip->options |= NAND_SKIP_BBTSCAN;
 192
 193        /* Scan for card properties */
 194        chip->legacy.dummy_controller.ops = &sm_controller_ops;
 195        flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids;
 196        ret = nand_scan_with_ids(chip, 1, flash_ids);
 197        if (ret)
 198                return ret;
 199
 200        ret = mtd_device_register(mtd, NULL, 0);
 201        if (ret)
 202                nand_cleanup(chip);
 203
 204        return ret;
 205}
 206EXPORT_SYMBOL_GPL(sm_register_device);
 207
 208MODULE_LICENSE("GPL");
 209MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
 210MODULE_DESCRIPTION("Common SmartMedia/xD functions");
 211