18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2009 - Maxim Levitsky 48c2ecf20Sopenharmony_ci * SmartMedia/xD translation layer 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/random.h> 108c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 118c2ecf20Sopenharmony_ci#include <linux/kthread.h> 128c2ecf20Sopenharmony_ci#include <linux/freezer.h> 138c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 148c2ecf20Sopenharmony_ci#include <linux/bitops.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h> 178c2ecf20Sopenharmony_ci#include "nand/raw/sm_common.h" 188c2ecf20Sopenharmony_ci#include "sm_ftl.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct workqueue_struct *cache_flush_workqueue; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int cache_timeout = 1000; 258c2ecf20Sopenharmony_cimodule_param(cache_timeout, int, S_IRUGO); 268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cache_timeout, 278c2ecf20Sopenharmony_ci "Timeout (in ms) for cache flush (1000 ms default"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int debug; 308c2ecf20Sopenharmony_cimodule_param(debug, int, S_IRUGO | S_IWUSR); 318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-2)"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* ------------------- sysfs attributes ---------------------------------- */ 358c2ecf20Sopenharmony_cistruct sm_sysfs_attribute { 368c2ecf20Sopenharmony_ci struct device_attribute dev_attr; 378c2ecf20Sopenharmony_ci char *data; 388c2ecf20Sopenharmony_ci int len; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, 428c2ecf20Sopenharmony_ci char *buf) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct sm_sysfs_attribute *sm_attr = 458c2ecf20Sopenharmony_ci container_of(attr, struct sm_sysfs_attribute, dev_attr); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci strncpy(buf, sm_attr->data, sm_attr->len); 488c2ecf20Sopenharmony_ci return sm_attr->len; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define NUM_ATTRIBUTES 1 538c2ecf20Sopenharmony_ci#define SM_CIS_VENDOR_OFFSET 0x59 548c2ecf20Sopenharmony_cistatic struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct attribute_group *attr_group; 578c2ecf20Sopenharmony_ci struct attribute **attributes; 588c2ecf20Sopenharmony_ci struct sm_sysfs_attribute *vendor_attribute; 598c2ecf20Sopenharmony_ci char *vendor; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, 628c2ecf20Sopenharmony_ci SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (!vendor) 648c2ecf20Sopenharmony_ci goto error1; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Initialize sysfs attributes */ 678c2ecf20Sopenharmony_ci vendor_attribute = 688c2ecf20Sopenharmony_ci kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL); 698c2ecf20Sopenharmony_ci if (!vendor_attribute) 708c2ecf20Sopenharmony_ci goto error2; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci sysfs_attr_init(&vendor_attribute->dev_attr.attr); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci vendor_attribute->data = vendor; 758c2ecf20Sopenharmony_ci vendor_attribute->len = strlen(vendor); 768c2ecf20Sopenharmony_ci vendor_attribute->dev_attr.attr.name = "vendor"; 778c2ecf20Sopenharmony_ci vendor_attribute->dev_attr.attr.mode = S_IRUGO; 788c2ecf20Sopenharmony_ci vendor_attribute->dev_attr.show = sm_attr_show; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Create array of pointers to the attributes */ 828c2ecf20Sopenharmony_ci attributes = kcalloc(NUM_ATTRIBUTES + 1, sizeof(struct attribute *), 838c2ecf20Sopenharmony_ci GFP_KERNEL); 848c2ecf20Sopenharmony_ci if (!attributes) 858c2ecf20Sopenharmony_ci goto error3; 868c2ecf20Sopenharmony_ci attributes[0] = &vendor_attribute->dev_attr.attr; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Finally create the attribute group */ 898c2ecf20Sopenharmony_ci attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL); 908c2ecf20Sopenharmony_ci if (!attr_group) 918c2ecf20Sopenharmony_ci goto error4; 928c2ecf20Sopenharmony_ci attr_group->attrs = attributes; 938c2ecf20Sopenharmony_ci return attr_group; 948c2ecf20Sopenharmony_cierror4: 958c2ecf20Sopenharmony_ci kfree(attributes); 968c2ecf20Sopenharmony_cierror3: 978c2ecf20Sopenharmony_ci kfree(vendor_attribute); 988c2ecf20Sopenharmony_cierror2: 998c2ecf20Sopenharmony_ci kfree(vendor); 1008c2ecf20Sopenharmony_cierror1: 1018c2ecf20Sopenharmony_ci return NULL; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void sm_delete_sysfs_attributes(struct sm_ftl *ftl) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct attribute **attributes = ftl->disk_attributes->attrs; 1078c2ecf20Sopenharmony_ci int i; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; attributes[i] ; i++) { 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci struct device_attribute *dev_attr = container_of(attributes[i], 1128c2ecf20Sopenharmony_ci struct device_attribute, attr); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci struct sm_sysfs_attribute *sm_attr = 1158c2ecf20Sopenharmony_ci container_of(dev_attr, 1168c2ecf20Sopenharmony_ci struct sm_sysfs_attribute, dev_attr); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci kfree(sm_attr->data); 1198c2ecf20Sopenharmony_ci kfree(sm_attr); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci kfree(ftl->disk_attributes->attrs); 1238c2ecf20Sopenharmony_ci kfree(ftl->disk_attributes); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* ----------------------- oob helpers -------------------------------------- */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int sm_get_lba(uint8_t *lba) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci /* check fixed bits */ 1328c2ecf20Sopenharmony_ci if ((lba[0] & 0xF8) != 0x10) 1338c2ecf20Sopenharmony_ci return -2; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* check parity - endianness doesn't matter */ 1368c2ecf20Sopenharmony_ci if (hweight16(*(uint16_t *)lba) & 1) 1378c2ecf20Sopenharmony_ci return -2; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return (lba[1] >> 1) | ((lba[0] & 0x07) << 7); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Read LBA associated with block 1458c2ecf20Sopenharmony_ci * returns -1, if block is erased 1468c2ecf20Sopenharmony_ci * returns -2 if error happens 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cistatic int sm_read_lba(struct sm_oob *oob) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci static const uint32_t erased_pattern[4] = { 1518c2ecf20Sopenharmony_ci 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci uint16_t lba_test; 1548c2ecf20Sopenharmony_ci int lba; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* First test for erased block */ 1578c2ecf20Sopenharmony_ci if (!memcmp(oob, erased_pattern, SM_OOB_SIZE)) 1588c2ecf20Sopenharmony_ci return -1; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Now check is both copies of the LBA differ too much */ 1618c2ecf20Sopenharmony_ci lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2; 1628c2ecf20Sopenharmony_ci if (lba_test && !is_power_of_2(lba_test)) 1638c2ecf20Sopenharmony_ci return -2; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* And read it */ 1668c2ecf20Sopenharmony_ci lba = sm_get_lba(oob->lba_copy1); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (lba == -2) 1698c2ecf20Sopenharmony_ci lba = sm_get_lba(oob->lba_copy2); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return lba; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void sm_write_lba(struct sm_oob *oob, uint16_t lba) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci uint8_t tmp[2]; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci WARN_ON(lba >= 1000); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci tmp[0] = 0x10 | ((lba >> 7) & 0x07); 1818c2ecf20Sopenharmony_ci tmp[1] = (lba << 1) & 0xFF; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (hweight16(*(uint16_t *)tmp) & 0x01) 1848c2ecf20Sopenharmony_ci tmp[1] |= 1; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0]; 1878c2ecf20Sopenharmony_ci oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1]; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* Make offset from parts */ 1928c2ecf20Sopenharmony_cistatic loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci WARN_ON(boffset & (SM_SECTOR_SIZE - 1)); 1958c2ecf20Sopenharmony_ci WARN_ON(zone < 0 || zone >= ftl->zone_count); 1968c2ecf20Sopenharmony_ci WARN_ON(block >= ftl->zone_size); 1978c2ecf20Sopenharmony_ci WARN_ON(boffset >= ftl->block_size); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (block == -1) 2008c2ecf20Sopenharmony_ci return -1; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* Breaks offset into parts */ 2068c2ecf20Sopenharmony_cistatic void sm_break_offset(struct sm_ftl *ftl, loff_t loffset, 2078c2ecf20Sopenharmony_ci int *zone, int *block, int *boffset) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci u64 offset = loffset; 2108c2ecf20Sopenharmony_ci *boffset = do_div(offset, ftl->block_size); 2118c2ecf20Sopenharmony_ci *block = do_div(offset, ftl->max_lba); 2128c2ecf20Sopenharmony_ci *zone = offset >= ftl->zone_count ? -1 : offset; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* ---------------------- low level IO ------------------------------------- */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci uint8_t ecc[3]; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc, 2228c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 2238c2ecf20Sopenharmony_ci if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE, 2248c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0) 2258c2ecf20Sopenharmony_ci return -EIO; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci buffer += SM_SMALL_PAGE; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc, 2308c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 2318c2ecf20Sopenharmony_ci if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE, 2328c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0) 2338c2ecf20Sopenharmony_ci return -EIO; 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* Reads a sector + oob*/ 2388c2ecf20Sopenharmony_cistatic int sm_read_sector(struct sm_ftl *ftl, 2398c2ecf20Sopenharmony_ci int zone, int block, int boffset, 2408c2ecf20Sopenharmony_ci uint8_t *buffer, struct sm_oob *oob) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct mtd_info *mtd = ftl->trans->mtd; 2438c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 2448c2ecf20Sopenharmony_ci struct sm_oob tmp_oob; 2458c2ecf20Sopenharmony_ci int ret = -EIO; 2468c2ecf20Sopenharmony_ci int try = 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* FTL can contain -1 entries that are by default filled with bits */ 2498c2ecf20Sopenharmony_ci if (block == -1) { 2508c2ecf20Sopenharmony_ci if (buffer) 2518c2ecf20Sopenharmony_ci memset(buffer, 0xFF, SM_SECTOR_SIZE); 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* User might not need the oob, but we do for data verification */ 2568c2ecf20Sopenharmony_ci if (!oob) 2578c2ecf20Sopenharmony_ci oob = &tmp_oob; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; 2608c2ecf20Sopenharmony_ci ops.ooboffs = 0; 2618c2ecf20Sopenharmony_ci ops.ooblen = SM_OOB_SIZE; 2628c2ecf20Sopenharmony_ci ops.oobbuf = (void *)oob; 2638c2ecf20Sopenharmony_ci ops.len = SM_SECTOR_SIZE; 2648c2ecf20Sopenharmony_ci ops.datbuf = buffer; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciagain: 2678c2ecf20Sopenharmony_ci if (try++) { 2688c2ecf20Sopenharmony_ci /* Avoid infinite recursion on CIS reads, sm_recheck_media 2698c2ecf20Sopenharmony_ci won't help anyway */ 2708c2ecf20Sopenharmony_ci if (zone == 0 && block == ftl->cis_block && boffset == 2718c2ecf20Sopenharmony_ci ftl->cis_boffset) 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Test if media is stable */ 2758c2ecf20Sopenharmony_ci if (try == 3 || sm_recheck_media(ftl)) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Unfortunately, oob read will _always_ succeed, 2808c2ecf20Sopenharmony_ci despite card removal..... */ 2818c2ecf20Sopenharmony_ci ret = mtd_read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Test for unknown errors */ 2848c2ecf20Sopenharmony_ci if (ret != 0 && !mtd_is_bitflip_or_eccerr(ret)) { 2858c2ecf20Sopenharmony_ci dbg("read of block %d at zone %d, failed due to error (%d)", 2868c2ecf20Sopenharmony_ci block, zone, ret); 2878c2ecf20Sopenharmony_ci goto again; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* Do a basic test on the oob, to guard against returned garbage */ 2918c2ecf20Sopenharmony_ci if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved)) 2928c2ecf20Sopenharmony_ci goto again; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* This should never happen, unless there is a bug in the mtd driver */ 2958c2ecf20Sopenharmony_ci WARN_ON(ops.oobretlen != SM_OOB_SIZE); 2968c2ecf20Sopenharmony_ci WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (!buffer) 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Test if sector marked as bad */ 3028c2ecf20Sopenharmony_ci if (!sm_sector_valid(oob)) { 3038c2ecf20Sopenharmony_ci dbg("read of block %d at zone %d, failed because it is marked" 3048c2ecf20Sopenharmony_ci " as bad" , block, zone); 3058c2ecf20Sopenharmony_ci goto again; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Test ECC*/ 3098c2ecf20Sopenharmony_ci if (mtd_is_eccerr(ret) || 3108c2ecf20Sopenharmony_ci (ftl->smallpagenand && sm_correct_sector(buffer, oob))) { 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dbg("read of block %d at zone %d, failed due to ECC error", 3138c2ecf20Sopenharmony_ci block, zone); 3148c2ecf20Sopenharmony_ci goto again; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* Writes a sector to media */ 3218c2ecf20Sopenharmony_cistatic int sm_write_sector(struct sm_ftl *ftl, 3228c2ecf20Sopenharmony_ci int zone, int block, int boffset, 3238c2ecf20Sopenharmony_ci uint8_t *buffer, struct sm_oob *oob) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 3268c2ecf20Sopenharmony_ci struct mtd_info *mtd = ftl->trans->mtd; 3278c2ecf20Sopenharmony_ci int ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci BUG_ON(ftl->readonly); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (zone == 0 && (block == ftl->cis_block || block == 0)) { 3328c2ecf20Sopenharmony_ci dbg("attempted to write the CIS!"); 3338c2ecf20Sopenharmony_ci return -EIO; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (ftl->unstable) 3378c2ecf20Sopenharmony_ci return -EIO; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; 3408c2ecf20Sopenharmony_ci ops.len = SM_SECTOR_SIZE; 3418c2ecf20Sopenharmony_ci ops.datbuf = buffer; 3428c2ecf20Sopenharmony_ci ops.ooboffs = 0; 3438c2ecf20Sopenharmony_ci ops.ooblen = SM_OOB_SIZE; 3448c2ecf20Sopenharmony_ci ops.oobbuf = (void *)oob; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ret = mtd_write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Now we assume that hardware will catch write bitflip errors */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (ret) { 3518c2ecf20Sopenharmony_ci dbg("write to block %d at zone %d, failed with error %d", 3528c2ecf20Sopenharmony_ci block, zone, ret); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci sm_recheck_media(ftl); 3558c2ecf20Sopenharmony_ci return ret; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* This should never happen, unless there is a bug in the driver */ 3598c2ecf20Sopenharmony_ci WARN_ON(ops.oobretlen != SM_OOB_SIZE); 3608c2ecf20Sopenharmony_ci WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* ------------------------ block IO ------------------------------------- */ 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* Write a block using data and lba, and invalid sector bitmap */ 3688c2ecf20Sopenharmony_cistatic int sm_write_block(struct sm_ftl *ftl, uint8_t *buf, 3698c2ecf20Sopenharmony_ci int zone, int block, int lba, 3708c2ecf20Sopenharmony_ci unsigned long invalid_bitmap) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct sm_oob oob; 3738c2ecf20Sopenharmony_ci int boffset; 3748c2ecf20Sopenharmony_ci int retry = 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Initialize the oob with requested values */ 3778c2ecf20Sopenharmony_ci memset(&oob, 0xFF, SM_OOB_SIZE); 3788c2ecf20Sopenharmony_ci sm_write_lba(&oob, lba); 3798c2ecf20Sopenharmony_cirestart: 3808c2ecf20Sopenharmony_ci if (ftl->unstable) 3818c2ecf20Sopenharmony_ci return -EIO; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci for (boffset = 0; boffset < ftl->block_size; 3848c2ecf20Sopenharmony_ci boffset += SM_SECTOR_SIZE) { 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci oob.data_status = 0xFF; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (test_bit(boffset / SM_SECTOR_SIZE, &invalid_bitmap)) { 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci sm_printk("sector %d of block at LBA %d of zone %d" 3918c2ecf20Sopenharmony_ci " couldn't be read, marking it as invalid", 3928c2ecf20Sopenharmony_ci boffset / SM_SECTOR_SIZE, lba, zone); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci oob.data_status = 0; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (ftl->smallpagenand) { 3988c2ecf20Sopenharmony_ci __nand_calculate_ecc(buf + boffset, SM_SMALL_PAGE, 3998c2ecf20Sopenharmony_ci oob.ecc1, 4008c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE, 4038c2ecf20Sopenharmony_ci SM_SMALL_PAGE, oob.ecc2, 4048c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci if (!sm_write_sector(ftl, zone, block, boffset, 4078c2ecf20Sopenharmony_ci buf + boffset, &oob)) 4088c2ecf20Sopenharmony_ci continue; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (!retry) { 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* If write fails. try to erase the block */ 4138c2ecf20Sopenharmony_ci /* This is safe, because we never write in blocks 4148c2ecf20Sopenharmony_ci that contain valuable data. 4158c2ecf20Sopenharmony_ci This is intended to repair block that are marked 4168c2ecf20Sopenharmony_ci as erased, but that isn't fully erased*/ 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (sm_erase_block(ftl, zone, block, 0)) 4198c2ecf20Sopenharmony_ci return -EIO; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci retry = 1; 4228c2ecf20Sopenharmony_ci goto restart; 4238c2ecf20Sopenharmony_ci } else { 4248c2ecf20Sopenharmony_ci sm_mark_block_bad(ftl, zone, block); 4258c2ecf20Sopenharmony_ci return -EIO; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* Mark whole block at offset 'offs' as bad. */ 4338c2ecf20Sopenharmony_cistatic void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct sm_oob oob; 4368c2ecf20Sopenharmony_ci int boffset; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci memset(&oob, 0xFF, SM_OOB_SIZE); 4398c2ecf20Sopenharmony_ci oob.block_status = 0xF0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (ftl->unstable) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (sm_recheck_media(ftl)) 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci sm_printk("marking block %d of zone %d as bad", block, zone); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* We aren't checking the return value, because we don't care */ 4508c2ecf20Sopenharmony_ci /* This also fails on fake xD cards, but I guess these won't expose 4518c2ecf20Sopenharmony_ci any bad blocks till fail completely */ 4528c2ecf20Sopenharmony_ci for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE) 4538c2ecf20Sopenharmony_ci sm_write_sector(ftl, zone, block, boffset, NULL, &oob); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * Erase a block within a zone 4588c2ecf20Sopenharmony_ci * If erase succeeds, it updates free block fifo, otherwise marks block as bad 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, 4618c2ecf20Sopenharmony_ci int put_free) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct ftl_zone *zone = &ftl->zones[zone_num]; 4648c2ecf20Sopenharmony_ci struct mtd_info *mtd = ftl->trans->mtd; 4658c2ecf20Sopenharmony_ci struct erase_info erase; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci erase.addr = sm_mkoffset(ftl, zone_num, block, 0); 4688c2ecf20Sopenharmony_ci erase.len = ftl->block_size; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (ftl->unstable) 4718c2ecf20Sopenharmony_ci return -EIO; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci BUG_ON(ftl->readonly); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (zone_num == 0 && (block == ftl->cis_block || block == 0)) { 4768c2ecf20Sopenharmony_ci sm_printk("attempted to erase the CIS!"); 4778c2ecf20Sopenharmony_ci return -EIO; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (mtd_erase(mtd, &erase)) { 4818c2ecf20Sopenharmony_ci sm_printk("erase of block %d in zone %d failed", 4828c2ecf20Sopenharmony_ci block, zone_num); 4838c2ecf20Sopenharmony_ci goto error; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (put_free) 4878c2ecf20Sopenharmony_ci kfifo_in(&zone->free_sectors, 4888c2ecf20Sopenharmony_ci (const unsigned char *)&block, sizeof(block)); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_cierror: 4928c2ecf20Sopenharmony_ci sm_mark_block_bad(ftl, zone_num, block); 4938c2ecf20Sopenharmony_ci return -EIO; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/* Thoroughly test that block is valid. */ 4978c2ecf20Sopenharmony_cistatic int sm_check_block(struct sm_ftl *ftl, int zone, int block) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int boffset; 5008c2ecf20Sopenharmony_ci struct sm_oob oob; 5018c2ecf20Sopenharmony_ci int lbas[] = { -3, 0, 0, 0 }; 5028c2ecf20Sopenharmony_ci int i = 0; 5038c2ecf20Sopenharmony_ci int test_lba; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* First just check that block doesn't look fishy */ 5078c2ecf20Sopenharmony_ci /* Only blocks that are valid or are sliced in two parts, are 5088c2ecf20Sopenharmony_ci accepted */ 5098c2ecf20Sopenharmony_ci for (boffset = 0; boffset < ftl->block_size; 5108c2ecf20Sopenharmony_ci boffset += SM_SECTOR_SIZE) { 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* This shouldn't happen anyway */ 5138c2ecf20Sopenharmony_ci if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob)) 5148c2ecf20Sopenharmony_ci return -2; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci test_lba = sm_read_lba(&oob); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (lbas[i] != test_lba) 5198c2ecf20Sopenharmony_ci lbas[++i] = test_lba; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* If we found three different LBAs, something is fishy */ 5228c2ecf20Sopenharmony_ci if (i == 3) 5238c2ecf20Sopenharmony_ci return -EIO; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* If the block is sliced (partially erased usually) erase it */ 5278c2ecf20Sopenharmony_ci if (i == 2) { 5288c2ecf20Sopenharmony_ci sm_erase_block(ftl, zone, block, 1); 5298c2ecf20Sopenharmony_ci return 1; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/* ----------------- media scanning --------------------------------- */ 5368c2ecf20Sopenharmony_cistatic const struct chs_entry chs_table[] = { 5378c2ecf20Sopenharmony_ci { 1, 125, 4, 4 }, 5388c2ecf20Sopenharmony_ci { 2, 125, 4, 8 }, 5398c2ecf20Sopenharmony_ci { 4, 250, 4, 8 }, 5408c2ecf20Sopenharmony_ci { 8, 250, 4, 16 }, 5418c2ecf20Sopenharmony_ci { 16, 500, 4, 16 }, 5428c2ecf20Sopenharmony_ci { 32, 500, 8, 16 }, 5438c2ecf20Sopenharmony_ci { 64, 500, 8, 32 }, 5448c2ecf20Sopenharmony_ci { 128, 500, 16, 32 }, 5458c2ecf20Sopenharmony_ci { 256, 1000, 16, 32 }, 5468c2ecf20Sopenharmony_ci { 512, 1015, 32, 63 }, 5478c2ecf20Sopenharmony_ci { 1024, 985, 33, 63 }, 5488c2ecf20Sopenharmony_ci { 2048, 985, 33, 63 }, 5498c2ecf20Sopenharmony_ci { 0 }, 5508c2ecf20Sopenharmony_ci}; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic const uint8_t cis_signature[] = { 5548c2ecf20Sopenharmony_ci 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_ci/* Find out media parameters. 5578c2ecf20Sopenharmony_ci * This ideally has to be based on nand id, but for now device size is enough */ 5588c2ecf20Sopenharmony_cistatic int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci int i; 5618c2ecf20Sopenharmony_ci int size_in_megs = mtd->size / (1024 * 1024); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ftl->readonly = mtd->type == MTD_ROM; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* Manual settings for very old devices */ 5668c2ecf20Sopenharmony_ci ftl->zone_count = 1; 5678c2ecf20Sopenharmony_ci ftl->smallpagenand = 0; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci switch (size_in_megs) { 5708c2ecf20Sopenharmony_ci case 1: 5718c2ecf20Sopenharmony_ci /* 1 MiB flash/rom SmartMedia card (256 byte pages)*/ 5728c2ecf20Sopenharmony_ci ftl->zone_size = 256; 5738c2ecf20Sopenharmony_ci ftl->max_lba = 250; 5748c2ecf20Sopenharmony_ci ftl->block_size = 8 * SM_SECTOR_SIZE; 5758c2ecf20Sopenharmony_ci ftl->smallpagenand = 1; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci case 2: 5798c2ecf20Sopenharmony_ci /* 2 MiB flash SmartMedia (256 byte pages)*/ 5808c2ecf20Sopenharmony_ci if (mtd->writesize == SM_SMALL_PAGE) { 5818c2ecf20Sopenharmony_ci ftl->zone_size = 512; 5828c2ecf20Sopenharmony_ci ftl->max_lba = 500; 5838c2ecf20Sopenharmony_ci ftl->block_size = 8 * SM_SECTOR_SIZE; 5848c2ecf20Sopenharmony_ci ftl->smallpagenand = 1; 5858c2ecf20Sopenharmony_ci /* 2 MiB rom SmartMedia */ 5868c2ecf20Sopenharmony_ci } else { 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (!ftl->readonly) 5898c2ecf20Sopenharmony_ci return -ENODEV; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ftl->zone_size = 256; 5928c2ecf20Sopenharmony_ci ftl->max_lba = 250; 5938c2ecf20Sopenharmony_ci ftl->block_size = 16 * SM_SECTOR_SIZE; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci case 4: 5978c2ecf20Sopenharmony_ci /* 4 MiB flash/rom SmartMedia device */ 5988c2ecf20Sopenharmony_ci ftl->zone_size = 512; 5998c2ecf20Sopenharmony_ci ftl->max_lba = 500; 6008c2ecf20Sopenharmony_ci ftl->block_size = 16 * SM_SECTOR_SIZE; 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci case 8: 6038c2ecf20Sopenharmony_ci /* 8 MiB flash/rom SmartMedia device */ 6048c2ecf20Sopenharmony_ci ftl->zone_size = 1024; 6058c2ecf20Sopenharmony_ci ftl->max_lba = 1000; 6068c2ecf20Sopenharmony_ci ftl->block_size = 16 * SM_SECTOR_SIZE; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* Minimum xD size is 16MiB. Also, all xD cards have standard zone 6108c2ecf20Sopenharmony_ci sizes. SmartMedia cards exist up to 128 MiB and have same layout*/ 6118c2ecf20Sopenharmony_ci if (size_in_megs >= 16) { 6128c2ecf20Sopenharmony_ci ftl->zone_count = size_in_megs / 16; 6138c2ecf20Sopenharmony_ci ftl->zone_size = 1024; 6148c2ecf20Sopenharmony_ci ftl->max_lba = 1000; 6158c2ecf20Sopenharmony_ci ftl->block_size = 32 * SM_SECTOR_SIZE; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* Test for proper write,erase and oob sizes */ 6198c2ecf20Sopenharmony_ci if (mtd->erasesize > ftl->block_size) 6208c2ecf20Sopenharmony_ci return -ENODEV; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (mtd->writesize > SM_SECTOR_SIZE) 6238c2ecf20Sopenharmony_ci return -ENODEV; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE) 6268c2ecf20Sopenharmony_ci return -ENODEV; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE) 6298c2ecf20Sopenharmony_ci return -ENODEV; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* We use OOB */ 6328c2ecf20Sopenharmony_ci if (!mtd_has_oob(mtd)) 6338c2ecf20Sopenharmony_ci return -ENODEV; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Find geometry information */ 6368c2ecf20Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(chs_table) ; i++) { 6378c2ecf20Sopenharmony_ci if (chs_table[i].size == size_in_megs) { 6388c2ecf20Sopenharmony_ci ftl->cylinders = chs_table[i].cyl; 6398c2ecf20Sopenharmony_ci ftl->heads = chs_table[i].head; 6408c2ecf20Sopenharmony_ci ftl->sectors = chs_table[i].sec; 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci sm_printk("media has unknown size : %dMiB", size_in_megs); 6468c2ecf20Sopenharmony_ci ftl->cylinders = 985; 6478c2ecf20Sopenharmony_ci ftl->heads = 33; 6488c2ecf20Sopenharmony_ci ftl->sectors = 63; 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/* Validate the CIS */ 6538c2ecf20Sopenharmony_cistatic int sm_read_cis(struct sm_ftl *ftl) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct sm_oob oob; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (sm_read_sector(ftl, 6588c2ecf20Sopenharmony_ci 0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob)) 6598c2ecf20Sopenharmony_ci return -EIO; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (!sm_sector_valid(&oob) || !sm_block_valid(&oob)) 6628c2ecf20Sopenharmony_ci return -EIO; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset, 6658c2ecf20Sopenharmony_ci cis_signature, sizeof(cis_signature))) { 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return -EIO; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/* Scan the media for the CIS */ 6738c2ecf20Sopenharmony_cistatic int sm_find_cis(struct sm_ftl *ftl) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct sm_oob oob; 6768c2ecf20Sopenharmony_ci int block, boffset; 6778c2ecf20Sopenharmony_ci int block_found = 0; 6788c2ecf20Sopenharmony_ci int cis_found = 0; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Search for first valid block */ 6818c2ecf20Sopenharmony_ci for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) { 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (sm_read_sector(ftl, 0, block, 0, NULL, &oob)) 6848c2ecf20Sopenharmony_ci continue; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (!sm_block_valid(&oob)) 6878c2ecf20Sopenharmony_ci continue; 6888c2ecf20Sopenharmony_ci block_found = 1; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (!block_found) 6938c2ecf20Sopenharmony_ci return -EIO; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* Search for first valid sector in this block */ 6968c2ecf20Sopenharmony_ci for (boffset = 0 ; boffset < ftl->block_size; 6978c2ecf20Sopenharmony_ci boffset += SM_SECTOR_SIZE) { 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (sm_read_sector(ftl, 0, block, boffset, NULL, &oob)) 7008c2ecf20Sopenharmony_ci continue; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (!sm_sector_valid(&oob)) 7038c2ecf20Sopenharmony_ci continue; 7048c2ecf20Sopenharmony_ci break; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (boffset == ftl->block_size) 7088c2ecf20Sopenharmony_ci return -EIO; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci ftl->cis_block = block; 7118c2ecf20Sopenharmony_ci ftl->cis_boffset = boffset; 7128c2ecf20Sopenharmony_ci ftl->cis_page_offset = 0; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci cis_found = !sm_read_cis(ftl); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (!cis_found) { 7178c2ecf20Sopenharmony_ci ftl->cis_page_offset = SM_SMALL_PAGE; 7188c2ecf20Sopenharmony_ci cis_found = !sm_read_cis(ftl); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (cis_found) { 7228c2ecf20Sopenharmony_ci dbg("CIS block found at offset %x", 7238c2ecf20Sopenharmony_ci block * ftl->block_size + 7248c2ecf20Sopenharmony_ci boffset + ftl->cis_page_offset); 7258c2ecf20Sopenharmony_ci return 0; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci return -EIO; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/* Basic test to determine if underlying mtd device if functional */ 7318c2ecf20Sopenharmony_cistatic int sm_recheck_media(struct sm_ftl *ftl) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci if (sm_read_cis(ftl)) { 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (!ftl->unstable) { 7368c2ecf20Sopenharmony_ci sm_printk("media unstable, not allowing writes"); 7378c2ecf20Sopenharmony_ci ftl->unstable = 1; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci return -EIO; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci/* Initialize a FTL zone */ 7458c2ecf20Sopenharmony_cistatic int sm_init_zone(struct sm_ftl *ftl, int zone_num) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct ftl_zone *zone = &ftl->zones[zone_num]; 7488c2ecf20Sopenharmony_ci struct sm_oob oob; 7498c2ecf20Sopenharmony_ci uint16_t block; 7508c2ecf20Sopenharmony_ci int lba; 7518c2ecf20Sopenharmony_ci int i = 0; 7528c2ecf20Sopenharmony_ci int len; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci dbg("initializing zone %d", zone_num); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Allocate memory for FTL table */ 7578c2ecf20Sopenharmony_ci zone->lba_to_phys_table = kmalloc_array(ftl->max_lba, 2, GFP_KERNEL); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (!zone->lba_to_phys_table) 7608c2ecf20Sopenharmony_ci return -ENOMEM; 7618c2ecf20Sopenharmony_ci memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Allocate memory for free sectors FIFO */ 7658c2ecf20Sopenharmony_ci if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) { 7668c2ecf20Sopenharmony_ci kfree(zone->lba_to_phys_table); 7678c2ecf20Sopenharmony_ci return -ENOMEM; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* Now scan the zone */ 7718c2ecf20Sopenharmony_ci for (block = 0 ; block < ftl->zone_size ; block++) { 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* Skip blocks till the CIS (including) */ 7748c2ecf20Sopenharmony_ci if (zone_num == 0 && block <= ftl->cis_block) 7758c2ecf20Sopenharmony_ci continue; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* Read the oob of first sector */ 7788c2ecf20Sopenharmony_ci if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) { 7798c2ecf20Sopenharmony_ci kfifo_free(&zone->free_sectors); 7808c2ecf20Sopenharmony_ci kfree(zone->lba_to_phys_table); 7818c2ecf20Sopenharmony_ci return -EIO; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Test to see if block is erased. It is enough to test 7858c2ecf20Sopenharmony_ci first sector, because erase happens in one shot */ 7868c2ecf20Sopenharmony_ci if (sm_block_erased(&oob)) { 7878c2ecf20Sopenharmony_ci kfifo_in(&zone->free_sectors, 7888c2ecf20Sopenharmony_ci (unsigned char *)&block, 2); 7898c2ecf20Sopenharmony_ci continue; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* If block is marked as bad, skip it */ 7938c2ecf20Sopenharmony_ci /* This assumes we can trust first sector*/ 7948c2ecf20Sopenharmony_ci /* However the way the block valid status is defined, ensures 7958c2ecf20Sopenharmony_ci very low probability of failure here */ 7968c2ecf20Sopenharmony_ci if (!sm_block_valid(&oob)) { 7978c2ecf20Sopenharmony_ci dbg("PH %04d <-> <marked bad>", block); 7988c2ecf20Sopenharmony_ci continue; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci lba = sm_read_lba(&oob); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Invalid LBA means that block is damaged. */ 8058c2ecf20Sopenharmony_ci /* We can try to erase it, or mark it as bad, but 8068c2ecf20Sopenharmony_ci lets leave that to recovery application */ 8078c2ecf20Sopenharmony_ci if (lba == -2 || lba >= ftl->max_lba) { 8088c2ecf20Sopenharmony_ci dbg("PH %04d <-> LBA %04d(bad)", block, lba); 8098c2ecf20Sopenharmony_ci continue; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* If there is no collision, 8148c2ecf20Sopenharmony_ci just put the sector in the FTL table */ 8158c2ecf20Sopenharmony_ci if (zone->lba_to_phys_table[lba] < 0) { 8168c2ecf20Sopenharmony_ci dbg_verbose("PH %04d <-> LBA %04d", block, lba); 8178c2ecf20Sopenharmony_ci zone->lba_to_phys_table[lba] = block; 8188c2ecf20Sopenharmony_ci continue; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci sm_printk("collision" 8228c2ecf20Sopenharmony_ci " of LBA %d between blocks %d and %d in zone %d", 8238c2ecf20Sopenharmony_ci lba, zone->lba_to_phys_table[lba], block, zone_num); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* Test that this block is valid*/ 8268c2ecf20Sopenharmony_ci if (sm_check_block(ftl, zone_num, block)) 8278c2ecf20Sopenharmony_ci continue; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* Test now the old block */ 8308c2ecf20Sopenharmony_ci if (sm_check_block(ftl, zone_num, 8318c2ecf20Sopenharmony_ci zone->lba_to_phys_table[lba])) { 8328c2ecf20Sopenharmony_ci zone->lba_to_phys_table[lba] = block; 8338c2ecf20Sopenharmony_ci continue; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* If both blocks are valid and share same LBA, it means that 8378c2ecf20Sopenharmony_ci they hold different versions of same data. It not 8388c2ecf20Sopenharmony_ci known which is more recent, thus just erase one of them 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_ci sm_printk("both blocks are valid, erasing the later"); 8418c2ecf20Sopenharmony_ci sm_erase_block(ftl, zone_num, block, 1); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci dbg("zone initialized"); 8458c2ecf20Sopenharmony_ci zone->initialized = 1; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* No free sectors, means that the zone is heavily damaged, write won't 8488c2ecf20Sopenharmony_ci work, but it can still can be (partially) read */ 8498c2ecf20Sopenharmony_ci if (!kfifo_len(&zone->free_sectors)) { 8508c2ecf20Sopenharmony_ci sm_printk("no free blocks in zone %d", zone_num); 8518c2ecf20Sopenharmony_ci return 0; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* Randomize first block we write to */ 8558c2ecf20Sopenharmony_ci get_random_bytes(&i, 2); 8568c2ecf20Sopenharmony_ci i %= (kfifo_len(&zone->free_sectors) / 2); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci while (i--) { 8598c2ecf20Sopenharmony_ci len = kfifo_out(&zone->free_sectors, 8608c2ecf20Sopenharmony_ci (unsigned char *)&block, 2); 8618c2ecf20Sopenharmony_ci WARN_ON(len != 2); 8628c2ecf20Sopenharmony_ci kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* Get and automatically initialize an FTL mapping for one zone */ 8688c2ecf20Sopenharmony_cistatic struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct ftl_zone *zone; 8718c2ecf20Sopenharmony_ci int error; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci BUG_ON(zone_num >= ftl->zone_count); 8748c2ecf20Sopenharmony_ci zone = &ftl->zones[zone_num]; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (!zone->initialized) { 8778c2ecf20Sopenharmony_ci error = sm_init_zone(ftl, zone_num); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (error) 8808c2ecf20Sopenharmony_ci return ERR_PTR(error); 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci return zone; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci/* ----------------- cache handling ------------------------------------------*/ 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci/* Initialize the one block cache */ 8898c2ecf20Sopenharmony_cistatic void sm_cache_init(struct sm_ftl *ftl) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; 8928c2ecf20Sopenharmony_ci ftl->cache_clean = 1; 8938c2ecf20Sopenharmony_ci ftl->cache_zone = -1; 8948c2ecf20Sopenharmony_ci ftl->cache_block = -1; 8958c2ecf20Sopenharmony_ci /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/ 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* Put sector in one block cache */ 8998c2ecf20Sopenharmony_cistatic void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); 9028c2ecf20Sopenharmony_ci clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); 9038c2ecf20Sopenharmony_ci ftl->cache_clean = 0; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/* Read a sector from the cache */ 9078c2ecf20Sopenharmony_cistatic int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci if (test_bit(boffset / SM_SECTOR_SIZE, 9108c2ecf20Sopenharmony_ci &ftl->cache_data_invalid_bitmap)) 9118c2ecf20Sopenharmony_ci return -1; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE); 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/* Write the cache to hardware */ 9188c2ecf20Sopenharmony_cistatic int sm_cache_flush(struct sm_ftl *ftl) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci struct ftl_zone *zone; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci int sector_num; 9238c2ecf20Sopenharmony_ci uint16_t write_sector; 9248c2ecf20Sopenharmony_ci int zone_num = ftl->cache_zone; 9258c2ecf20Sopenharmony_ci int block_num; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (ftl->cache_clean) 9288c2ecf20Sopenharmony_ci return 0; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (ftl->unstable) 9318c2ecf20Sopenharmony_ci return -EIO; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci BUG_ON(zone_num < 0); 9348c2ecf20Sopenharmony_ci zone = &ftl->zones[zone_num]; 9358c2ecf20Sopenharmony_ci block_num = zone->lba_to_phys_table[ftl->cache_block]; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* Try to read all unread areas of the cache block*/ 9398c2ecf20Sopenharmony_ci for_each_set_bit(sector_num, &ftl->cache_data_invalid_bitmap, 9408c2ecf20Sopenharmony_ci ftl->block_size / SM_SECTOR_SIZE) { 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (!sm_read_sector(ftl, 9438c2ecf20Sopenharmony_ci zone_num, block_num, sector_num * SM_SECTOR_SIZE, 9448c2ecf20Sopenharmony_ci ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL)) 9458c2ecf20Sopenharmony_ci clear_bit(sector_num, 9468c2ecf20Sopenharmony_ci &ftl->cache_data_invalid_bitmap); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_cirestart: 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (ftl->unstable) 9518c2ecf20Sopenharmony_ci return -EIO; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* If there are no spare blocks, */ 9548c2ecf20Sopenharmony_ci /* we could still continue by erasing/writing the current block, 9558c2ecf20Sopenharmony_ci but for such worn out media it doesn't worth the trouble, 9568c2ecf20Sopenharmony_ci and the dangers */ 9578c2ecf20Sopenharmony_ci if (kfifo_out(&zone->free_sectors, 9588c2ecf20Sopenharmony_ci (unsigned char *)&write_sector, 2) != 2) { 9598c2ecf20Sopenharmony_ci dbg("no free sectors for write!"); 9608c2ecf20Sopenharmony_ci return -EIO; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, 9658c2ecf20Sopenharmony_ci ftl->cache_block, ftl->cache_data_invalid_bitmap)) 9668c2ecf20Sopenharmony_ci goto restart; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* Update the FTL table */ 9698c2ecf20Sopenharmony_ci zone->lba_to_phys_table[ftl->cache_block] = write_sector; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* Write succesfull, so erase and free the old block */ 9728c2ecf20Sopenharmony_ci if (block_num > 0) 9738c2ecf20Sopenharmony_ci sm_erase_block(ftl, zone_num, block_num, 1); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci sm_cache_init(ftl); 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci/* flush timer, runs a second after last write */ 9818c2ecf20Sopenharmony_cistatic void sm_cache_flush_timer(struct timer_list *t) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct sm_ftl *ftl = from_timer(ftl, t, timer); 9848c2ecf20Sopenharmony_ci queue_work(cache_flush_workqueue, &ftl->flush_work); 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci/* cache flush work, kicked by timer */ 9888c2ecf20Sopenharmony_cistatic void sm_cache_flush_work(struct work_struct *work) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct sm_ftl *ftl = container_of(work, struct sm_ftl, flush_work); 9918c2ecf20Sopenharmony_ci mutex_lock(&ftl->mutex); 9928c2ecf20Sopenharmony_ci sm_cache_flush(ftl); 9938c2ecf20Sopenharmony_ci mutex_unlock(&ftl->mutex); 9948c2ecf20Sopenharmony_ci return; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci/* ---------------- outside interface -------------------------------------- */ 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci/* outside interface: read a sector */ 10008c2ecf20Sopenharmony_cistatic int sm_read(struct mtd_blktrans_dev *dev, 10018c2ecf20Sopenharmony_ci unsigned long sect_no, char *buf) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct sm_ftl *ftl = dev->priv; 10048c2ecf20Sopenharmony_ci struct ftl_zone *zone; 10058c2ecf20Sopenharmony_ci int error = 0, in_cache = 0; 10068c2ecf20Sopenharmony_ci int zone_num, block, boffset; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci sm_break_offset(ftl, sect_no << 9, &zone_num, &block, &boffset); 10098c2ecf20Sopenharmony_ci mutex_lock(&ftl->mutex); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci zone = sm_get_zone(ftl, zone_num); 10138c2ecf20Sopenharmony_ci if (IS_ERR(zone)) { 10148c2ecf20Sopenharmony_ci error = PTR_ERR(zone); 10158c2ecf20Sopenharmony_ci goto unlock; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Have to look at cache first */ 10198c2ecf20Sopenharmony_ci if (ftl->cache_zone == zone_num && ftl->cache_block == block) { 10208c2ecf20Sopenharmony_ci in_cache = 1; 10218c2ecf20Sopenharmony_ci if (!sm_cache_get(ftl, buf, boffset)) 10228c2ecf20Sopenharmony_ci goto unlock; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* Translate the block and return if doesn't exist in the table */ 10268c2ecf20Sopenharmony_ci block = zone->lba_to_phys_table[block]; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (block == -1) { 10298c2ecf20Sopenharmony_ci memset(buf, 0xFF, SM_SECTOR_SIZE); 10308c2ecf20Sopenharmony_ci goto unlock; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (sm_read_sector(ftl, zone_num, block, boffset, buf, NULL)) { 10348c2ecf20Sopenharmony_ci error = -EIO; 10358c2ecf20Sopenharmony_ci goto unlock; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (in_cache) 10398c2ecf20Sopenharmony_ci sm_cache_put(ftl, buf, boffset); 10408c2ecf20Sopenharmony_ciunlock: 10418c2ecf20Sopenharmony_ci mutex_unlock(&ftl->mutex); 10428c2ecf20Sopenharmony_ci return error; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci/* outside interface: write a sector */ 10468c2ecf20Sopenharmony_cistatic int sm_write(struct mtd_blktrans_dev *dev, 10478c2ecf20Sopenharmony_ci unsigned long sec_no, char *buf) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct sm_ftl *ftl = dev->priv; 10508c2ecf20Sopenharmony_ci struct ftl_zone *zone; 10518c2ecf20Sopenharmony_ci int error = 0, zone_num, block, boffset; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci BUG_ON(ftl->readonly); 10548c2ecf20Sopenharmony_ci sm_break_offset(ftl, sec_no << 9, &zone_num, &block, &boffset); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* No need in flush thread running now */ 10578c2ecf20Sopenharmony_ci del_timer(&ftl->timer); 10588c2ecf20Sopenharmony_ci mutex_lock(&ftl->mutex); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci zone = sm_get_zone(ftl, zone_num); 10618c2ecf20Sopenharmony_ci if (IS_ERR(zone)) { 10628c2ecf20Sopenharmony_ci error = PTR_ERR(zone); 10638c2ecf20Sopenharmony_ci goto unlock; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* If entry is not in cache, flush it */ 10678c2ecf20Sopenharmony_ci if (ftl->cache_block != block || ftl->cache_zone != zone_num) { 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci error = sm_cache_flush(ftl); 10708c2ecf20Sopenharmony_ci if (error) 10718c2ecf20Sopenharmony_ci goto unlock; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ftl->cache_block = block; 10748c2ecf20Sopenharmony_ci ftl->cache_zone = zone_num; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci sm_cache_put(ftl, buf, boffset); 10788c2ecf20Sopenharmony_ciunlock: 10798c2ecf20Sopenharmony_ci mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout)); 10808c2ecf20Sopenharmony_ci mutex_unlock(&ftl->mutex); 10818c2ecf20Sopenharmony_ci return error; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/* outside interface: flush everything */ 10858c2ecf20Sopenharmony_cistatic int sm_flush(struct mtd_blktrans_dev *dev) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci struct sm_ftl *ftl = dev->priv; 10888c2ecf20Sopenharmony_ci int retval; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci mutex_lock(&ftl->mutex); 10918c2ecf20Sopenharmony_ci retval = sm_cache_flush(ftl); 10928c2ecf20Sopenharmony_ci mutex_unlock(&ftl->mutex); 10938c2ecf20Sopenharmony_ci return retval; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci/* outside interface: device is released */ 10978c2ecf20Sopenharmony_cistatic void sm_release(struct mtd_blktrans_dev *dev) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct sm_ftl *ftl = dev->priv; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci del_timer_sync(&ftl->timer); 11028c2ecf20Sopenharmony_ci cancel_work_sync(&ftl->flush_work); 11038c2ecf20Sopenharmony_ci mutex_lock(&ftl->mutex); 11048c2ecf20Sopenharmony_ci sm_cache_flush(ftl); 11058c2ecf20Sopenharmony_ci mutex_unlock(&ftl->mutex); 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci/* outside interface: get geometry */ 11098c2ecf20Sopenharmony_cistatic int sm_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci struct sm_ftl *ftl = dev->priv; 11128c2ecf20Sopenharmony_ci geo->heads = ftl->heads; 11138c2ecf20Sopenharmony_ci geo->sectors = ftl->sectors; 11148c2ecf20Sopenharmony_ci geo->cylinders = ftl->cylinders; 11158c2ecf20Sopenharmony_ci return 0; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci/* external interface: main initialization function */ 11198c2ecf20Sopenharmony_cistatic void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct mtd_blktrans_dev *trans; 11228c2ecf20Sopenharmony_ci struct sm_ftl *ftl; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* Allocate & initialize our private structure */ 11258c2ecf20Sopenharmony_ci ftl = kzalloc(sizeof(struct sm_ftl), GFP_KERNEL); 11268c2ecf20Sopenharmony_ci if (!ftl) 11278c2ecf20Sopenharmony_ci goto error1; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci mutex_init(&ftl->mutex); 11318c2ecf20Sopenharmony_ci timer_setup(&ftl->timer, sm_cache_flush_timer, 0); 11328c2ecf20Sopenharmony_ci INIT_WORK(&ftl->flush_work, sm_cache_flush_work); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* Read media information */ 11358c2ecf20Sopenharmony_ci if (sm_get_media_info(ftl, mtd)) { 11368c2ecf20Sopenharmony_ci dbg("found unsupported mtd device, aborting"); 11378c2ecf20Sopenharmony_ci goto error2; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Allocate temporary CIS buffer for read retry support */ 11428c2ecf20Sopenharmony_ci ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); 11438c2ecf20Sopenharmony_ci if (!ftl->cis_buffer) 11448c2ecf20Sopenharmony_ci goto error2; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* Allocate zone array, it will be initialized on demand */ 11478c2ecf20Sopenharmony_ci ftl->zones = kcalloc(ftl->zone_count, sizeof(struct ftl_zone), 11488c2ecf20Sopenharmony_ci GFP_KERNEL); 11498c2ecf20Sopenharmony_ci if (!ftl->zones) 11508c2ecf20Sopenharmony_ci goto error3; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* Allocate the cache*/ 11538c2ecf20Sopenharmony_ci ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (!ftl->cache_data) 11568c2ecf20Sopenharmony_ci goto error4; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci sm_cache_init(ftl); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* Allocate upper layer structure and initialize it */ 11628c2ecf20Sopenharmony_ci trans = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL); 11638c2ecf20Sopenharmony_ci if (!trans) 11648c2ecf20Sopenharmony_ci goto error5; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci ftl->trans = trans; 11678c2ecf20Sopenharmony_ci trans->priv = ftl; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci trans->tr = tr; 11708c2ecf20Sopenharmony_ci trans->mtd = mtd; 11718c2ecf20Sopenharmony_ci trans->devnum = -1; 11728c2ecf20Sopenharmony_ci trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9; 11738c2ecf20Sopenharmony_ci trans->readonly = ftl->readonly; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (sm_find_cis(ftl)) { 11768c2ecf20Sopenharmony_ci dbg("CIS not found on mtd device, aborting"); 11778c2ecf20Sopenharmony_ci goto error6; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci ftl->disk_attributes = sm_create_sysfs_attributes(ftl); 11818c2ecf20Sopenharmony_ci if (!ftl->disk_attributes) 11828c2ecf20Sopenharmony_ci goto error6; 11838c2ecf20Sopenharmony_ci trans->disk_attributes = ftl->disk_attributes; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d", 11868c2ecf20Sopenharmony_ci (int)(mtd->size / (1024 * 1024)), mtd->index); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci dbg("FTL layout:"); 11898c2ecf20Sopenharmony_ci dbg("%d zone(s), each consists of %d blocks (+%d spares)", 11908c2ecf20Sopenharmony_ci ftl->zone_count, ftl->max_lba, 11918c2ecf20Sopenharmony_ci ftl->zone_size - ftl->max_lba); 11928c2ecf20Sopenharmony_ci dbg("each block consists of %d bytes", 11938c2ecf20Sopenharmony_ci ftl->block_size); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* Register device*/ 11978c2ecf20Sopenharmony_ci if (add_mtd_blktrans_dev(trans)) { 11988c2ecf20Sopenharmony_ci dbg("error in mtdblktrans layer"); 11998c2ecf20Sopenharmony_ci goto error6; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci return; 12028c2ecf20Sopenharmony_cierror6: 12038c2ecf20Sopenharmony_ci kfree(trans); 12048c2ecf20Sopenharmony_cierror5: 12058c2ecf20Sopenharmony_ci kfree(ftl->cache_data); 12068c2ecf20Sopenharmony_cierror4: 12078c2ecf20Sopenharmony_ci kfree(ftl->zones); 12088c2ecf20Sopenharmony_cierror3: 12098c2ecf20Sopenharmony_ci kfree(ftl->cis_buffer); 12108c2ecf20Sopenharmony_cierror2: 12118c2ecf20Sopenharmony_ci kfree(ftl); 12128c2ecf20Sopenharmony_cierror1: 12138c2ecf20Sopenharmony_ci return; 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci/* main interface: device {surprise,} removal */ 12178c2ecf20Sopenharmony_cistatic void sm_remove_dev(struct mtd_blktrans_dev *dev) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct sm_ftl *ftl = dev->priv; 12208c2ecf20Sopenharmony_ci int i; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci del_mtd_blktrans_dev(dev); 12238c2ecf20Sopenharmony_ci ftl->trans = NULL; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci for (i = 0 ; i < ftl->zone_count; i++) { 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (!ftl->zones[i].initialized) 12288c2ecf20Sopenharmony_ci continue; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci kfree(ftl->zones[i].lba_to_phys_table); 12318c2ecf20Sopenharmony_ci kfifo_free(&ftl->zones[i].free_sectors); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci sm_delete_sysfs_attributes(ftl); 12358c2ecf20Sopenharmony_ci kfree(ftl->cis_buffer); 12368c2ecf20Sopenharmony_ci kfree(ftl->zones); 12378c2ecf20Sopenharmony_ci kfree(ftl->cache_data); 12388c2ecf20Sopenharmony_ci kfree(ftl); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic struct mtd_blktrans_ops sm_ftl_ops = { 12428c2ecf20Sopenharmony_ci .name = "smblk", 12438c2ecf20Sopenharmony_ci .major = 0, 12448c2ecf20Sopenharmony_ci .part_bits = SM_FTL_PARTN_BITS, 12458c2ecf20Sopenharmony_ci .blksize = SM_SECTOR_SIZE, 12468c2ecf20Sopenharmony_ci .getgeo = sm_getgeo, 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci .add_mtd = sm_add_mtd, 12498c2ecf20Sopenharmony_ci .remove_dev = sm_remove_dev, 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci .readsect = sm_read, 12528c2ecf20Sopenharmony_ci .writesect = sm_write, 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci .flush = sm_flush, 12558c2ecf20Sopenharmony_ci .release = sm_release, 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12588c2ecf20Sopenharmony_ci}; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic __init int sm_module_init(void) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci int error = 0; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci cache_flush_workqueue = create_freezable_workqueue("smflush"); 12658c2ecf20Sopenharmony_ci if (!cache_flush_workqueue) 12668c2ecf20Sopenharmony_ci return -ENOMEM; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci error = register_mtd_blktrans(&sm_ftl_ops); 12698c2ecf20Sopenharmony_ci if (error) 12708c2ecf20Sopenharmony_ci destroy_workqueue(cache_flush_workqueue); 12718c2ecf20Sopenharmony_ci return error; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic void __exit sm_module_exit(void) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci destroy_workqueue(cache_flush_workqueue); 12788c2ecf20Sopenharmony_ci deregister_mtd_blktrans(&sm_ftl_ops); 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cimodule_init(sm_module_init); 12828c2ecf20Sopenharmony_cimodule_exit(sm_module_exit); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); 12868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Smartmedia/xD mtd translation layer"); 1287