193db446aSBoris Brezillon /* 293db446aSBoris Brezillon * Copyright © 2009 - Maxim Levitsky 393db446aSBoris Brezillon * Common routines & support for xD format 493db446aSBoris Brezillon * 593db446aSBoris Brezillon * This program is free software; you can redistribute it and/or modify 693db446aSBoris Brezillon * it under the terms of the GNU General Public License version 2 as 793db446aSBoris Brezillon * published by the Free Software Foundation. 893db446aSBoris Brezillon */ 993db446aSBoris Brezillon #include <linux/kernel.h> 1093db446aSBoris Brezillon #include <linux/mtd/rawnand.h> 1193db446aSBoris Brezillon #include <linux/module.h> 1293db446aSBoris Brezillon #include <linux/sizes.h> 1393db446aSBoris Brezillon #include "sm_common.h" 1493db446aSBoris Brezillon 1593db446aSBoris Brezillon static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section, 1693db446aSBoris Brezillon struct mtd_oob_region *oobregion) 1793db446aSBoris Brezillon { 1893db446aSBoris Brezillon if (section > 1) 1993db446aSBoris Brezillon return -ERANGE; 2093db446aSBoris Brezillon 2193db446aSBoris Brezillon oobregion->length = 3; 2293db446aSBoris Brezillon oobregion->offset = ((section + 1) * 8) - 3; 2393db446aSBoris Brezillon 2493db446aSBoris Brezillon return 0; 2593db446aSBoris Brezillon } 2693db446aSBoris Brezillon 2793db446aSBoris Brezillon static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section, 2893db446aSBoris Brezillon struct mtd_oob_region *oobregion) 2993db446aSBoris Brezillon { 3093db446aSBoris Brezillon switch (section) { 3193db446aSBoris Brezillon case 0: 3293db446aSBoris Brezillon /* reserved */ 3393db446aSBoris Brezillon oobregion->offset = 0; 3493db446aSBoris Brezillon oobregion->length = 4; 3593db446aSBoris Brezillon break; 3693db446aSBoris Brezillon case 1: 3793db446aSBoris Brezillon /* LBA1 */ 3893db446aSBoris Brezillon oobregion->offset = 6; 3993db446aSBoris Brezillon oobregion->length = 2; 4093db446aSBoris Brezillon break; 4193db446aSBoris Brezillon case 2: 4293db446aSBoris Brezillon /* LBA2 */ 4393db446aSBoris Brezillon oobregion->offset = 11; 4493db446aSBoris Brezillon oobregion->length = 2; 4593db446aSBoris Brezillon break; 4693db446aSBoris Brezillon default: 4793db446aSBoris Brezillon return -ERANGE; 4893db446aSBoris Brezillon } 4993db446aSBoris Brezillon 5093db446aSBoris Brezillon return 0; 5193db446aSBoris Brezillon } 5293db446aSBoris Brezillon 5393db446aSBoris Brezillon static const struct mtd_ooblayout_ops oob_sm_ops = { 5493db446aSBoris Brezillon .ecc = oob_sm_ooblayout_ecc, 5593db446aSBoris Brezillon .free = oob_sm_ooblayout_free, 5693db446aSBoris Brezillon }; 5793db446aSBoris Brezillon 5893db446aSBoris Brezillon /* NOTE: This layout is is not compatabable with SmartMedia, */ 5993db446aSBoris Brezillon /* because the 256 byte devices have page depenent oob layout */ 6093db446aSBoris Brezillon /* However it does preserve the bad block markers */ 6193db446aSBoris Brezillon /* If you use smftl, it will bypass this and work correctly */ 6293db446aSBoris Brezillon /* If you not, then you break SmartMedia compliance anyway */ 6393db446aSBoris Brezillon 6493db446aSBoris Brezillon static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section, 6593db446aSBoris Brezillon struct mtd_oob_region *oobregion) 6693db446aSBoris Brezillon { 6793db446aSBoris Brezillon if (section) 6893db446aSBoris Brezillon return -ERANGE; 6993db446aSBoris Brezillon 7093db446aSBoris Brezillon oobregion->length = 3; 7193db446aSBoris Brezillon oobregion->offset = 0; 7293db446aSBoris Brezillon 7393db446aSBoris Brezillon return 0; 7493db446aSBoris Brezillon } 7593db446aSBoris Brezillon 7693db446aSBoris Brezillon static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section, 7793db446aSBoris Brezillon struct mtd_oob_region *oobregion) 7893db446aSBoris Brezillon { 7993db446aSBoris Brezillon switch (section) { 8093db446aSBoris Brezillon case 0: 8193db446aSBoris Brezillon /* reserved */ 8293db446aSBoris Brezillon oobregion->offset = 3; 8393db446aSBoris Brezillon oobregion->length = 2; 8493db446aSBoris Brezillon break; 8593db446aSBoris Brezillon case 1: 8693db446aSBoris Brezillon /* LBA1 */ 8793db446aSBoris Brezillon oobregion->offset = 6; 8893db446aSBoris Brezillon oobregion->length = 2; 8993db446aSBoris Brezillon break; 9093db446aSBoris Brezillon default: 9193db446aSBoris Brezillon return -ERANGE; 9293db446aSBoris Brezillon } 9393db446aSBoris Brezillon 9493db446aSBoris Brezillon return 0; 9593db446aSBoris Brezillon } 9693db446aSBoris Brezillon 9793db446aSBoris Brezillon static const struct mtd_ooblayout_ops oob_sm_small_ops = { 9893db446aSBoris Brezillon .ecc = oob_sm_small_ooblayout_ecc, 9993db446aSBoris Brezillon .free = oob_sm_small_ooblayout_free, 10093db446aSBoris Brezillon }; 10193db446aSBoris Brezillon 102c17556f5SBoris Brezillon static int sm_block_markbad(struct nand_chip *chip, loff_t ofs) 10393db446aSBoris Brezillon { 104c17556f5SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(chip); 10593db446aSBoris Brezillon struct mtd_oob_ops ops; 10693db446aSBoris Brezillon struct sm_oob oob; 10793db446aSBoris Brezillon int ret; 10893db446aSBoris Brezillon 10993db446aSBoris Brezillon memset(&oob, -1, SM_OOB_SIZE); 11093db446aSBoris Brezillon oob.block_status = 0x0F; 11193db446aSBoris Brezillon 11293db446aSBoris Brezillon /* As long as this function is called on erase block boundaries 11393db446aSBoris Brezillon it will work correctly for 256 byte nand */ 11493db446aSBoris Brezillon ops.mode = MTD_OPS_PLACE_OOB; 11593db446aSBoris Brezillon ops.ooboffs = 0; 11693db446aSBoris Brezillon ops.ooblen = mtd->oobsize; 11793db446aSBoris Brezillon ops.oobbuf = (void *)&oob; 11893db446aSBoris Brezillon ops.datbuf = NULL; 11993db446aSBoris Brezillon 12093db446aSBoris Brezillon 12193db446aSBoris Brezillon ret = mtd_write_oob(mtd, ofs, &ops); 12293db446aSBoris Brezillon if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { 12363fa37f0SShreeya Patel pr_notice("sm_common: can't mark sector at %i as bad\n", 12493db446aSBoris Brezillon (int)ofs); 12593db446aSBoris Brezillon return -EIO; 12693db446aSBoris Brezillon } 12793db446aSBoris Brezillon 12893db446aSBoris Brezillon return 0; 12993db446aSBoris Brezillon } 13093db446aSBoris Brezillon 13193db446aSBoris Brezillon static struct nand_flash_dev nand_smartmedia_flash_ids[] = { 13293db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM), 13393db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0), 13493db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0), 13593db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0), 13693db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM), 13793db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0), 13893db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM), 13993db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0), 14093db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM), 14193db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0), 14293db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM), 14393db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0), 14493db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM), 14593db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0), 14693db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM), 14793db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0), 14893db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM), 14993db446aSBoris Brezillon {NULL} 15093db446aSBoris Brezillon }; 15193db446aSBoris Brezillon 15293db446aSBoris Brezillon static struct nand_flash_dev nand_xd_flash_ids[] = { 15393db446aSBoris Brezillon LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0), 15493db446aSBoris Brezillon LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0), 15593db446aSBoris Brezillon LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0), 15693db446aSBoris Brezillon LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0), 15793db446aSBoris Brezillon LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD), 15893db446aSBoris Brezillon LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD), 15993db446aSBoris Brezillon LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD), 16093db446aSBoris Brezillon LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD), 16193db446aSBoris Brezillon {NULL} 16293db446aSBoris Brezillon }; 16393db446aSBoris Brezillon 164fe13ae02SMiquel Raynal static int sm_attach_chip(struct nand_chip *chip) 16593db446aSBoris Brezillon { 166fe13ae02SMiquel Raynal struct mtd_info *mtd = nand_to_mtd(chip); 16793db446aSBoris Brezillon 16893db446aSBoris Brezillon /* Bad block marker position */ 16993db446aSBoris Brezillon chip->badblockpos = 0x05; 17093db446aSBoris Brezillon chip->badblockbits = 7; 171cdc784c7SBoris Brezillon chip->legacy.block_markbad = sm_block_markbad; 17293db446aSBoris Brezillon 17393db446aSBoris Brezillon /* ECC layout */ 17493db446aSBoris Brezillon if (mtd->writesize == SM_SECTOR_SIZE) 17593db446aSBoris Brezillon mtd_set_ooblayout(mtd, &oob_sm_ops); 17693db446aSBoris Brezillon else if (mtd->writesize == SM_SMALL_PAGE) 17793db446aSBoris Brezillon mtd_set_ooblayout(mtd, &oob_sm_small_ops); 17893db446aSBoris Brezillon else 17993db446aSBoris Brezillon return -ENODEV; 18093db446aSBoris Brezillon 181fe13ae02SMiquel Raynal return 0; 182fe13ae02SMiquel Raynal } 18393db446aSBoris Brezillon 184fe13ae02SMiquel Raynal static const struct nand_controller_ops sm_controller_ops = { 185fe13ae02SMiquel Raynal .attach_chip = sm_attach_chip, 186fe13ae02SMiquel Raynal }; 187fe13ae02SMiquel Raynal 188fe13ae02SMiquel Raynal int sm_register_device(struct mtd_info *mtd, int smartmedia) 189fe13ae02SMiquel Raynal { 190fe13ae02SMiquel Raynal struct nand_chip *chip = mtd_to_nand(mtd); 191fe13ae02SMiquel Raynal struct nand_flash_dev *flash_ids; 192fe13ae02SMiquel Raynal int ret; 193fe13ae02SMiquel Raynal 194fe13ae02SMiquel Raynal chip->options |= NAND_SKIP_BBTSCAN; 195fe13ae02SMiquel Raynal 196fe13ae02SMiquel Raynal /* Scan for card properties */ 1977b6a9b28SBoris Brezillon chip->legacy.dummy_controller.ops = &sm_controller_ops; 198fe13ae02SMiquel Raynal flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids; 19900ad378fSBoris Brezillon ret = nand_scan_with_ids(chip, 1, flash_ids); 20093db446aSBoris Brezillon if (ret) 20193db446aSBoris Brezillon return ret; 20293db446aSBoris Brezillon 20392aa292dSMiquel Raynal ret = mtd_device_register(mtd, NULL, 0); 20492aa292dSMiquel Raynal if (ret) 20592aa292dSMiquel Raynal nand_cleanup(chip); 20692aa292dSMiquel Raynal 20792aa292dSMiquel Raynal return ret; 20893db446aSBoris Brezillon } 20993db446aSBoris Brezillon EXPORT_SYMBOL_GPL(sm_register_device); 21093db446aSBoris Brezillon 21193db446aSBoris Brezillon MODULE_LICENSE("GPL"); 21293db446aSBoris Brezillon MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); 21393db446aSBoris Brezillon MODULE_DESCRIPTION("Common SmartMedia/xD functions"); 214