1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27d17c02aSMaxim Levitsky /* 37d17c02aSMaxim Levitsky * Copyright © 2009 - Maxim Levitsky 47d17c02aSMaxim Levitsky * SmartMedia/xD translation layer 57d17c02aSMaxim Levitsky */ 67d17c02aSMaxim Levitsky 77d17c02aSMaxim Levitsky #include <linux/kernel.h> 87d17c02aSMaxim Levitsky #include <linux/module.h> 97d17c02aSMaxim Levitsky #include <linux/random.h> 107d17c02aSMaxim Levitsky #include <linux/hdreg.h> 117d17c02aSMaxim Levitsky #include <linux/kthread.h> 127d17c02aSMaxim Levitsky #include <linux/freezer.h> 137d17c02aSMaxim Levitsky #include <linux/sysfs.h> 147d17c02aSMaxim Levitsky #include <linux/bitops.h> 158da552f2SStephen Rothwell #include <linux/slab.h> 16e5acf9c8SMiquel Raynal #include <linux/mtd/nand-ecc-sw-hamming.h> 1793db446aSBoris Brezillon #include "nand/raw/sm_common.h" 187d17c02aSMaxim Levitsky #include "sm_ftl.h" 197d17c02aSMaxim Levitsky 207d17c02aSMaxim Levitsky 217d17c02aSMaxim Levitsky 22582b2ffcSJingoo Han static struct workqueue_struct *cache_flush_workqueue; 237d17c02aSMaxim Levitsky 247d17c02aSMaxim Levitsky static int cache_timeout = 1000; 25f9fbcdc3SRusty Russell module_param(cache_timeout, int, S_IRUGO); 267d17c02aSMaxim Levitsky MODULE_PARM_DESC(cache_timeout, 277d17c02aSMaxim Levitsky "Timeout (in ms) for cache flush (1000 ms default"); 287d17c02aSMaxim Levitsky 297d17c02aSMaxim Levitsky static int debug; 307d17c02aSMaxim Levitsky module_param(debug, int, S_IRUGO | S_IWUSR); 317d17c02aSMaxim Levitsky MODULE_PARM_DESC(debug, "Debug level (0-2)"); 327d17c02aSMaxim Levitsky 337d17c02aSMaxim Levitsky 3492394b5cSBrian Norris /* ------------------- sysfs attributes ---------------------------------- */ 357d17c02aSMaxim Levitsky struct sm_sysfs_attribute { 367d17c02aSMaxim Levitsky struct device_attribute dev_attr; 377d17c02aSMaxim Levitsky char *data; 387d17c02aSMaxim Levitsky int len; 397d17c02aSMaxim Levitsky }; 407d17c02aSMaxim Levitsky 41582b2ffcSJingoo Han static ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, 427d17c02aSMaxim Levitsky char *buf) 437d17c02aSMaxim Levitsky { 447d17c02aSMaxim Levitsky struct sm_sysfs_attribute *sm_attr = 457d17c02aSMaxim Levitsky container_of(attr, struct sm_sysfs_attribute, dev_attr); 467d17c02aSMaxim Levitsky 477d17c02aSMaxim Levitsky strncpy(buf, sm_attr->data, sm_attr->len); 487d17c02aSMaxim Levitsky return sm_attr->len; 497d17c02aSMaxim Levitsky } 507d17c02aSMaxim Levitsky 517d17c02aSMaxim Levitsky 527d17c02aSMaxim Levitsky #define NUM_ATTRIBUTES 1 537d17c02aSMaxim Levitsky #define SM_CIS_VENDOR_OFFSET 0x59 54582b2ffcSJingoo Han static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) 557d17c02aSMaxim Levitsky { 567d17c02aSMaxim Levitsky struct attribute_group *attr_group; 577d17c02aSMaxim Levitsky struct attribute **attributes; 587d17c02aSMaxim Levitsky struct sm_sysfs_attribute *vendor_attribute; 59b4c23305SDan Carpenter char *vendor; 607d17c02aSMaxim Levitsky 61b4c23305SDan Carpenter vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, 62b4c23305SDan Carpenter SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL); 63629286b9SXiaochen Wang if (!vendor) 64629286b9SXiaochen Wang goto error1; 657d17c02aSMaxim Levitsky 667d17c02aSMaxim Levitsky /* Initialize sysfs attributes */ 677d17c02aSMaxim Levitsky vendor_attribute = 687d17c02aSMaxim Levitsky kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL); 69629286b9SXiaochen Wang if (!vendor_attribute) 70629286b9SXiaochen Wang goto error2; 717d17c02aSMaxim Levitsky 72ca7081d9SMaxim Levitsky sysfs_attr_init(&vendor_attribute->dev_attr.attr); 73ca7081d9SMaxim Levitsky 747d17c02aSMaxim Levitsky vendor_attribute->data = vendor; 75b4c23305SDan Carpenter vendor_attribute->len = strlen(vendor); 767d17c02aSMaxim Levitsky vendor_attribute->dev_attr.attr.name = "vendor"; 777d17c02aSMaxim Levitsky vendor_attribute->dev_attr.attr.mode = S_IRUGO; 787d17c02aSMaxim Levitsky vendor_attribute->dev_attr.show = sm_attr_show; 797d17c02aSMaxim Levitsky 807d17c02aSMaxim Levitsky 817d17c02aSMaxim Levitsky /* Create array of pointers to the attributes */ 826396bb22SKees Cook attributes = kcalloc(NUM_ATTRIBUTES + 1, sizeof(struct attribute *), 837d17c02aSMaxim Levitsky GFP_KERNEL); 84629286b9SXiaochen Wang if (!attributes) 85629286b9SXiaochen Wang goto error3; 867d17c02aSMaxim Levitsky attributes[0] = &vendor_attribute->dev_attr.attr; 877d17c02aSMaxim Levitsky 887d17c02aSMaxim Levitsky /* Finally create the attribute group */ 897d17c02aSMaxim Levitsky attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL); 90629286b9SXiaochen Wang if (!attr_group) 91629286b9SXiaochen Wang goto error4; 927d17c02aSMaxim Levitsky attr_group->attrs = attributes; 937d17c02aSMaxim Levitsky return attr_group; 94629286b9SXiaochen Wang error4: 95629286b9SXiaochen Wang kfree(attributes); 96629286b9SXiaochen Wang error3: 97629286b9SXiaochen Wang kfree(vendor_attribute); 98629286b9SXiaochen Wang error2: 99629286b9SXiaochen Wang kfree(vendor); 100629286b9SXiaochen Wang error1: 101629286b9SXiaochen Wang return NULL; 1027d17c02aSMaxim Levitsky } 1037d17c02aSMaxim Levitsky 104582b2ffcSJingoo Han static void sm_delete_sysfs_attributes(struct sm_ftl *ftl) 1057d17c02aSMaxim Levitsky { 1067d17c02aSMaxim Levitsky struct attribute **attributes = ftl->disk_attributes->attrs; 1077d17c02aSMaxim Levitsky int i; 1087d17c02aSMaxim Levitsky 1097d17c02aSMaxim Levitsky for (i = 0; attributes[i] ; i++) { 1107d17c02aSMaxim Levitsky 1117d17c02aSMaxim Levitsky struct device_attribute *dev_attr = container_of(attributes[i], 1127d17c02aSMaxim Levitsky struct device_attribute, attr); 1137d17c02aSMaxim Levitsky 1147d17c02aSMaxim Levitsky struct sm_sysfs_attribute *sm_attr = 1157d17c02aSMaxim Levitsky container_of(dev_attr, 1167d17c02aSMaxim Levitsky struct sm_sysfs_attribute, dev_attr); 1177d17c02aSMaxim Levitsky 1187d17c02aSMaxim Levitsky kfree(sm_attr->data); 1197d17c02aSMaxim Levitsky kfree(sm_attr); 1207d17c02aSMaxim Levitsky } 1217d17c02aSMaxim Levitsky 1227d17c02aSMaxim Levitsky kfree(ftl->disk_attributes->attrs); 1237d17c02aSMaxim Levitsky kfree(ftl->disk_attributes); 1247d17c02aSMaxim Levitsky } 1257d17c02aSMaxim Levitsky 1267d17c02aSMaxim Levitsky 1277d17c02aSMaxim Levitsky /* ----------------------- oob helpers -------------------------------------- */ 1287d17c02aSMaxim Levitsky 1297d17c02aSMaxim Levitsky static int sm_get_lba(uint8_t *lba) 1307d17c02aSMaxim Levitsky { 1317d17c02aSMaxim Levitsky /* check fixed bits */ 1327d17c02aSMaxim Levitsky if ((lba[0] & 0xF8) != 0x10) 1337d17c02aSMaxim Levitsky return -2; 1347d17c02aSMaxim Levitsky 1357854d3f7SBrian Norris /* check parity - endianness doesn't matter */ 1367d17c02aSMaxim Levitsky if (hweight16(*(uint16_t *)lba) & 1) 1377d17c02aSMaxim Levitsky return -2; 1387d17c02aSMaxim Levitsky 1397d17c02aSMaxim Levitsky return (lba[1] >> 1) | ((lba[0] & 0x07) << 7); 1407d17c02aSMaxim Levitsky } 1417d17c02aSMaxim Levitsky 1427d17c02aSMaxim Levitsky 1437d17c02aSMaxim Levitsky /* 14492394b5cSBrian Norris * Read LBA associated with block 1457d17c02aSMaxim Levitsky * returns -1, if block is erased 1467d17c02aSMaxim Levitsky * returns -2 if error happens 1477d17c02aSMaxim Levitsky */ 1487d17c02aSMaxim Levitsky static int sm_read_lba(struct sm_oob *oob) 1497d17c02aSMaxim Levitsky { 1507d17c02aSMaxim Levitsky static const uint32_t erased_pattern[4] = { 1517d17c02aSMaxim Levitsky 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; 1527d17c02aSMaxim Levitsky 1537d17c02aSMaxim Levitsky uint16_t lba_test; 1547d17c02aSMaxim Levitsky int lba; 1557d17c02aSMaxim Levitsky 1567d17c02aSMaxim Levitsky /* First test for erased block */ 1577d17c02aSMaxim Levitsky if (!memcmp(oob, erased_pattern, SM_OOB_SIZE)) 1587d17c02aSMaxim Levitsky return -1; 1597d17c02aSMaxim Levitsky 1607d17c02aSMaxim Levitsky /* Now check is both copies of the LBA differ too much */ 1617d17c02aSMaxim Levitsky lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2; 1627d17c02aSMaxim Levitsky if (lba_test && !is_power_of_2(lba_test)) 1637d17c02aSMaxim Levitsky return -2; 1647d17c02aSMaxim Levitsky 1657d17c02aSMaxim Levitsky /* And read it */ 1667d17c02aSMaxim Levitsky lba = sm_get_lba(oob->lba_copy1); 1677d17c02aSMaxim Levitsky 1687d17c02aSMaxim Levitsky if (lba == -2) 1697d17c02aSMaxim Levitsky lba = sm_get_lba(oob->lba_copy2); 1707d17c02aSMaxim Levitsky 1717d17c02aSMaxim Levitsky return lba; 1727d17c02aSMaxim Levitsky } 1737d17c02aSMaxim Levitsky 1747d17c02aSMaxim Levitsky static void sm_write_lba(struct sm_oob *oob, uint16_t lba) 1757d17c02aSMaxim Levitsky { 1767d17c02aSMaxim Levitsky uint8_t tmp[2]; 1777d17c02aSMaxim Levitsky 1787d17c02aSMaxim Levitsky WARN_ON(lba >= 1000); 1797d17c02aSMaxim Levitsky 1807d17c02aSMaxim Levitsky tmp[0] = 0x10 | ((lba >> 7) & 0x07); 1817d17c02aSMaxim Levitsky tmp[1] = (lba << 1) & 0xFF; 1827d17c02aSMaxim Levitsky 1837d17c02aSMaxim Levitsky if (hweight16(*(uint16_t *)tmp) & 0x01) 1847d17c02aSMaxim Levitsky tmp[1] |= 1; 1857d17c02aSMaxim Levitsky 1867d17c02aSMaxim Levitsky oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0]; 1877d17c02aSMaxim Levitsky oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1]; 1887d17c02aSMaxim Levitsky } 1897d17c02aSMaxim Levitsky 1907d17c02aSMaxim Levitsky 1917d17c02aSMaxim Levitsky /* Make offset from parts */ 1927d17c02aSMaxim Levitsky static loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset) 1937d17c02aSMaxim Levitsky { 1947d17c02aSMaxim Levitsky WARN_ON(boffset & (SM_SECTOR_SIZE - 1)); 1957d17c02aSMaxim Levitsky WARN_ON(zone < 0 || zone >= ftl->zone_count); 1967d17c02aSMaxim Levitsky WARN_ON(block >= ftl->zone_size); 1977d17c02aSMaxim Levitsky WARN_ON(boffset >= ftl->block_size); 1987d17c02aSMaxim Levitsky 1997d17c02aSMaxim Levitsky if (block == -1) 2007d17c02aSMaxim Levitsky return -1; 2017d17c02aSMaxim Levitsky 2027d17c02aSMaxim Levitsky return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset; 2037d17c02aSMaxim Levitsky } 2047d17c02aSMaxim Levitsky 2057d17c02aSMaxim Levitsky /* Breaks offset into parts */ 2062b2462d5SNicolas Pitre static void sm_break_offset(struct sm_ftl *ftl, loff_t loffset, 2077d17c02aSMaxim Levitsky int *zone, int *block, int *boffset) 2087d17c02aSMaxim Levitsky { 2092b2462d5SNicolas Pitre u64 offset = loffset; 2107d17c02aSMaxim Levitsky *boffset = do_div(offset, ftl->block_size); 2117d17c02aSMaxim Levitsky *block = do_div(offset, ftl->max_lba); 2127d17c02aSMaxim Levitsky *zone = offset >= ftl->zone_count ? -1 : offset; 2137d17c02aSMaxim Levitsky } 2147d17c02aSMaxim Levitsky 2157d17c02aSMaxim Levitsky /* ---------------------- low level IO ------------------------------------- */ 2167d17c02aSMaxim Levitsky 2177d17c02aSMaxim Levitsky static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) 2187d17c02aSMaxim Levitsky { 21990ccf0a0SMiquel Raynal bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); 2207d17c02aSMaxim Levitsky uint8_t ecc[3]; 2217d17c02aSMaxim Levitsky 22290ccf0a0SMiquel Raynal ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order); 22390ccf0a0SMiquel Raynal if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc1, SM_SMALL_PAGE, 22490ccf0a0SMiquel Raynal sm_order) < 0) 2257d17c02aSMaxim Levitsky return -EIO; 2267d17c02aSMaxim Levitsky 2277d17c02aSMaxim Levitsky buffer += SM_SMALL_PAGE; 2287d17c02aSMaxim Levitsky 22990ccf0a0SMiquel Raynal ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order); 23090ccf0a0SMiquel Raynal if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc2, SM_SMALL_PAGE, 23190ccf0a0SMiquel Raynal sm_order) < 0) 2327d17c02aSMaxim Levitsky return -EIO; 2337d17c02aSMaxim Levitsky return 0; 2347d17c02aSMaxim Levitsky } 2357d17c02aSMaxim Levitsky 2367d17c02aSMaxim Levitsky /* Reads a sector + oob*/ 2377d17c02aSMaxim Levitsky static int sm_read_sector(struct sm_ftl *ftl, 2387d17c02aSMaxim Levitsky int zone, int block, int boffset, 2397d17c02aSMaxim Levitsky uint8_t *buffer, struct sm_oob *oob) 2407d17c02aSMaxim Levitsky { 2417d17c02aSMaxim Levitsky struct mtd_info *mtd = ftl->trans->mtd; 2427d17c02aSMaxim Levitsky struct mtd_oob_ops ops; 2437d17c02aSMaxim Levitsky struct sm_oob tmp_oob; 244133fa8c7SMaxim Levitsky int ret = -EIO; 2457d17c02aSMaxim Levitsky int try = 0; 2467d17c02aSMaxim Levitsky 2477d17c02aSMaxim Levitsky /* FTL can contain -1 entries that are by default filled with bits */ 2487d17c02aSMaxim Levitsky if (block == -1) { 249de08b5acSArnd Bergmann if (buffer) 2507d17c02aSMaxim Levitsky memset(buffer, 0xFF, SM_SECTOR_SIZE); 2517d17c02aSMaxim Levitsky return 0; 2527d17c02aSMaxim Levitsky } 2537d17c02aSMaxim Levitsky 25492394b5cSBrian Norris /* User might not need the oob, but we do for data verification */ 2557d17c02aSMaxim Levitsky if (!oob) 2567d17c02aSMaxim Levitsky oob = &tmp_oob; 2577d17c02aSMaxim Levitsky 2580612b9ddSBrian Norris ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; 2597d17c02aSMaxim Levitsky ops.ooboffs = 0; 2607d17c02aSMaxim Levitsky ops.ooblen = SM_OOB_SIZE; 2617d17c02aSMaxim Levitsky ops.oobbuf = (void *)oob; 2627d17c02aSMaxim Levitsky ops.len = SM_SECTOR_SIZE; 2637d17c02aSMaxim Levitsky ops.datbuf = buffer; 2647d17c02aSMaxim Levitsky 2657d17c02aSMaxim Levitsky again: 2667d17c02aSMaxim Levitsky if (try++) { 2677d17c02aSMaxim Levitsky /* Avoid infinite recursion on CIS reads, sm_recheck_media 268*cc9d663aSShubhankar Kuranagatti * won't help anyway 269*cc9d663aSShubhankar Kuranagatti */ 2707d17c02aSMaxim Levitsky if (zone == 0 && block == ftl->cis_block && boffset == 2717d17c02aSMaxim Levitsky ftl->cis_boffset) 2727d17c02aSMaxim Levitsky return ret; 2737d17c02aSMaxim Levitsky 2747d17c02aSMaxim Levitsky /* Test if media is stable */ 2757d17c02aSMaxim Levitsky if (try == 3 || sm_recheck_media(ftl)) 2767d17c02aSMaxim Levitsky return ret; 2777d17c02aSMaxim Levitsky } 2787d17c02aSMaxim Levitsky 27992394b5cSBrian Norris /* Unfortunately, oob read will _always_ succeed, 280*cc9d663aSShubhankar Kuranagatti * despite card removal..... 281*cc9d663aSShubhankar Kuranagatti */ 282fd2819bbSArtem Bityutskiy ret = mtd_read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); 2837d17c02aSMaxim Levitsky 2847d17c02aSMaxim Levitsky /* Test for unknown errors */ 285d57f4054SBrian Norris if (ret != 0 && !mtd_is_bitflip_or_eccerr(ret)) { 2867d17c02aSMaxim Levitsky dbg("read of block %d at zone %d, failed due to error (%d)", 2877d17c02aSMaxim Levitsky block, zone, ret); 2887d17c02aSMaxim Levitsky goto again; 2897d17c02aSMaxim Levitsky } 2907d17c02aSMaxim Levitsky 2917d17c02aSMaxim Levitsky /* Do a basic test on the oob, to guard against returned garbage */ 2927d17c02aSMaxim Levitsky if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved)) 2937d17c02aSMaxim Levitsky goto again; 2947d17c02aSMaxim Levitsky 2957d17c02aSMaxim Levitsky /* This should never happen, unless there is a bug in the mtd driver */ 2967d17c02aSMaxim Levitsky WARN_ON(ops.oobretlen != SM_OOB_SIZE); 2977d17c02aSMaxim Levitsky WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); 2987d17c02aSMaxim Levitsky 2997d17c02aSMaxim Levitsky if (!buffer) 3007d17c02aSMaxim Levitsky return 0; 3017d17c02aSMaxim Levitsky 3027d17c02aSMaxim Levitsky /* Test if sector marked as bad */ 3037d17c02aSMaxim Levitsky if (!sm_sector_valid(oob)) { 3047d17c02aSMaxim Levitsky dbg("read of block %d at zone %d, failed because it is marked" 3057d17c02aSMaxim Levitsky " as bad" , block, zone); 3067d17c02aSMaxim Levitsky goto again; 3077d17c02aSMaxim Levitsky } 3087d17c02aSMaxim Levitsky 3097d17c02aSMaxim Levitsky /* Test ECC*/ 310d57f4054SBrian Norris if (mtd_is_eccerr(ret) || 3117d17c02aSMaxim Levitsky (ftl->smallpagenand && sm_correct_sector(buffer, oob))) { 3127d17c02aSMaxim Levitsky 3137d17c02aSMaxim Levitsky dbg("read of block %d at zone %d, failed due to ECC error", 3147d17c02aSMaxim Levitsky block, zone); 3157d17c02aSMaxim Levitsky goto again; 3167d17c02aSMaxim Levitsky } 3177d17c02aSMaxim Levitsky 3187d17c02aSMaxim Levitsky return 0; 3197d17c02aSMaxim Levitsky } 3207d17c02aSMaxim Levitsky 3217d17c02aSMaxim Levitsky /* Writes a sector to media */ 3227d17c02aSMaxim Levitsky static int sm_write_sector(struct sm_ftl *ftl, 3237d17c02aSMaxim Levitsky int zone, int block, int boffset, 3247d17c02aSMaxim Levitsky uint8_t *buffer, struct sm_oob *oob) 3257d17c02aSMaxim Levitsky { 3267d17c02aSMaxim Levitsky struct mtd_oob_ops ops; 3277d17c02aSMaxim Levitsky struct mtd_info *mtd = ftl->trans->mtd; 3287d17c02aSMaxim Levitsky int ret; 3297d17c02aSMaxim Levitsky 3307d17c02aSMaxim Levitsky BUG_ON(ftl->readonly); 3317d17c02aSMaxim Levitsky 3327d17c02aSMaxim Levitsky if (zone == 0 && (block == ftl->cis_block || block == 0)) { 3337d17c02aSMaxim Levitsky dbg("attempted to write the CIS!"); 3347d17c02aSMaxim Levitsky return -EIO; 3357d17c02aSMaxim Levitsky } 3367d17c02aSMaxim Levitsky 3377d17c02aSMaxim Levitsky if (ftl->unstable) 3387d17c02aSMaxim Levitsky return -EIO; 3397d17c02aSMaxim Levitsky 3400612b9ddSBrian Norris ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; 3417d17c02aSMaxim Levitsky ops.len = SM_SECTOR_SIZE; 3427d17c02aSMaxim Levitsky ops.datbuf = buffer; 3437d17c02aSMaxim Levitsky ops.ooboffs = 0; 3447d17c02aSMaxim Levitsky ops.ooblen = SM_OOB_SIZE; 3457d17c02aSMaxim Levitsky ops.oobbuf = (void *)oob; 3467d17c02aSMaxim Levitsky 347a2cc5ba0SArtem Bityutskiy ret = mtd_write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); 3487d17c02aSMaxim Levitsky 3497d17c02aSMaxim Levitsky /* Now we assume that hardware will catch write bitflip errors */ 3507d17c02aSMaxim Levitsky 3517d17c02aSMaxim Levitsky if (ret) { 3527d17c02aSMaxim Levitsky dbg("write to block %d at zone %d, failed with error %d", 3537d17c02aSMaxim Levitsky block, zone, ret); 3547d17c02aSMaxim Levitsky 3557d17c02aSMaxim Levitsky sm_recheck_media(ftl); 3567d17c02aSMaxim Levitsky return ret; 3577d17c02aSMaxim Levitsky } 3587d17c02aSMaxim Levitsky 3597d17c02aSMaxim Levitsky /* This should never happen, unless there is a bug in the driver */ 3607d17c02aSMaxim Levitsky WARN_ON(ops.oobretlen != SM_OOB_SIZE); 3617d17c02aSMaxim Levitsky WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); 3627d17c02aSMaxim Levitsky 3637d17c02aSMaxim Levitsky return 0; 3647d17c02aSMaxim Levitsky } 3657d17c02aSMaxim Levitsky 3667d17c02aSMaxim Levitsky /* ------------------------ block IO ------------------------------------- */ 3677d17c02aSMaxim Levitsky 3687d17c02aSMaxim Levitsky /* Write a block using data and lba, and invalid sector bitmap */ 3697d17c02aSMaxim Levitsky static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf, 3707d17c02aSMaxim Levitsky int zone, int block, int lba, 3717d17c02aSMaxim Levitsky unsigned long invalid_bitmap) 3727d17c02aSMaxim Levitsky { 37390ccf0a0SMiquel Raynal bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); 3747d17c02aSMaxim Levitsky struct sm_oob oob; 3757d17c02aSMaxim Levitsky int boffset; 3767d17c02aSMaxim Levitsky int retry = 0; 3777d17c02aSMaxim Levitsky 3787d17c02aSMaxim Levitsky /* Initialize the oob with requested values */ 3797d17c02aSMaxim Levitsky memset(&oob, 0xFF, SM_OOB_SIZE); 3807d17c02aSMaxim Levitsky sm_write_lba(&oob, lba); 3817d17c02aSMaxim Levitsky restart: 3827d17c02aSMaxim Levitsky if (ftl->unstable) 3837d17c02aSMaxim Levitsky return -EIO; 3847d17c02aSMaxim Levitsky 3857d17c02aSMaxim Levitsky for (boffset = 0; boffset < ftl->block_size; 3867d17c02aSMaxim Levitsky boffset += SM_SECTOR_SIZE) { 3877d17c02aSMaxim Levitsky 3887d17c02aSMaxim Levitsky oob.data_status = 0xFF; 3897d17c02aSMaxim Levitsky 3907d17c02aSMaxim Levitsky if (test_bit(boffset / SM_SECTOR_SIZE, &invalid_bitmap)) { 3917d17c02aSMaxim Levitsky 3927d17c02aSMaxim Levitsky sm_printk("sector %d of block at LBA %d of zone %d" 393c19ca6cbSMasanari Iida " couldn't be read, marking it as invalid", 3947d17c02aSMaxim Levitsky boffset / SM_SECTOR_SIZE, lba, zone); 3957d17c02aSMaxim Levitsky 3967d17c02aSMaxim Levitsky oob.data_status = 0; 3977d17c02aSMaxim Levitsky } 3987d17c02aSMaxim Levitsky 3997d17c02aSMaxim Levitsky if (ftl->smallpagenand) { 40090ccf0a0SMiquel Raynal ecc_sw_hamming_calculate(buf + boffset, 40190ccf0a0SMiquel Raynal SM_SMALL_PAGE, oob.ecc1, 40290ccf0a0SMiquel Raynal sm_order); 4037d17c02aSMaxim Levitsky 40490ccf0a0SMiquel Raynal ecc_sw_hamming_calculate(buf + boffset + SM_SMALL_PAGE, 405309600c1SBoris Brezillon SM_SMALL_PAGE, oob.ecc2, 40690ccf0a0SMiquel Raynal sm_order); 4077d17c02aSMaxim Levitsky } 4087d17c02aSMaxim Levitsky if (!sm_write_sector(ftl, zone, block, boffset, 4097d17c02aSMaxim Levitsky buf + boffset, &oob)) 4107d17c02aSMaxim Levitsky continue; 4117d17c02aSMaxim Levitsky 4127d17c02aSMaxim Levitsky if (!retry) { 4137d17c02aSMaxim Levitsky 4147d17c02aSMaxim Levitsky /* If write fails. try to erase the block */ 4157d17c02aSMaxim Levitsky /* This is safe, because we never write in blocks 416*cc9d663aSShubhankar Kuranagatti * that contain valuable data. 417*cc9d663aSShubhankar Kuranagatti * This is intended to repair block that are marked 418*cc9d663aSShubhankar Kuranagatti * as erased, but that isn't fully erased 419*cc9d663aSShubhankar Kuranagatti */ 4207d17c02aSMaxim Levitsky 4217d17c02aSMaxim Levitsky if (sm_erase_block(ftl, zone, block, 0)) 4227d17c02aSMaxim Levitsky return -EIO; 4237d17c02aSMaxim Levitsky 4247d17c02aSMaxim Levitsky retry = 1; 4257d17c02aSMaxim Levitsky goto restart; 4267d17c02aSMaxim Levitsky } else { 4277d17c02aSMaxim Levitsky sm_mark_block_bad(ftl, zone, block); 4287d17c02aSMaxim Levitsky return -EIO; 4297d17c02aSMaxim Levitsky } 4307d17c02aSMaxim Levitsky } 4317d17c02aSMaxim Levitsky return 0; 4327d17c02aSMaxim Levitsky } 4337d17c02aSMaxim Levitsky 4347d17c02aSMaxim Levitsky 4357d17c02aSMaxim Levitsky /* Mark whole block at offset 'offs' as bad. */ 4367d17c02aSMaxim Levitsky static void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block) 4377d17c02aSMaxim Levitsky { 4387d17c02aSMaxim Levitsky struct sm_oob oob; 4397d17c02aSMaxim Levitsky int boffset; 4407d17c02aSMaxim Levitsky 4417d17c02aSMaxim Levitsky memset(&oob, 0xFF, SM_OOB_SIZE); 4427d17c02aSMaxim Levitsky oob.block_status = 0xF0; 4437d17c02aSMaxim Levitsky 4447d17c02aSMaxim Levitsky if (ftl->unstable) 4457d17c02aSMaxim Levitsky return; 4467d17c02aSMaxim Levitsky 4477d17c02aSMaxim Levitsky if (sm_recheck_media(ftl)) 4487d17c02aSMaxim Levitsky return; 4497d17c02aSMaxim Levitsky 4507d17c02aSMaxim Levitsky sm_printk("marking block %d of zone %d as bad", block, zone); 4517d17c02aSMaxim Levitsky 4527d17c02aSMaxim Levitsky /* We aren't checking the return value, because we don't care */ 4537d17c02aSMaxim Levitsky /* This also fails on fake xD cards, but I guess these won't expose 454*cc9d663aSShubhankar Kuranagatti * any bad blocks till fail completely 455*cc9d663aSShubhankar Kuranagatti */ 4567d17c02aSMaxim Levitsky for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE) 4577d17c02aSMaxim Levitsky sm_write_sector(ftl, zone, block, boffset, NULL, &oob); 4587d17c02aSMaxim Levitsky } 4597d17c02aSMaxim Levitsky 4607d17c02aSMaxim Levitsky /* 4617d17c02aSMaxim Levitsky * Erase a block within a zone 46292394b5cSBrian Norris * If erase succeeds, it updates free block fifo, otherwise marks block as bad 4637d17c02aSMaxim Levitsky */ 4647d17c02aSMaxim Levitsky static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, 4657d17c02aSMaxim Levitsky int put_free) 4667d17c02aSMaxim Levitsky { 4677d17c02aSMaxim Levitsky struct ftl_zone *zone = &ftl->zones[zone_num]; 4687d17c02aSMaxim Levitsky struct mtd_info *mtd = ftl->trans->mtd; 4697d17c02aSMaxim Levitsky struct erase_info erase; 4707d17c02aSMaxim Levitsky 4717d17c02aSMaxim Levitsky erase.addr = sm_mkoffset(ftl, zone_num, block, 0); 4727d17c02aSMaxim Levitsky erase.len = ftl->block_size; 4737d17c02aSMaxim Levitsky 4747d17c02aSMaxim Levitsky if (ftl->unstable) 4757d17c02aSMaxim Levitsky return -EIO; 4767d17c02aSMaxim Levitsky 4777d17c02aSMaxim Levitsky BUG_ON(ftl->readonly); 4787d17c02aSMaxim Levitsky 4797d17c02aSMaxim Levitsky if (zone_num == 0 && (block == ftl->cis_block || block == 0)) { 4807d17c02aSMaxim Levitsky sm_printk("attempted to erase the CIS!"); 4817d17c02aSMaxim Levitsky return -EIO; 4827d17c02aSMaxim Levitsky } 4837d17c02aSMaxim Levitsky 4847e1f0dc0SArtem Bityutskiy if (mtd_erase(mtd, &erase)) { 4857d17c02aSMaxim Levitsky sm_printk("erase of block %d in zone %d failed", 4867d17c02aSMaxim Levitsky block, zone_num); 4877d17c02aSMaxim Levitsky goto error; 4887d17c02aSMaxim Levitsky } 4897d17c02aSMaxim Levitsky 4907d17c02aSMaxim Levitsky if (put_free) 4917d17c02aSMaxim Levitsky kfifo_in(&zone->free_sectors, 4927d17c02aSMaxim Levitsky (const unsigned char *)&block, sizeof(block)); 4937d17c02aSMaxim Levitsky 4947d17c02aSMaxim Levitsky return 0; 4957d17c02aSMaxim Levitsky error: 4967d17c02aSMaxim Levitsky sm_mark_block_bad(ftl, zone_num, block); 4977d17c02aSMaxim Levitsky return -EIO; 4987d17c02aSMaxim Levitsky } 4997d17c02aSMaxim Levitsky 50092394b5cSBrian Norris /* Thoroughly test that block is valid. */ 5017d17c02aSMaxim Levitsky static int sm_check_block(struct sm_ftl *ftl, int zone, int block) 5027d17c02aSMaxim Levitsky { 5037d17c02aSMaxim Levitsky int boffset; 5047d17c02aSMaxim Levitsky struct sm_oob oob; 5057d17c02aSMaxim Levitsky int lbas[] = { -3, 0, 0, 0 }; 5067d17c02aSMaxim Levitsky int i = 0; 5077d17c02aSMaxim Levitsky int test_lba; 5087d17c02aSMaxim Levitsky 5097d17c02aSMaxim Levitsky 5107d17c02aSMaxim Levitsky /* First just check that block doesn't look fishy */ 5117d17c02aSMaxim Levitsky /* Only blocks that are valid or are sliced in two parts, are 512*cc9d663aSShubhankar Kuranagatti * accepted 513*cc9d663aSShubhankar Kuranagatti */ 5147d17c02aSMaxim Levitsky for (boffset = 0; boffset < ftl->block_size; 5157d17c02aSMaxim Levitsky boffset += SM_SECTOR_SIZE) { 5167d17c02aSMaxim Levitsky 51792394b5cSBrian Norris /* This shouldn't happen anyway */ 5187d17c02aSMaxim Levitsky if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob)) 5197d17c02aSMaxim Levitsky return -2; 5207d17c02aSMaxim Levitsky 5217d17c02aSMaxim Levitsky test_lba = sm_read_lba(&oob); 5227d17c02aSMaxim Levitsky 5237d17c02aSMaxim Levitsky if (lbas[i] != test_lba) 5247d17c02aSMaxim Levitsky lbas[++i] = test_lba; 5257d17c02aSMaxim Levitsky 5267d17c02aSMaxim Levitsky /* If we found three different LBAs, something is fishy */ 5277d17c02aSMaxim Levitsky if (i == 3) 5287d17c02aSMaxim Levitsky return -EIO; 5297d17c02aSMaxim Levitsky } 5307d17c02aSMaxim Levitsky 53125985edcSLucas De Marchi /* If the block is sliced (partially erased usually) erase it */ 5327d17c02aSMaxim Levitsky if (i == 2) { 5337d17c02aSMaxim Levitsky sm_erase_block(ftl, zone, block, 1); 5347d17c02aSMaxim Levitsky return 1; 5357d17c02aSMaxim Levitsky } 5367d17c02aSMaxim Levitsky 5377d17c02aSMaxim Levitsky return 0; 5387d17c02aSMaxim Levitsky } 5397d17c02aSMaxim Levitsky 5407d17c02aSMaxim Levitsky /* ----------------- media scanning --------------------------------- */ 5417d17c02aSMaxim Levitsky static const struct chs_entry chs_table[] = { 5427d17c02aSMaxim Levitsky { 1, 125, 4, 4 }, 5437d17c02aSMaxim Levitsky { 2, 125, 4, 8 }, 5447d17c02aSMaxim Levitsky { 4, 250, 4, 8 }, 5457d17c02aSMaxim Levitsky { 8, 250, 4, 16 }, 5467d17c02aSMaxim Levitsky { 16, 500, 4, 16 }, 5477d17c02aSMaxim Levitsky { 32, 500, 8, 16 }, 5487d17c02aSMaxim Levitsky { 64, 500, 8, 32 }, 5497d17c02aSMaxim Levitsky { 128, 500, 16, 32 }, 5507d17c02aSMaxim Levitsky { 256, 1000, 16, 32 }, 5517d17c02aSMaxim Levitsky { 512, 1015, 32, 63 }, 5527d17c02aSMaxim Levitsky { 1024, 985, 33, 63 }, 5537d17c02aSMaxim Levitsky { 2048, 985, 33, 63 }, 5547d17c02aSMaxim Levitsky { 0 }, 5557d17c02aSMaxim Levitsky }; 5567d17c02aSMaxim Levitsky 5577d17c02aSMaxim Levitsky 5587d17c02aSMaxim Levitsky static const uint8_t cis_signature[] = { 5597d17c02aSMaxim Levitsky 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 5607d17c02aSMaxim Levitsky }; 5617d17c02aSMaxim Levitsky /* Find out media parameters. 562*cc9d663aSShubhankar Kuranagatti * This ideally has to be based on nand id, but for now device size is enough 563*cc9d663aSShubhankar Kuranagatti */ 564582b2ffcSJingoo Han static int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) 5657d17c02aSMaxim Levitsky { 5667d17c02aSMaxim Levitsky int i; 5677d17c02aSMaxim Levitsky int size_in_megs = mtd->size / (1024 * 1024); 5687d17c02aSMaxim Levitsky 5697d17c02aSMaxim Levitsky ftl->readonly = mtd->type == MTD_ROM; 5707d17c02aSMaxim Levitsky 5717d17c02aSMaxim Levitsky /* Manual settings for very old devices */ 5727d17c02aSMaxim Levitsky ftl->zone_count = 1; 5737d17c02aSMaxim Levitsky ftl->smallpagenand = 0; 5747d17c02aSMaxim Levitsky 5757d17c02aSMaxim Levitsky switch (size_in_megs) { 5767d17c02aSMaxim Levitsky case 1: 5777d17c02aSMaxim Levitsky /* 1 MiB flash/rom SmartMedia card (256 byte pages)*/ 5787d17c02aSMaxim Levitsky ftl->zone_size = 256; 5797d17c02aSMaxim Levitsky ftl->max_lba = 250; 5807d17c02aSMaxim Levitsky ftl->block_size = 8 * SM_SECTOR_SIZE; 5817d17c02aSMaxim Levitsky ftl->smallpagenand = 1; 5827d17c02aSMaxim Levitsky 5837d17c02aSMaxim Levitsky break; 5847d17c02aSMaxim Levitsky case 2: 5857d17c02aSMaxim Levitsky /* 2 MiB flash SmartMedia (256 byte pages)*/ 5867d17c02aSMaxim Levitsky if (mtd->writesize == SM_SMALL_PAGE) { 5877d17c02aSMaxim Levitsky ftl->zone_size = 512; 5887d17c02aSMaxim Levitsky ftl->max_lba = 500; 5897d17c02aSMaxim Levitsky ftl->block_size = 8 * SM_SECTOR_SIZE; 5907d17c02aSMaxim Levitsky ftl->smallpagenand = 1; 5917d17c02aSMaxim Levitsky /* 2 MiB rom SmartMedia */ 5927d17c02aSMaxim Levitsky } else { 5937d17c02aSMaxim Levitsky 5947d17c02aSMaxim Levitsky if (!ftl->readonly) 5957d17c02aSMaxim Levitsky return -ENODEV; 5967d17c02aSMaxim Levitsky 5977d17c02aSMaxim Levitsky ftl->zone_size = 256; 5987d17c02aSMaxim Levitsky ftl->max_lba = 250; 5997d17c02aSMaxim Levitsky ftl->block_size = 16 * SM_SECTOR_SIZE; 6007d17c02aSMaxim Levitsky } 6017d17c02aSMaxim Levitsky break; 6027d17c02aSMaxim Levitsky case 4: 6037d17c02aSMaxim Levitsky /* 4 MiB flash/rom SmartMedia device */ 6047d17c02aSMaxim Levitsky ftl->zone_size = 512; 6057d17c02aSMaxim Levitsky ftl->max_lba = 500; 6067d17c02aSMaxim Levitsky ftl->block_size = 16 * SM_SECTOR_SIZE; 6077d17c02aSMaxim Levitsky break; 6087d17c02aSMaxim Levitsky case 8: 6097d17c02aSMaxim Levitsky /* 8 MiB flash/rom SmartMedia device */ 6107d17c02aSMaxim Levitsky ftl->zone_size = 1024; 6117d17c02aSMaxim Levitsky ftl->max_lba = 1000; 6127d17c02aSMaxim Levitsky ftl->block_size = 16 * SM_SECTOR_SIZE; 6137d17c02aSMaxim Levitsky } 6147d17c02aSMaxim Levitsky 6157d17c02aSMaxim Levitsky /* Minimum xD size is 16MiB. Also, all xD cards have standard zone 616*cc9d663aSShubhankar Kuranagatti * sizes. SmartMedia cards exist up to 128 MiB and have same layout 617*cc9d663aSShubhankar Kuranagatti */ 6187d17c02aSMaxim Levitsky if (size_in_megs >= 16) { 6197d17c02aSMaxim Levitsky ftl->zone_count = size_in_megs / 16; 6207d17c02aSMaxim Levitsky ftl->zone_size = 1024; 6217d17c02aSMaxim Levitsky ftl->max_lba = 1000; 6227d17c02aSMaxim Levitsky ftl->block_size = 32 * SM_SECTOR_SIZE; 6237d17c02aSMaxim Levitsky } 6247d17c02aSMaxim Levitsky 6257d17c02aSMaxim Levitsky /* Test for proper write,erase and oob sizes */ 6267d17c02aSMaxim Levitsky if (mtd->erasesize > ftl->block_size) 6277d17c02aSMaxim Levitsky return -ENODEV; 6287d17c02aSMaxim Levitsky 6297d17c02aSMaxim Levitsky if (mtd->writesize > SM_SECTOR_SIZE) 6307d17c02aSMaxim Levitsky return -ENODEV; 6317d17c02aSMaxim Levitsky 6327d17c02aSMaxim Levitsky if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE) 6337d17c02aSMaxim Levitsky return -ENODEV; 6347d17c02aSMaxim Levitsky 6357d17c02aSMaxim Levitsky if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE) 6367d17c02aSMaxim Levitsky return -ENODEV; 6377d17c02aSMaxim Levitsky 638fc002e3cSArtem Bityutskiy /* We use OOB */ 639fc002e3cSArtem Bityutskiy if (!mtd_has_oob(mtd)) 6407d17c02aSMaxim Levitsky return -ENODEV; 6417d17c02aSMaxim Levitsky 6427d17c02aSMaxim Levitsky /* Find geometry information */ 6437d17c02aSMaxim Levitsky for (i = 0 ; i < ARRAY_SIZE(chs_table) ; i++) { 6447d17c02aSMaxim Levitsky if (chs_table[i].size == size_in_megs) { 6457d17c02aSMaxim Levitsky ftl->cylinders = chs_table[i].cyl; 6467d17c02aSMaxim Levitsky ftl->heads = chs_table[i].head; 6477d17c02aSMaxim Levitsky ftl->sectors = chs_table[i].sec; 6487d17c02aSMaxim Levitsky return 0; 6497d17c02aSMaxim Levitsky } 6507d17c02aSMaxim Levitsky } 6517d17c02aSMaxim Levitsky 6527d17c02aSMaxim Levitsky sm_printk("media has unknown size : %dMiB", size_in_megs); 6537d17c02aSMaxim Levitsky ftl->cylinders = 985; 6547d17c02aSMaxim Levitsky ftl->heads = 33; 6557d17c02aSMaxim Levitsky ftl->sectors = 63; 6567d17c02aSMaxim Levitsky return 0; 6577d17c02aSMaxim Levitsky } 6587d17c02aSMaxim Levitsky 6597d17c02aSMaxim Levitsky /* Validate the CIS */ 6607d17c02aSMaxim Levitsky static int sm_read_cis(struct sm_ftl *ftl) 6617d17c02aSMaxim Levitsky { 6627d17c02aSMaxim Levitsky struct sm_oob oob; 6637d17c02aSMaxim Levitsky 6647d17c02aSMaxim Levitsky if (sm_read_sector(ftl, 6657d17c02aSMaxim Levitsky 0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob)) 6667d17c02aSMaxim Levitsky return -EIO; 6677d17c02aSMaxim Levitsky 6687d17c02aSMaxim Levitsky if (!sm_sector_valid(&oob) || !sm_block_valid(&oob)) 6697d17c02aSMaxim Levitsky return -EIO; 6707d17c02aSMaxim Levitsky 6717d17c02aSMaxim Levitsky if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset, 6727d17c02aSMaxim Levitsky cis_signature, sizeof(cis_signature))) { 6737d17c02aSMaxim Levitsky return 0; 6747d17c02aSMaxim Levitsky } 6757d17c02aSMaxim Levitsky 6767d17c02aSMaxim Levitsky return -EIO; 6777d17c02aSMaxim Levitsky } 6787d17c02aSMaxim Levitsky 6797d17c02aSMaxim Levitsky /* Scan the media for the CIS */ 6807d17c02aSMaxim Levitsky static int sm_find_cis(struct sm_ftl *ftl) 6817d17c02aSMaxim Levitsky { 6827d17c02aSMaxim Levitsky struct sm_oob oob; 6837d17c02aSMaxim Levitsky int block, boffset; 6847d17c02aSMaxim Levitsky int block_found = 0; 6857d17c02aSMaxim Levitsky int cis_found = 0; 6867d17c02aSMaxim Levitsky 6877d17c02aSMaxim Levitsky /* Search for first valid block */ 6887d17c02aSMaxim Levitsky for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) { 6897d17c02aSMaxim Levitsky 6907d17c02aSMaxim Levitsky if (sm_read_sector(ftl, 0, block, 0, NULL, &oob)) 6917d17c02aSMaxim Levitsky continue; 6927d17c02aSMaxim Levitsky 6937d17c02aSMaxim Levitsky if (!sm_block_valid(&oob)) 6947d17c02aSMaxim Levitsky continue; 6957d17c02aSMaxim Levitsky block_found = 1; 6967d17c02aSMaxim Levitsky break; 6977d17c02aSMaxim Levitsky } 6987d17c02aSMaxim Levitsky 6997d17c02aSMaxim Levitsky if (!block_found) 7007d17c02aSMaxim Levitsky return -EIO; 7017d17c02aSMaxim Levitsky 7027d17c02aSMaxim Levitsky /* Search for first valid sector in this block */ 7037d17c02aSMaxim Levitsky for (boffset = 0 ; boffset < ftl->block_size; 7047d17c02aSMaxim Levitsky boffset += SM_SECTOR_SIZE) { 7057d17c02aSMaxim Levitsky 7067d17c02aSMaxim Levitsky if (sm_read_sector(ftl, 0, block, boffset, NULL, &oob)) 7077d17c02aSMaxim Levitsky continue; 7087d17c02aSMaxim Levitsky 7097d17c02aSMaxim Levitsky if (!sm_sector_valid(&oob)) 7107d17c02aSMaxim Levitsky continue; 7117d17c02aSMaxim Levitsky break; 7127d17c02aSMaxim Levitsky } 7137d17c02aSMaxim Levitsky 7147d17c02aSMaxim Levitsky if (boffset == ftl->block_size) 7157d17c02aSMaxim Levitsky return -EIO; 7167d17c02aSMaxim Levitsky 7177d17c02aSMaxim Levitsky ftl->cis_block = block; 7187d17c02aSMaxim Levitsky ftl->cis_boffset = boffset; 7197d17c02aSMaxim Levitsky ftl->cis_page_offset = 0; 7207d17c02aSMaxim Levitsky 7217d17c02aSMaxim Levitsky cis_found = !sm_read_cis(ftl); 7227d17c02aSMaxim Levitsky 7237d17c02aSMaxim Levitsky if (!cis_found) { 7247d17c02aSMaxim Levitsky ftl->cis_page_offset = SM_SMALL_PAGE; 7257d17c02aSMaxim Levitsky cis_found = !sm_read_cis(ftl); 7267d17c02aSMaxim Levitsky } 7277d17c02aSMaxim Levitsky 7287d17c02aSMaxim Levitsky if (cis_found) { 7297d17c02aSMaxim Levitsky dbg("CIS block found at offset %x", 7307d17c02aSMaxim Levitsky block * ftl->block_size + 7317d17c02aSMaxim Levitsky boffset + ftl->cis_page_offset); 7327d17c02aSMaxim Levitsky return 0; 7337d17c02aSMaxim Levitsky } 7347d17c02aSMaxim Levitsky return -EIO; 7357d17c02aSMaxim Levitsky } 7367d17c02aSMaxim Levitsky 7377d17c02aSMaxim Levitsky /* Basic test to determine if underlying mtd device if functional */ 7387d17c02aSMaxim Levitsky static int sm_recheck_media(struct sm_ftl *ftl) 7397d17c02aSMaxim Levitsky { 7407d17c02aSMaxim Levitsky if (sm_read_cis(ftl)) { 7417d17c02aSMaxim Levitsky 7427d17c02aSMaxim Levitsky if (!ftl->unstable) { 7437d17c02aSMaxim Levitsky sm_printk("media unstable, not allowing writes"); 7447d17c02aSMaxim Levitsky ftl->unstable = 1; 7457d17c02aSMaxim Levitsky } 7467d17c02aSMaxim Levitsky return -EIO; 7477d17c02aSMaxim Levitsky } 7487d17c02aSMaxim Levitsky return 0; 7497d17c02aSMaxim Levitsky } 7507d17c02aSMaxim Levitsky 7517d17c02aSMaxim Levitsky /* Initialize a FTL zone */ 7527d17c02aSMaxim Levitsky static int sm_init_zone(struct sm_ftl *ftl, int zone_num) 7537d17c02aSMaxim Levitsky { 7547d17c02aSMaxim Levitsky struct ftl_zone *zone = &ftl->zones[zone_num]; 7557d17c02aSMaxim Levitsky struct sm_oob oob; 7567d17c02aSMaxim Levitsky uint16_t block; 7577d17c02aSMaxim Levitsky int lba; 7587d17c02aSMaxim Levitsky int i = 0; 759133fa8c7SMaxim Levitsky int len; 7607d17c02aSMaxim Levitsky 7617d17c02aSMaxim Levitsky dbg("initializing zone %d", zone_num); 7627d17c02aSMaxim Levitsky 7637d17c02aSMaxim Levitsky /* Allocate memory for FTL table */ 7646da2ec56SKees Cook zone->lba_to_phys_table = kmalloc_array(ftl->max_lba, 2, GFP_KERNEL); 7657d17c02aSMaxim Levitsky 7667d17c02aSMaxim Levitsky if (!zone->lba_to_phys_table) 7677d17c02aSMaxim Levitsky return -ENOMEM; 7687d17c02aSMaxim Levitsky memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2); 7697d17c02aSMaxim Levitsky 7707d17c02aSMaxim Levitsky 7717d17c02aSMaxim Levitsky /* Allocate memory for free sectors FIFO */ 7727d17c02aSMaxim Levitsky if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) { 7737d17c02aSMaxim Levitsky kfree(zone->lba_to_phys_table); 7747d17c02aSMaxim Levitsky return -ENOMEM; 7757d17c02aSMaxim Levitsky } 7767d17c02aSMaxim Levitsky 7777d17c02aSMaxim Levitsky /* Now scan the zone */ 7787d17c02aSMaxim Levitsky for (block = 0 ; block < ftl->zone_size ; block++) { 7797d17c02aSMaxim Levitsky 7807d17c02aSMaxim Levitsky /* Skip blocks till the CIS (including) */ 7817d17c02aSMaxim Levitsky if (zone_num == 0 && block <= ftl->cis_block) 7827d17c02aSMaxim Levitsky continue; 7837d17c02aSMaxim Levitsky 7847d17c02aSMaxim Levitsky /* Read the oob of first sector */ 785137e92fdSWenwen Wang if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) { 786137e92fdSWenwen Wang kfifo_free(&zone->free_sectors); 787137e92fdSWenwen Wang kfree(zone->lba_to_phys_table); 7887d17c02aSMaxim Levitsky return -EIO; 789137e92fdSWenwen Wang } 7907d17c02aSMaxim Levitsky 7917d17c02aSMaxim Levitsky /* Test to see if block is erased. It is enough to test 792*cc9d663aSShubhankar Kuranagatti * first sector, because erase happens in one shot 793*cc9d663aSShubhankar Kuranagatti */ 7947d17c02aSMaxim Levitsky if (sm_block_erased(&oob)) { 7957d17c02aSMaxim Levitsky kfifo_in(&zone->free_sectors, 7967d17c02aSMaxim Levitsky (unsigned char *)&block, 2); 7977d17c02aSMaxim Levitsky continue; 7987d17c02aSMaxim Levitsky } 7997d17c02aSMaxim Levitsky 8007d17c02aSMaxim Levitsky /* If block is marked as bad, skip it */ 8017d17c02aSMaxim Levitsky /* This assumes we can trust first sector*/ 8027d17c02aSMaxim Levitsky /* However the way the block valid status is defined, ensures 803*cc9d663aSShubhankar Kuranagatti * very low probability of failure here 804*cc9d663aSShubhankar Kuranagatti */ 8057d17c02aSMaxim Levitsky if (!sm_block_valid(&oob)) { 8067d17c02aSMaxim Levitsky dbg("PH %04d <-> <marked bad>", block); 8077d17c02aSMaxim Levitsky continue; 8087d17c02aSMaxim Levitsky } 8097d17c02aSMaxim Levitsky 8107d17c02aSMaxim Levitsky 8117d17c02aSMaxim Levitsky lba = sm_read_lba(&oob); 8127d17c02aSMaxim Levitsky 8137d17c02aSMaxim Levitsky /* Invalid LBA means that block is damaged. */ 8147d17c02aSMaxim Levitsky /* We can try to erase it, or mark it as bad, but 815*cc9d663aSShubhankar Kuranagatti * lets leave that to recovery application 816*cc9d663aSShubhankar Kuranagatti */ 8177d17c02aSMaxim Levitsky if (lba == -2 || lba >= ftl->max_lba) { 8187d17c02aSMaxim Levitsky dbg("PH %04d <-> LBA %04d(bad)", block, lba); 8197d17c02aSMaxim Levitsky continue; 8207d17c02aSMaxim Levitsky } 8217d17c02aSMaxim Levitsky 8227d17c02aSMaxim Levitsky 8237d17c02aSMaxim Levitsky /* If there is no collision, 824*cc9d663aSShubhankar Kuranagatti * just put the sector in the FTL table 825*cc9d663aSShubhankar Kuranagatti */ 8267d17c02aSMaxim Levitsky if (zone->lba_to_phys_table[lba] < 0) { 8277d17c02aSMaxim Levitsky dbg_verbose("PH %04d <-> LBA %04d", block, lba); 8287d17c02aSMaxim Levitsky zone->lba_to_phys_table[lba] = block; 8297d17c02aSMaxim Levitsky continue; 8307d17c02aSMaxim Levitsky } 8317d17c02aSMaxim Levitsky 8327d17c02aSMaxim Levitsky sm_printk("collision" 8337d17c02aSMaxim Levitsky " of LBA %d between blocks %d and %d in zone %d", 8347d17c02aSMaxim Levitsky lba, zone->lba_to_phys_table[lba], block, zone_num); 8357d17c02aSMaxim Levitsky 8367d17c02aSMaxim Levitsky /* Test that this block is valid*/ 8377d17c02aSMaxim Levitsky if (sm_check_block(ftl, zone_num, block)) 8387d17c02aSMaxim Levitsky continue; 8397d17c02aSMaxim Levitsky 8407d17c02aSMaxim Levitsky /* Test now the old block */ 8417d17c02aSMaxim Levitsky if (sm_check_block(ftl, zone_num, 8427d17c02aSMaxim Levitsky zone->lba_to_phys_table[lba])) { 8437d17c02aSMaxim Levitsky zone->lba_to_phys_table[lba] = block; 8447d17c02aSMaxim Levitsky continue; 8457d17c02aSMaxim Levitsky } 8467d17c02aSMaxim Levitsky 8477d17c02aSMaxim Levitsky /* If both blocks are valid and share same LBA, it means that 848*cc9d663aSShubhankar Kuranagatti * they hold different versions of same data. It not 849*cc9d663aSShubhankar Kuranagatti * known which is more recent, thus just erase one of them 8507d17c02aSMaxim Levitsky */ 8517d17c02aSMaxim Levitsky sm_printk("both blocks are valid, erasing the later"); 8527d17c02aSMaxim Levitsky sm_erase_block(ftl, zone_num, block, 1); 8537d17c02aSMaxim Levitsky } 8547d17c02aSMaxim Levitsky 8557d17c02aSMaxim Levitsky dbg("zone initialized"); 8567d17c02aSMaxim Levitsky zone->initialized = 1; 8577d17c02aSMaxim Levitsky 8587d17c02aSMaxim Levitsky /* No free sectors, means that the zone is heavily damaged, write won't 859*cc9d663aSShubhankar Kuranagatti * work, but it can still can be (partially) read 860*cc9d663aSShubhankar Kuranagatti */ 8617d17c02aSMaxim Levitsky if (!kfifo_len(&zone->free_sectors)) { 8627d17c02aSMaxim Levitsky sm_printk("no free blocks in zone %d", zone_num); 8637d17c02aSMaxim Levitsky return 0; 8647d17c02aSMaxim Levitsky } 8657d17c02aSMaxim Levitsky 8667d17c02aSMaxim Levitsky /* Randomize first block we write to */ 8677d17c02aSMaxim Levitsky get_random_bytes(&i, 2); 8687d17c02aSMaxim Levitsky i %= (kfifo_len(&zone->free_sectors) / 2); 8697d17c02aSMaxim Levitsky 8707d17c02aSMaxim Levitsky while (i--) { 871133fa8c7SMaxim Levitsky len = kfifo_out(&zone->free_sectors, 872133fa8c7SMaxim Levitsky (unsigned char *)&block, 2); 873133fa8c7SMaxim Levitsky WARN_ON(len != 2); 8747d17c02aSMaxim Levitsky kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); 8757d17c02aSMaxim Levitsky } 8767d17c02aSMaxim Levitsky return 0; 8777d17c02aSMaxim Levitsky } 8787d17c02aSMaxim Levitsky 87925985edcSLucas De Marchi /* Get and automatically initialize an FTL mapping for one zone */ 880582b2ffcSJingoo Han static struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) 8817d17c02aSMaxim Levitsky { 8827d17c02aSMaxim Levitsky struct ftl_zone *zone; 8837d17c02aSMaxim Levitsky int error; 8847d17c02aSMaxim Levitsky 8857d17c02aSMaxim Levitsky BUG_ON(zone_num >= ftl->zone_count); 8867d17c02aSMaxim Levitsky zone = &ftl->zones[zone_num]; 8877d17c02aSMaxim Levitsky 8887d17c02aSMaxim Levitsky if (!zone->initialized) { 8897d17c02aSMaxim Levitsky error = sm_init_zone(ftl, zone_num); 8907d17c02aSMaxim Levitsky 8917d17c02aSMaxim Levitsky if (error) 8927d17c02aSMaxim Levitsky return ERR_PTR(error); 8937d17c02aSMaxim Levitsky } 8947d17c02aSMaxim Levitsky return zone; 8957d17c02aSMaxim Levitsky } 8967d17c02aSMaxim Levitsky 8977d17c02aSMaxim Levitsky 8987d17c02aSMaxim Levitsky /* ----------------- cache handling ------------------------------------------*/ 8997d17c02aSMaxim Levitsky 9007d17c02aSMaxim Levitsky /* Initialize the one block cache */ 901582b2ffcSJingoo Han static void sm_cache_init(struct sm_ftl *ftl) 9027d17c02aSMaxim Levitsky { 9037d17c02aSMaxim Levitsky ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; 9047d17c02aSMaxim Levitsky ftl->cache_clean = 1; 9057d17c02aSMaxim Levitsky ftl->cache_zone = -1; 9067d17c02aSMaxim Levitsky ftl->cache_block = -1; 9077d17c02aSMaxim Levitsky /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/ 9087d17c02aSMaxim Levitsky } 9097d17c02aSMaxim Levitsky 9107d17c02aSMaxim Levitsky /* Put sector in one block cache */ 911582b2ffcSJingoo Han static void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) 9127d17c02aSMaxim Levitsky { 9137d17c02aSMaxim Levitsky memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); 9147d17c02aSMaxim Levitsky clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); 9157d17c02aSMaxim Levitsky ftl->cache_clean = 0; 9167d17c02aSMaxim Levitsky } 9177d17c02aSMaxim Levitsky 9187d17c02aSMaxim Levitsky /* Read a sector from the cache */ 919582b2ffcSJingoo Han static int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) 9207d17c02aSMaxim Levitsky { 9217d17c02aSMaxim Levitsky if (test_bit(boffset / SM_SECTOR_SIZE, 9227d17c02aSMaxim Levitsky &ftl->cache_data_invalid_bitmap)) 9237d17c02aSMaxim Levitsky return -1; 9247d17c02aSMaxim Levitsky 9257d17c02aSMaxim Levitsky memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE); 9267d17c02aSMaxim Levitsky return 0; 9277d17c02aSMaxim Levitsky } 9287d17c02aSMaxim Levitsky 9297d17c02aSMaxim Levitsky /* Write the cache to hardware */ 930582b2ffcSJingoo Han static int sm_cache_flush(struct sm_ftl *ftl) 9317d17c02aSMaxim Levitsky { 9327d17c02aSMaxim Levitsky struct ftl_zone *zone; 9337d17c02aSMaxim Levitsky 9347d17c02aSMaxim Levitsky int sector_num; 9357d17c02aSMaxim Levitsky uint16_t write_sector; 9367d17c02aSMaxim Levitsky int zone_num = ftl->cache_zone; 9377d17c02aSMaxim Levitsky int block_num; 9387d17c02aSMaxim Levitsky 9397d17c02aSMaxim Levitsky if (ftl->cache_clean) 9407d17c02aSMaxim Levitsky return 0; 9417d17c02aSMaxim Levitsky 9427d17c02aSMaxim Levitsky if (ftl->unstable) 9437d17c02aSMaxim Levitsky return -EIO; 9447d17c02aSMaxim Levitsky 9457d17c02aSMaxim Levitsky BUG_ON(zone_num < 0); 9467d17c02aSMaxim Levitsky zone = &ftl->zones[zone_num]; 9477d17c02aSMaxim Levitsky block_num = zone->lba_to_phys_table[ftl->cache_block]; 9487d17c02aSMaxim Levitsky 9497d17c02aSMaxim Levitsky 9507d17c02aSMaxim Levitsky /* Try to read all unread areas of the cache block*/ 951fed457a8SAkinobu Mita for_each_set_bit(sector_num, &ftl->cache_data_invalid_bitmap, 9527d17c02aSMaxim Levitsky ftl->block_size / SM_SECTOR_SIZE) { 9537d17c02aSMaxim Levitsky 9547d17c02aSMaxim Levitsky if (!sm_read_sector(ftl, 9557d17c02aSMaxim Levitsky zone_num, block_num, sector_num * SM_SECTOR_SIZE, 9567d17c02aSMaxim Levitsky ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL)) 9577d17c02aSMaxim Levitsky clear_bit(sector_num, 9587d17c02aSMaxim Levitsky &ftl->cache_data_invalid_bitmap); 9597d17c02aSMaxim Levitsky } 9607d17c02aSMaxim Levitsky restart: 9617d17c02aSMaxim Levitsky 9627d17c02aSMaxim Levitsky if (ftl->unstable) 9637d17c02aSMaxim Levitsky return -EIO; 964133fa8c7SMaxim Levitsky 965133fa8c7SMaxim Levitsky /* If there are no spare blocks, */ 966133fa8c7SMaxim Levitsky /* we could still continue by erasing/writing the current block, 967*cc9d663aSShubhankar Kuranagatti * but for such worn out media it doesn't worth the trouble, 968*cc9d663aSShubhankar Kuranagatti * and the dangers 969*cc9d663aSShubhankar Kuranagatti */ 970133fa8c7SMaxim Levitsky if (kfifo_out(&zone->free_sectors, 971133fa8c7SMaxim Levitsky (unsigned char *)&write_sector, 2) != 2) { 9727d17c02aSMaxim Levitsky dbg("no free sectors for write!"); 9737d17c02aSMaxim Levitsky return -EIO; 9747d17c02aSMaxim Levitsky } 9757d17c02aSMaxim Levitsky 9767d17c02aSMaxim Levitsky 9777d17c02aSMaxim Levitsky if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, 9787d17c02aSMaxim Levitsky ftl->cache_block, ftl->cache_data_invalid_bitmap)) 9797d17c02aSMaxim Levitsky goto restart; 9807d17c02aSMaxim Levitsky 9817d17c02aSMaxim Levitsky /* Update the FTL table */ 9827d17c02aSMaxim Levitsky zone->lba_to_phys_table[ftl->cache_block] = write_sector; 9837d17c02aSMaxim Levitsky 9847d17c02aSMaxim Levitsky /* Write succesfull, so erase and free the old block */ 9857d17c02aSMaxim Levitsky if (block_num > 0) 9867d17c02aSMaxim Levitsky sm_erase_block(ftl, zone_num, block_num, 1); 9877d17c02aSMaxim Levitsky 9887d17c02aSMaxim Levitsky sm_cache_init(ftl); 9897d17c02aSMaxim Levitsky return 0; 9907d17c02aSMaxim Levitsky } 9917d17c02aSMaxim Levitsky 9927d17c02aSMaxim Levitsky 9937d17c02aSMaxim Levitsky /* flush timer, runs a second after last write */ 994e99e88a9SKees Cook static void sm_cache_flush_timer(struct timer_list *t) 9957d17c02aSMaxim Levitsky { 996e99e88a9SKees Cook struct sm_ftl *ftl = from_timer(ftl, t, timer); 9977d17c02aSMaxim Levitsky queue_work(cache_flush_workqueue, &ftl->flush_work); 9987d17c02aSMaxim Levitsky } 9997d17c02aSMaxim Levitsky 10007d17c02aSMaxim Levitsky /* cache flush work, kicked by timer */ 10017d17c02aSMaxim Levitsky static void sm_cache_flush_work(struct work_struct *work) 10027d17c02aSMaxim Levitsky { 10037d17c02aSMaxim Levitsky struct sm_ftl *ftl = container_of(work, struct sm_ftl, flush_work); 10047d17c02aSMaxim Levitsky mutex_lock(&ftl->mutex); 10057d17c02aSMaxim Levitsky sm_cache_flush(ftl); 10067d17c02aSMaxim Levitsky mutex_unlock(&ftl->mutex); 10077d17c02aSMaxim Levitsky return; 10087d17c02aSMaxim Levitsky } 10097d17c02aSMaxim Levitsky 10107d17c02aSMaxim Levitsky /* ---------------- outside interface -------------------------------------- */ 10117d17c02aSMaxim Levitsky 10127d17c02aSMaxim Levitsky /* outside interface: read a sector */ 10137d17c02aSMaxim Levitsky static int sm_read(struct mtd_blktrans_dev *dev, 10147d17c02aSMaxim Levitsky unsigned long sect_no, char *buf) 10157d17c02aSMaxim Levitsky { 10167d17c02aSMaxim Levitsky struct sm_ftl *ftl = dev->priv; 10177d17c02aSMaxim Levitsky struct ftl_zone *zone; 10187d17c02aSMaxim Levitsky int error = 0, in_cache = 0; 10197d17c02aSMaxim Levitsky int zone_num, block, boffset; 10207d17c02aSMaxim Levitsky 10217d17c02aSMaxim Levitsky sm_break_offset(ftl, sect_no << 9, &zone_num, &block, &boffset); 10227d17c02aSMaxim Levitsky mutex_lock(&ftl->mutex); 10237d17c02aSMaxim Levitsky 10247d17c02aSMaxim Levitsky 10257d17c02aSMaxim Levitsky zone = sm_get_zone(ftl, zone_num); 10267d17c02aSMaxim Levitsky if (IS_ERR(zone)) { 10277d17c02aSMaxim Levitsky error = PTR_ERR(zone); 10287d17c02aSMaxim Levitsky goto unlock; 10297d17c02aSMaxim Levitsky } 10307d17c02aSMaxim Levitsky 10317d17c02aSMaxim Levitsky /* Have to look at cache first */ 10327d17c02aSMaxim Levitsky if (ftl->cache_zone == zone_num && ftl->cache_block == block) { 10337d17c02aSMaxim Levitsky in_cache = 1; 10347d17c02aSMaxim Levitsky if (!sm_cache_get(ftl, buf, boffset)) 10357d17c02aSMaxim Levitsky goto unlock; 10367d17c02aSMaxim Levitsky } 10377d17c02aSMaxim Levitsky 10387d17c02aSMaxim Levitsky /* Translate the block and return if doesn't exist in the table */ 10397d17c02aSMaxim Levitsky block = zone->lba_to_phys_table[block]; 10407d17c02aSMaxim Levitsky 10417d17c02aSMaxim Levitsky if (block == -1) { 10427d17c02aSMaxim Levitsky memset(buf, 0xFF, SM_SECTOR_SIZE); 10437d17c02aSMaxim Levitsky goto unlock; 10447d17c02aSMaxim Levitsky } 10457d17c02aSMaxim Levitsky 10467d17c02aSMaxim Levitsky if (sm_read_sector(ftl, zone_num, block, boffset, buf, NULL)) { 10477d17c02aSMaxim Levitsky error = -EIO; 10487d17c02aSMaxim Levitsky goto unlock; 10497d17c02aSMaxim Levitsky } 10507d17c02aSMaxim Levitsky 10517d17c02aSMaxim Levitsky if (in_cache) 10527d17c02aSMaxim Levitsky sm_cache_put(ftl, buf, boffset); 10537d17c02aSMaxim Levitsky unlock: 10547d17c02aSMaxim Levitsky mutex_unlock(&ftl->mutex); 10557d17c02aSMaxim Levitsky return error; 10567d17c02aSMaxim Levitsky } 10577d17c02aSMaxim Levitsky 10587d17c02aSMaxim Levitsky /* outside interface: write a sector */ 10597d17c02aSMaxim Levitsky static int sm_write(struct mtd_blktrans_dev *dev, 10607d17c02aSMaxim Levitsky unsigned long sec_no, char *buf) 10617d17c02aSMaxim Levitsky { 10627d17c02aSMaxim Levitsky struct sm_ftl *ftl = dev->priv; 10637d17c02aSMaxim Levitsky struct ftl_zone *zone; 1064f7f0d358SBrian Norris int error = 0, zone_num, block, boffset; 10657d17c02aSMaxim Levitsky 10667d17c02aSMaxim Levitsky BUG_ON(ftl->readonly); 10677d17c02aSMaxim Levitsky sm_break_offset(ftl, sec_no << 9, &zone_num, &block, &boffset); 10687d17c02aSMaxim Levitsky 10697d17c02aSMaxim Levitsky /* No need in flush thread running now */ 10707d17c02aSMaxim Levitsky del_timer(&ftl->timer); 10717d17c02aSMaxim Levitsky mutex_lock(&ftl->mutex); 10727d17c02aSMaxim Levitsky 10737d17c02aSMaxim Levitsky zone = sm_get_zone(ftl, zone_num); 10747d17c02aSMaxim Levitsky if (IS_ERR(zone)) { 10757d17c02aSMaxim Levitsky error = PTR_ERR(zone); 10767d17c02aSMaxim Levitsky goto unlock; 10777d17c02aSMaxim Levitsky } 10787d17c02aSMaxim Levitsky 10797d17c02aSMaxim Levitsky /* If entry is not in cache, flush it */ 10807d17c02aSMaxim Levitsky if (ftl->cache_block != block || ftl->cache_zone != zone_num) { 10817d17c02aSMaxim Levitsky 10827d17c02aSMaxim Levitsky error = sm_cache_flush(ftl); 10837d17c02aSMaxim Levitsky if (error) 10847d17c02aSMaxim Levitsky goto unlock; 10857d17c02aSMaxim Levitsky 10867d17c02aSMaxim Levitsky ftl->cache_block = block; 10877d17c02aSMaxim Levitsky ftl->cache_zone = zone_num; 10887d17c02aSMaxim Levitsky } 10897d17c02aSMaxim Levitsky 10907d17c02aSMaxim Levitsky sm_cache_put(ftl, buf, boffset); 10917d17c02aSMaxim Levitsky unlock: 10927d17c02aSMaxim Levitsky mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout)); 10937d17c02aSMaxim Levitsky mutex_unlock(&ftl->mutex); 10947d17c02aSMaxim Levitsky return error; 10957d17c02aSMaxim Levitsky } 10967d17c02aSMaxim Levitsky 10977d17c02aSMaxim Levitsky /* outside interface: flush everything */ 10987d17c02aSMaxim Levitsky static int sm_flush(struct mtd_blktrans_dev *dev) 10997d17c02aSMaxim Levitsky { 11007d17c02aSMaxim Levitsky struct sm_ftl *ftl = dev->priv; 11017d17c02aSMaxim Levitsky int retval; 11027d17c02aSMaxim Levitsky 11037d17c02aSMaxim Levitsky mutex_lock(&ftl->mutex); 11047d17c02aSMaxim Levitsky retval = sm_cache_flush(ftl); 11057d17c02aSMaxim Levitsky mutex_unlock(&ftl->mutex); 11067d17c02aSMaxim Levitsky return retval; 11077d17c02aSMaxim Levitsky } 11087d17c02aSMaxim Levitsky 11097d17c02aSMaxim Levitsky /* outside interface: device is released */ 1110a8ca889eSAl Viro static void sm_release(struct mtd_blktrans_dev *dev) 11117d17c02aSMaxim Levitsky { 11127d17c02aSMaxim Levitsky struct sm_ftl *ftl = dev->priv; 11137d17c02aSMaxim Levitsky 11147d17c02aSMaxim Levitsky mutex_lock(&ftl->mutex); 11157d17c02aSMaxim Levitsky del_timer_sync(&ftl->timer); 11167d17c02aSMaxim Levitsky cancel_work_sync(&ftl->flush_work); 11177d17c02aSMaxim Levitsky sm_cache_flush(ftl); 11187d17c02aSMaxim Levitsky mutex_unlock(&ftl->mutex); 11197d17c02aSMaxim Levitsky } 11207d17c02aSMaxim Levitsky 11217d17c02aSMaxim Levitsky /* outside interface: get geometry */ 11227d17c02aSMaxim Levitsky static int sm_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) 11237d17c02aSMaxim Levitsky { 11247d17c02aSMaxim Levitsky struct sm_ftl *ftl = dev->priv; 11257d17c02aSMaxim Levitsky geo->heads = ftl->heads; 11267d17c02aSMaxim Levitsky geo->sectors = ftl->sectors; 11277d17c02aSMaxim Levitsky geo->cylinders = ftl->cylinders; 11287d17c02aSMaxim Levitsky return 0; 11297d17c02aSMaxim Levitsky } 11307d17c02aSMaxim Levitsky 11317d17c02aSMaxim Levitsky /* external interface: main initialization function */ 11327d17c02aSMaxim Levitsky static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 11337d17c02aSMaxim Levitsky { 11347d17c02aSMaxim Levitsky struct mtd_blktrans_dev *trans; 11357d17c02aSMaxim Levitsky struct sm_ftl *ftl; 11367d17c02aSMaxim Levitsky 11377d17c02aSMaxim Levitsky /* Allocate & initialize our private structure */ 11387d17c02aSMaxim Levitsky ftl = kzalloc(sizeof(struct sm_ftl), GFP_KERNEL); 11397d17c02aSMaxim Levitsky if (!ftl) 11407d17c02aSMaxim Levitsky goto error1; 11417d17c02aSMaxim Levitsky 11427d17c02aSMaxim Levitsky 11437d17c02aSMaxim Levitsky mutex_init(&ftl->mutex); 1144e99e88a9SKees Cook timer_setup(&ftl->timer, sm_cache_flush_timer, 0); 11457d17c02aSMaxim Levitsky INIT_WORK(&ftl->flush_work, sm_cache_flush_work); 11467d17c02aSMaxim Levitsky 11477d17c02aSMaxim Levitsky /* Read media information */ 11487d17c02aSMaxim Levitsky if (sm_get_media_info(ftl, mtd)) { 11497d17c02aSMaxim Levitsky dbg("found unsupported mtd device, aborting"); 11507d17c02aSMaxim Levitsky goto error2; 11517d17c02aSMaxim Levitsky } 11527d17c02aSMaxim Levitsky 11537d17c02aSMaxim Levitsky 11547d17c02aSMaxim Levitsky /* Allocate temporary CIS buffer for read retry support */ 11557d17c02aSMaxim Levitsky ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); 11567d17c02aSMaxim Levitsky if (!ftl->cis_buffer) 11577d17c02aSMaxim Levitsky goto error2; 11587d17c02aSMaxim Levitsky 11597d17c02aSMaxim Levitsky /* Allocate zone array, it will be initialized on demand */ 11606396bb22SKees Cook ftl->zones = kcalloc(ftl->zone_count, sizeof(struct ftl_zone), 11617d17c02aSMaxim Levitsky GFP_KERNEL); 11627d17c02aSMaxim Levitsky if (!ftl->zones) 11637d17c02aSMaxim Levitsky goto error3; 11647d17c02aSMaxim Levitsky 11657d17c02aSMaxim Levitsky /* Allocate the cache*/ 11667d17c02aSMaxim Levitsky ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); 11677d17c02aSMaxim Levitsky 11687d17c02aSMaxim Levitsky if (!ftl->cache_data) 11697d17c02aSMaxim Levitsky goto error4; 11707d17c02aSMaxim Levitsky 11717d17c02aSMaxim Levitsky sm_cache_init(ftl); 11727d17c02aSMaxim Levitsky 11737d17c02aSMaxim Levitsky 11747d17c02aSMaxim Levitsky /* Allocate upper layer structure and initialize it */ 11757d17c02aSMaxim Levitsky trans = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL); 11767d17c02aSMaxim Levitsky if (!trans) 11777d17c02aSMaxim Levitsky goto error5; 11787d17c02aSMaxim Levitsky 11797d17c02aSMaxim Levitsky ftl->trans = trans; 11807d17c02aSMaxim Levitsky trans->priv = ftl; 11817d17c02aSMaxim Levitsky 11827d17c02aSMaxim Levitsky trans->tr = tr; 11837d17c02aSMaxim Levitsky trans->mtd = mtd; 11847d17c02aSMaxim Levitsky trans->devnum = -1; 11857d17c02aSMaxim Levitsky trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9; 11867d17c02aSMaxim Levitsky trans->readonly = ftl->readonly; 11877d17c02aSMaxim Levitsky 11887d17c02aSMaxim Levitsky if (sm_find_cis(ftl)) { 11897d17c02aSMaxim Levitsky dbg("CIS not found on mtd device, aborting"); 11907d17c02aSMaxim Levitsky goto error6; 11917d17c02aSMaxim Levitsky } 11927d17c02aSMaxim Levitsky 11937d17c02aSMaxim Levitsky ftl->disk_attributes = sm_create_sysfs_attributes(ftl); 1194629286b9SXiaochen Wang if (!ftl->disk_attributes) 1195629286b9SXiaochen Wang goto error6; 11967d17c02aSMaxim Levitsky trans->disk_attributes = ftl->disk_attributes; 11977d17c02aSMaxim Levitsky 11987d17c02aSMaxim Levitsky sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d", 11997d17c02aSMaxim Levitsky (int)(mtd->size / (1024 * 1024)), mtd->index); 12007d17c02aSMaxim Levitsky 12017d17c02aSMaxim Levitsky dbg("FTL layout:"); 12027d17c02aSMaxim Levitsky dbg("%d zone(s), each consists of %d blocks (+%d spares)", 12037d17c02aSMaxim Levitsky ftl->zone_count, ftl->max_lba, 12047d17c02aSMaxim Levitsky ftl->zone_size - ftl->max_lba); 12057d17c02aSMaxim Levitsky dbg("each block consists of %d bytes", 12067d17c02aSMaxim Levitsky ftl->block_size); 12077d17c02aSMaxim Levitsky 12087d17c02aSMaxim Levitsky 12097d17c02aSMaxim Levitsky /* Register device*/ 12107d17c02aSMaxim Levitsky if (add_mtd_blktrans_dev(trans)) { 12117d17c02aSMaxim Levitsky dbg("error in mtdblktrans layer"); 12127d17c02aSMaxim Levitsky goto error6; 12137d17c02aSMaxim Levitsky } 12147d17c02aSMaxim Levitsky return; 12157d17c02aSMaxim Levitsky error6: 12167d17c02aSMaxim Levitsky kfree(trans); 12177d17c02aSMaxim Levitsky error5: 12187d17c02aSMaxim Levitsky kfree(ftl->cache_data); 12197d17c02aSMaxim Levitsky error4: 12207d17c02aSMaxim Levitsky kfree(ftl->zones); 12217d17c02aSMaxim Levitsky error3: 12227d17c02aSMaxim Levitsky kfree(ftl->cis_buffer); 12237d17c02aSMaxim Levitsky error2: 12247d17c02aSMaxim Levitsky kfree(ftl); 12257d17c02aSMaxim Levitsky error1: 12267d17c02aSMaxim Levitsky return; 12277d17c02aSMaxim Levitsky } 12287d17c02aSMaxim Levitsky 12297d17c02aSMaxim Levitsky /* main interface: device {surprise,} removal */ 12307d17c02aSMaxim Levitsky static void sm_remove_dev(struct mtd_blktrans_dev *dev) 12317d17c02aSMaxim Levitsky { 12327d17c02aSMaxim Levitsky struct sm_ftl *ftl = dev->priv; 12337d17c02aSMaxim Levitsky int i; 12347d17c02aSMaxim Levitsky 12357d17c02aSMaxim Levitsky del_mtd_blktrans_dev(dev); 12367d17c02aSMaxim Levitsky ftl->trans = NULL; 12377d17c02aSMaxim Levitsky 12387d17c02aSMaxim Levitsky for (i = 0 ; i < ftl->zone_count; i++) { 12397d17c02aSMaxim Levitsky 12407d17c02aSMaxim Levitsky if (!ftl->zones[i].initialized) 12417d17c02aSMaxim Levitsky continue; 12427d17c02aSMaxim Levitsky 12437d17c02aSMaxim Levitsky kfree(ftl->zones[i].lba_to_phys_table); 12447d17c02aSMaxim Levitsky kfifo_free(&ftl->zones[i].free_sectors); 12457d17c02aSMaxim Levitsky } 12467d17c02aSMaxim Levitsky 12477d17c02aSMaxim Levitsky sm_delete_sysfs_attributes(ftl); 12487d17c02aSMaxim Levitsky kfree(ftl->cis_buffer); 12497d17c02aSMaxim Levitsky kfree(ftl->zones); 12507d17c02aSMaxim Levitsky kfree(ftl->cache_data); 12517d17c02aSMaxim Levitsky kfree(ftl); 12527d17c02aSMaxim Levitsky } 12537d17c02aSMaxim Levitsky 12547d17c02aSMaxim Levitsky static struct mtd_blktrans_ops sm_ftl_ops = { 12557d17c02aSMaxim Levitsky .name = "smblk", 1256452380efSMaxim Levitsky .major = 0, 12577d17c02aSMaxim Levitsky .part_bits = SM_FTL_PARTN_BITS, 12587d17c02aSMaxim Levitsky .blksize = SM_SECTOR_SIZE, 12597d17c02aSMaxim Levitsky .getgeo = sm_getgeo, 12607d17c02aSMaxim Levitsky 12617d17c02aSMaxim Levitsky .add_mtd = sm_add_mtd, 12627d17c02aSMaxim Levitsky .remove_dev = sm_remove_dev, 12637d17c02aSMaxim Levitsky 12647d17c02aSMaxim Levitsky .readsect = sm_read, 12657d17c02aSMaxim Levitsky .writesect = sm_write, 12667d17c02aSMaxim Levitsky 12677d17c02aSMaxim Levitsky .flush = sm_flush, 12687d17c02aSMaxim Levitsky .release = sm_release, 12697d17c02aSMaxim Levitsky 12707d17c02aSMaxim Levitsky .owner = THIS_MODULE, 12717d17c02aSMaxim Levitsky }; 12727d17c02aSMaxim Levitsky 12737d17c02aSMaxim Levitsky static __init int sm_module_init(void) 12747d17c02aSMaxim Levitsky { 12757d17c02aSMaxim Levitsky int error = 0; 12767d17c02aSMaxim Levitsky 127739de86efSDan Carpenter cache_flush_workqueue = create_freezable_workqueue("smflush"); 127839de86efSDan Carpenter if (!cache_flush_workqueue) 127939de86efSDan Carpenter return -ENOMEM; 12807d17c02aSMaxim Levitsky 12817d17c02aSMaxim Levitsky error = register_mtd_blktrans(&sm_ftl_ops); 12827d17c02aSMaxim Levitsky if (error) 12837d17c02aSMaxim Levitsky destroy_workqueue(cache_flush_workqueue); 12847d17c02aSMaxim Levitsky return error; 12857d17c02aSMaxim Levitsky 12867d17c02aSMaxim Levitsky } 12877d17c02aSMaxim Levitsky 12887d17c02aSMaxim Levitsky static void __exit sm_module_exit(void) 12897d17c02aSMaxim Levitsky { 12907d17c02aSMaxim Levitsky destroy_workqueue(cache_flush_workqueue); 12917d17c02aSMaxim Levitsky deregister_mtd_blktrans(&sm_ftl_ops); 12927d17c02aSMaxim Levitsky } 12937d17c02aSMaxim Levitsky 12947d17c02aSMaxim Levitsky module_init(sm_module_init); 12957d17c02aSMaxim Levitsky module_exit(sm_module_exit); 12967d17c02aSMaxim Levitsky 12977d17c02aSMaxim Levitsky MODULE_LICENSE("GPL"); 12987d17c02aSMaxim Levitsky MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); 12997d17c02aSMaxim Levitsky MODULE_DESCRIPTION("Smartmedia/xD mtd translation layer"); 1300