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
sm_attr_show(struct device * dev,struct device_attribute * attr,char * buf)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
sm_create_sysfs_attributes(struct sm_ftl * ftl)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
sm_delete_sysfs_attributes(struct sm_ftl * ftl)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
sm_get_lba(uint8_t * lba)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 */
sm_read_lba(struct sm_oob * oob)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
sm_write_lba(struct sm_oob * oob,uint16_t lba)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 */
sm_mkoffset(struct sm_ftl * ftl,int zone,int block,int boffset)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 */
sm_break_offset(struct sm_ftl * ftl,loff_t loffset,int * zone,int * block,int * boffset)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
sm_correct_sector(uint8_t * buffer,struct sm_oob * oob)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*/
sm_read_sector(struct sm_ftl * ftl,int zone,int block,int boffset,uint8_t * buffer,struct sm_oob * 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;
242745df179SMichał Kępień 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
268cc9d663aSShubhankar Kuranagatti * won't help anyway
269cc9d663aSShubhankar 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,
280cc9d663aSShubhankar Kuranagatti * despite card removal.....
281cc9d663aSShubhankar 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 */
sm_write_sector(struct sm_ftl * ftl,int zone,int block,int boffset,uint8_t * buffer,struct sm_oob * oob)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 {
326745df179SMichał Kępień 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 */
sm_write_block(struct sm_ftl * ftl,uint8_t * buf,int zone,int block,int lba,unsigned long invalid_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
416cc9d663aSShubhankar Kuranagatti * that contain valuable data.
417cc9d663aSShubhankar Kuranagatti * This is intended to repair block that are marked
418cc9d663aSShubhankar Kuranagatti * as erased, but that isn't fully erased
419cc9d663aSShubhankar 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. */
sm_mark_block_bad(struct sm_ftl * ftl,int zone,int block)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
454cc9d663aSShubhankar Kuranagatti * any bad blocks till fail completely
455cc9d663aSShubhankar 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 */
sm_erase_block(struct sm_ftl * ftl,int zone_num,uint16_t block,int put_free)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. */
sm_check_block(struct sm_ftl * ftl,int zone,int block)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
512cc9d663aSShubhankar Kuranagatti * accepted
513cc9d663aSShubhankar 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.
562cc9d663aSShubhankar Kuranagatti * This ideally has to be based on nand id, but for now device size is enough
563cc9d663aSShubhankar Kuranagatti */
sm_get_media_info(struct sm_ftl * ftl,struct mtd_info * mtd)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
616cc9d663aSShubhankar Kuranagatti * sizes. SmartMedia cards exist up to 128 MiB and have same layout
617cc9d663aSShubhankar 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 */
sm_read_cis(struct sm_ftl * ftl)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 */
sm_find_cis(struct sm_ftl * ftl)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 */
sm_recheck_media(struct sm_ftl * ftl)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 */
sm_init_zone(struct sm_ftl * ftl,int zone_num)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
792cc9d663aSShubhankar Kuranagatti * first sector, because erase happens in one shot
793cc9d663aSShubhankar 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
803cc9d663aSShubhankar Kuranagatti * very low probability of failure here
804cc9d663aSShubhankar 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
815cc9d663aSShubhankar Kuranagatti * lets leave that to recovery application
816cc9d663aSShubhankar 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,
824cc9d663aSShubhankar Kuranagatti * just put the sector in the FTL table
825cc9d663aSShubhankar 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
848cc9d663aSShubhankar Kuranagatti * they hold different versions of same data. It not
849cc9d663aSShubhankar 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
859cc9d663aSShubhankar Kuranagatti * work, but it can still can be (partially) read
860cc9d663aSShubhankar 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 */
sm_get_zone(struct sm_ftl * ftl,int zone_num)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 */
sm_cache_init(struct sm_ftl * ftl)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 */
sm_cache_put(struct sm_ftl * ftl,char * buffer,int boffset)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 */
sm_cache_get(struct sm_ftl * ftl,char * buffer,int boffset)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 */
sm_cache_flush(struct sm_ftl * ftl)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,
967cc9d663aSShubhankar Kuranagatti * but for such worn out media it doesn't worth the trouble,
968cc9d663aSShubhankar Kuranagatti * and the dangers
969cc9d663aSShubhankar 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
984*efd2ed93SBo Liu /* Write successful, 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 */
sm_cache_flush_timer(struct timer_list * t)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 */
sm_cache_flush_work(struct work_struct * work)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 */
sm_read(struct mtd_blktrans_dev * dev,unsigned long sect_no,char * buf)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 */
sm_write(struct mtd_blktrans_dev * dev,unsigned long sec_no,char * buf)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 */
sm_flush(struct mtd_blktrans_dev * dev)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 */
sm_release(struct mtd_blktrans_dev * dev)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 del_timer_sync(&ftl->timer);
11157d17c02aSMaxim Levitsky cancel_work_sync(&ftl->flush_work);
1116a61528d9SDuoming Zhou mutex_lock(&ftl->mutex);
11177d17c02aSMaxim Levitsky sm_cache_flush(ftl);
11187d17c02aSMaxim Levitsky mutex_unlock(&ftl->mutex);
11197d17c02aSMaxim Levitsky }
11207d17c02aSMaxim Levitsky
11217d17c02aSMaxim Levitsky /* outside interface: get geometry */
sm_getgeo(struct mtd_blktrans_dev * dev,struct hd_geometry * geo)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 */
sm_add_mtd(struct mtd_blktrans_ops * tr,struct mtd_info * mtd)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 */
sm_remove_dev(struct mtd_blktrans_dev * dev)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
sm_module_init(void)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
sm_module_exit(void)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