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 10293db446aSBoris Brezillon static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) 10393db446aSBoris Brezillon { 10493db446aSBoris Brezillon struct mtd_oob_ops ops; 10593db446aSBoris Brezillon struct sm_oob oob; 10693db446aSBoris Brezillon int ret; 10793db446aSBoris Brezillon 10893db446aSBoris Brezillon memset(&oob, -1, SM_OOB_SIZE); 10993db446aSBoris Brezillon oob.block_status = 0x0F; 11093db446aSBoris Brezillon 11193db446aSBoris Brezillon /* As long as this function is called on erase block boundaries 11293db446aSBoris Brezillon it will work correctly for 256 byte nand */ 11393db446aSBoris Brezillon ops.mode = MTD_OPS_PLACE_OOB; 11493db446aSBoris Brezillon ops.ooboffs = 0; 11593db446aSBoris Brezillon ops.ooblen = mtd->oobsize; 11693db446aSBoris Brezillon ops.oobbuf = (void *)&oob; 11793db446aSBoris Brezillon ops.datbuf = NULL; 11893db446aSBoris Brezillon 11993db446aSBoris Brezillon 12093db446aSBoris Brezillon ret = mtd_write_oob(mtd, ofs, &ops); 12193db446aSBoris Brezillon if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { 12263fa37f0SShreeya Patel pr_notice("sm_common: can't mark sector at %i as bad\n", 12393db446aSBoris Brezillon (int)ofs); 12493db446aSBoris Brezillon return -EIO; 12593db446aSBoris Brezillon } 12693db446aSBoris Brezillon 12793db446aSBoris Brezillon return 0; 12893db446aSBoris Brezillon } 12993db446aSBoris Brezillon 13093db446aSBoris Brezillon static struct nand_flash_dev nand_smartmedia_flash_ids[] = { 13193db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM), 13293db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0), 13393db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0), 13493db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0), 13593db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM), 13693db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0), 13793db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM), 13893db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0), 13993db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM), 14093db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0), 14193db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM), 14293db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0), 14393db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM), 14493db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0), 14593db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM), 14693db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0), 14793db446aSBoris Brezillon LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM), 14893db446aSBoris Brezillon {NULL} 14993db446aSBoris Brezillon }; 15093db446aSBoris Brezillon 15193db446aSBoris Brezillon static struct nand_flash_dev nand_xd_flash_ids[] = { 15293db446aSBoris Brezillon LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0), 15393db446aSBoris Brezillon LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0), 15493db446aSBoris Brezillon LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0), 15593db446aSBoris Brezillon LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0), 15693db446aSBoris Brezillon LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD), 15793db446aSBoris Brezillon LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD), 15893db446aSBoris Brezillon LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD), 15993db446aSBoris Brezillon LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD), 16093db446aSBoris Brezillon {NULL} 16193db446aSBoris Brezillon }; 16293db446aSBoris Brezillon 16393db446aSBoris Brezillon int sm_register_device(struct mtd_info *mtd, int smartmedia) 16493db446aSBoris Brezillon { 16593db446aSBoris Brezillon struct nand_chip *chip = mtd_to_nand(mtd); 16693db446aSBoris Brezillon int ret; 16793db446aSBoris Brezillon 16893db446aSBoris Brezillon chip->options |= NAND_SKIP_BBTSCAN; 16993db446aSBoris Brezillon 17093db446aSBoris Brezillon /* Scan for card properties */ 17193db446aSBoris Brezillon ret = nand_scan_ident(mtd, 1, smartmedia ? 17293db446aSBoris Brezillon nand_smartmedia_flash_ids : nand_xd_flash_ids); 17393db446aSBoris Brezillon 17493db446aSBoris Brezillon if (ret) 17593db446aSBoris Brezillon return ret; 17693db446aSBoris Brezillon 17793db446aSBoris Brezillon /* Bad block marker position */ 17893db446aSBoris Brezillon chip->badblockpos = 0x05; 17993db446aSBoris Brezillon chip->badblockbits = 7; 18093db446aSBoris Brezillon chip->block_markbad = sm_block_markbad; 18193db446aSBoris Brezillon 18293db446aSBoris Brezillon /* ECC layout */ 18393db446aSBoris Brezillon if (mtd->writesize == SM_SECTOR_SIZE) 18493db446aSBoris Brezillon mtd_set_ooblayout(mtd, &oob_sm_ops); 18593db446aSBoris Brezillon else if (mtd->writesize == SM_SMALL_PAGE) 18693db446aSBoris Brezillon mtd_set_ooblayout(mtd, &oob_sm_small_ops); 18793db446aSBoris Brezillon else 18893db446aSBoris Brezillon return -ENODEV; 18993db446aSBoris Brezillon 19093db446aSBoris Brezillon ret = nand_scan_tail(mtd); 19193db446aSBoris Brezillon 19293db446aSBoris Brezillon if (ret) 19393db446aSBoris Brezillon return ret; 19493db446aSBoris Brezillon 19592aa292dSMiquel Raynal ret = mtd_device_register(mtd, NULL, 0); 19692aa292dSMiquel Raynal if (ret) 19792aa292dSMiquel Raynal nand_cleanup(chip); 19892aa292dSMiquel Raynal 19992aa292dSMiquel Raynal return ret; 20093db446aSBoris Brezillon } 20193db446aSBoris Brezillon EXPORT_SYMBOL_GPL(sm_register_device); 20293db446aSBoris Brezillon 20393db446aSBoris Brezillon MODULE_LICENSE("GPL"); 20493db446aSBoris Brezillon MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); 20593db446aSBoris Brezillon MODULE_DESCRIPTION("Common SmartMedia/xD functions"); 206