Lines Matching +full:use +full:- +full:minimum +full:- +full:ecc
1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright © 2009 - Maxim Levitsky
16 #include <linux/mtd/nand-ecc-sw-hamming.h>
31 MODULE_PARM_DESC(debug, "Debug level (0-2)");
34 /* ------------------- sysfs attributes ---------------------------------- */
47 strncpy(buf, sm_attr->data, sm_attr->len); in sm_attr_show()
48 return sm_attr->len; in sm_attr_show()
61 vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, in sm_create_sysfs_attributes()
62 SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL); in sm_create_sysfs_attributes()
72 sysfs_attr_init(&vendor_attribute->dev_attr.attr); in sm_create_sysfs_attributes()
74 vendor_attribute->data = vendor; in sm_create_sysfs_attributes()
75 vendor_attribute->len = strlen(vendor); in sm_create_sysfs_attributes()
76 vendor_attribute->dev_attr.attr.name = "vendor"; in sm_create_sysfs_attributes()
77 vendor_attribute->dev_attr.attr.mode = S_IRUGO; in sm_create_sysfs_attributes()
78 vendor_attribute->dev_attr.show = sm_attr_show; in sm_create_sysfs_attributes()
86 attributes[0] = &vendor_attribute->dev_attr.attr; in sm_create_sysfs_attributes()
92 attr_group->attrs = attributes; in sm_create_sysfs_attributes()
106 struct attribute **attributes = ftl->disk_attributes->attrs; in sm_delete_sysfs_attributes()
118 kfree(sm_attr->data); in sm_delete_sysfs_attributes()
122 kfree(ftl->disk_attributes->attrs); in sm_delete_sysfs_attributes()
123 kfree(ftl->disk_attributes); in sm_delete_sysfs_attributes()
127 /* ----------------------- oob helpers -------------------------------------- */
133 return -2; in sm_get_lba()
135 /* check parity - endianness doesn't matter */ in sm_get_lba()
137 return -2; in sm_get_lba()
145 * returns -1, if block is erased
146 * returns -2 if error happens
158 return -1; in sm_read_lba()
161 lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2; in sm_read_lba()
163 return -2; in sm_read_lba()
166 lba = sm_get_lba(oob->lba_copy1); in sm_read_lba()
168 if (lba == -2) in sm_read_lba()
169 lba = sm_get_lba(oob->lba_copy2); in sm_read_lba()
186 oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0]; in sm_write_lba()
187 oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1]; in sm_write_lba()
194 WARN_ON(boffset & (SM_SECTOR_SIZE - 1)); in sm_mkoffset()
195 WARN_ON(zone < 0 || zone >= ftl->zone_count); in sm_mkoffset()
196 WARN_ON(block >= ftl->zone_size); in sm_mkoffset()
197 WARN_ON(boffset >= ftl->block_size); in sm_mkoffset()
199 if (block == -1) in sm_mkoffset()
200 return -1; in sm_mkoffset()
202 return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset; in sm_mkoffset()
210 *boffset = do_div(offset, ftl->block_size); in sm_break_offset()
211 *block = do_div(offset, ftl->max_lba); in sm_break_offset()
212 *zone = offset >= ftl->zone_count ? -1 : offset; in sm_break_offset()
215 /* ---------------------- low level IO ------------------------------------- */
220 uint8_t ecc[3]; in sm_correct_sector() local
222 ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order); in sm_correct_sector()
223 if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc1, SM_SMALL_PAGE, in sm_correct_sector()
225 return -EIO; in sm_correct_sector()
229 ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order); in sm_correct_sector()
230 if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc2, SM_SMALL_PAGE, in sm_correct_sector()
232 return -EIO; in sm_correct_sector()
241 struct mtd_info *mtd = ftl->trans->mtd; in sm_read_sector()
244 int ret = -EIO; in sm_read_sector()
247 /* FTL can contain -1 entries that are by default filled with bits */ in sm_read_sector()
248 if (block == -1) { in sm_read_sector()
258 ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; in sm_read_sector()
270 if (zone == 0 && block == ftl->cis_block && boffset == in sm_read_sector()
271 ftl->cis_boffset) in sm_read_sector()
292 if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved)) in sm_read_sector()
309 /* Test ECC*/ in sm_read_sector()
311 (ftl->smallpagenand && sm_correct_sector(buffer, oob))) { in sm_read_sector()
313 dbg("read of block %d at zone %d, failed due to ECC error", in sm_read_sector()
327 struct mtd_info *mtd = ftl->trans->mtd; in sm_write_sector()
330 BUG_ON(ftl->readonly); in sm_write_sector()
332 if (zone == 0 && (block == ftl->cis_block || block == 0)) { in sm_write_sector()
334 return -EIO; in sm_write_sector()
337 if (ftl->unstable) in sm_write_sector()
338 return -EIO; in sm_write_sector()
340 ops.mode = ftl->smallpagenand ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; in sm_write_sector()
366 /* ------------------------ block IO ------------------------------------- */
382 if (ftl->unstable) in sm_write_block()
383 return -EIO; in sm_write_block()
385 for (boffset = 0; boffset < ftl->block_size; in sm_write_block()
399 if (ftl->smallpagenand) { in sm_write_block()
422 return -EIO; in sm_write_block()
428 return -EIO; in sm_write_block()
444 if (ftl->unstable) in sm_mark_block_bad()
456 for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE) in sm_mark_block_bad()
467 struct ftl_zone *zone = &ftl->zones[zone_num]; in sm_erase_block()
468 struct mtd_info *mtd = ftl->trans->mtd; in sm_erase_block()
472 erase.len = ftl->block_size; in sm_erase_block()
474 if (ftl->unstable) in sm_erase_block()
475 return -EIO; in sm_erase_block()
477 BUG_ON(ftl->readonly); in sm_erase_block()
479 if (zone_num == 0 && (block == ftl->cis_block || block == 0)) { in sm_erase_block()
481 return -EIO; in sm_erase_block()
491 kfifo_in(&zone->free_sectors, in sm_erase_block()
497 return -EIO; in sm_erase_block()
505 int lbas[] = { -3, 0, 0, 0 }; in sm_check_block()
514 for (boffset = 0; boffset < ftl->block_size; in sm_check_block()
519 return -2; in sm_check_block()
528 return -EIO; in sm_check_block()
540 /* ----------------- media scanning --------------------------------- */
567 int size_in_megs = mtd->size / (1024 * 1024); in sm_get_media_info()
569 ftl->readonly = mtd->type == MTD_ROM; in sm_get_media_info()
572 ftl->zone_count = 1; in sm_get_media_info()
573 ftl->smallpagenand = 0; in sm_get_media_info()
578 ftl->zone_size = 256; in sm_get_media_info()
579 ftl->max_lba = 250; in sm_get_media_info()
580 ftl->block_size = 8 * SM_SECTOR_SIZE; in sm_get_media_info()
581 ftl->smallpagenand = 1; in sm_get_media_info()
586 if (mtd->writesize == SM_SMALL_PAGE) { in sm_get_media_info()
587 ftl->zone_size = 512; in sm_get_media_info()
588 ftl->max_lba = 500; in sm_get_media_info()
589 ftl->block_size = 8 * SM_SECTOR_SIZE; in sm_get_media_info()
590 ftl->smallpagenand = 1; in sm_get_media_info()
594 if (!ftl->readonly) in sm_get_media_info()
595 return -ENODEV; in sm_get_media_info()
597 ftl->zone_size = 256; in sm_get_media_info()
598 ftl->max_lba = 250; in sm_get_media_info()
599 ftl->block_size = 16 * SM_SECTOR_SIZE; in sm_get_media_info()
604 ftl->zone_size = 512; in sm_get_media_info()
605 ftl->max_lba = 500; in sm_get_media_info()
606 ftl->block_size = 16 * SM_SECTOR_SIZE; in sm_get_media_info()
610 ftl->zone_size = 1024; in sm_get_media_info()
611 ftl->max_lba = 1000; in sm_get_media_info()
612 ftl->block_size = 16 * SM_SECTOR_SIZE; in sm_get_media_info()
615 /* Minimum xD size is 16MiB. Also, all xD cards have standard zone in sm_get_media_info()
619 ftl->zone_count = size_in_megs / 16; in sm_get_media_info()
620 ftl->zone_size = 1024; in sm_get_media_info()
621 ftl->max_lba = 1000; in sm_get_media_info()
622 ftl->block_size = 32 * SM_SECTOR_SIZE; in sm_get_media_info()
626 if (mtd->erasesize > ftl->block_size) in sm_get_media_info()
627 return -ENODEV; in sm_get_media_info()
629 if (mtd->writesize > SM_SECTOR_SIZE) in sm_get_media_info()
630 return -ENODEV; in sm_get_media_info()
632 if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE) in sm_get_media_info()
633 return -ENODEV; in sm_get_media_info()
635 if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE) in sm_get_media_info()
636 return -ENODEV; in sm_get_media_info()
638 /* We use OOB */ in sm_get_media_info()
640 return -ENODEV; in sm_get_media_info()
645 ftl->cylinders = chs_table[i].cyl; in sm_get_media_info()
646 ftl->heads = chs_table[i].head; in sm_get_media_info()
647 ftl->sectors = chs_table[i].sec; in sm_get_media_info()
653 ftl->cylinders = 985; in sm_get_media_info()
654 ftl->heads = 33; in sm_get_media_info()
655 ftl->sectors = 63; in sm_get_media_info()
665 0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob)) in sm_read_cis()
666 return -EIO; in sm_read_cis()
669 return -EIO; in sm_read_cis()
671 if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset, in sm_read_cis()
676 return -EIO; in sm_read_cis()
688 for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) { in sm_find_cis()
700 return -EIO; in sm_find_cis()
703 for (boffset = 0 ; boffset < ftl->block_size; in sm_find_cis()
714 if (boffset == ftl->block_size) in sm_find_cis()
715 return -EIO; in sm_find_cis()
717 ftl->cis_block = block; in sm_find_cis()
718 ftl->cis_boffset = boffset; in sm_find_cis()
719 ftl->cis_page_offset = 0; in sm_find_cis()
724 ftl->cis_page_offset = SM_SMALL_PAGE; in sm_find_cis()
730 block * ftl->block_size + in sm_find_cis()
731 boffset + ftl->cis_page_offset); in sm_find_cis()
734 return -EIO; in sm_find_cis()
742 if (!ftl->unstable) { in sm_recheck_media()
744 ftl->unstable = 1; in sm_recheck_media()
746 return -EIO; in sm_recheck_media()
754 struct ftl_zone *zone = &ftl->zones[zone_num]; in sm_init_zone()
764 zone->lba_to_phys_table = kmalloc_array(ftl->max_lba, 2, GFP_KERNEL); in sm_init_zone()
766 if (!zone->lba_to_phys_table) in sm_init_zone()
767 return -ENOMEM; in sm_init_zone()
768 memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2); in sm_init_zone()
772 if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) { in sm_init_zone()
773 kfree(zone->lba_to_phys_table); in sm_init_zone()
774 return -ENOMEM; in sm_init_zone()
778 for (block = 0 ; block < ftl->zone_size ; block++) { in sm_init_zone()
781 if (zone_num == 0 && block <= ftl->cis_block) in sm_init_zone()
786 kfifo_free(&zone->free_sectors); in sm_init_zone()
787 kfree(zone->lba_to_phys_table); in sm_init_zone()
788 return -EIO; in sm_init_zone()
795 kfifo_in(&zone->free_sectors, in sm_init_zone()
806 dbg("PH %04d <-> <marked bad>", block); in sm_init_zone()
817 if (lba == -2 || lba >= ftl->max_lba) { in sm_init_zone()
818 dbg("PH %04d <-> LBA %04d(bad)", block, lba); in sm_init_zone()
826 if (zone->lba_to_phys_table[lba] < 0) { in sm_init_zone()
827 dbg_verbose("PH %04d <-> LBA %04d", block, lba); in sm_init_zone()
828 zone->lba_to_phys_table[lba] = block; in sm_init_zone()
834 lba, zone->lba_to_phys_table[lba], block, zone_num); in sm_init_zone()
842 zone->lba_to_phys_table[lba])) { in sm_init_zone()
843 zone->lba_to_phys_table[lba] = block; in sm_init_zone()
856 zone->initialized = 1; in sm_init_zone()
861 if (!kfifo_len(&zone->free_sectors)) { in sm_init_zone()
868 i %= (kfifo_len(&zone->free_sectors) / 2); in sm_init_zone()
870 while (i--) { in sm_init_zone()
871 len = kfifo_out(&zone->free_sectors, in sm_init_zone()
874 kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); in sm_init_zone()
885 BUG_ON(zone_num >= ftl->zone_count); in sm_get_zone()
886 zone = &ftl->zones[zone_num]; in sm_get_zone()
888 if (!zone->initialized) { in sm_get_zone()
898 /* ----------------- cache handling ------------------------------------------*/
903 ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; in sm_cache_init()
904 ftl->cache_clean = 1; in sm_cache_init()
905 ftl->cache_zone = -1; in sm_cache_init()
906 ftl->cache_block = -1; in sm_cache_init()
907 /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/ in sm_cache_init()
913 memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); in sm_cache_put()
914 clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); in sm_cache_put()
915 ftl->cache_clean = 0; in sm_cache_put()
922 &ftl->cache_data_invalid_bitmap)) in sm_cache_get()
923 return -1; in sm_cache_get()
925 memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE); in sm_cache_get()
936 int zone_num = ftl->cache_zone; in sm_cache_flush()
939 if (ftl->cache_clean) in sm_cache_flush()
942 if (ftl->unstable) in sm_cache_flush()
943 return -EIO; in sm_cache_flush()
946 zone = &ftl->zones[zone_num]; in sm_cache_flush()
947 block_num = zone->lba_to_phys_table[ftl->cache_block]; in sm_cache_flush()
951 for_each_set_bit(sector_num, &ftl->cache_data_invalid_bitmap, in sm_cache_flush()
952 ftl->block_size / SM_SECTOR_SIZE) { in sm_cache_flush()
956 ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL)) in sm_cache_flush()
958 &ftl->cache_data_invalid_bitmap); in sm_cache_flush()
962 if (ftl->unstable) in sm_cache_flush()
963 return -EIO; in sm_cache_flush()
970 if (kfifo_out(&zone->free_sectors, in sm_cache_flush()
973 return -EIO; in sm_cache_flush()
977 if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, in sm_cache_flush()
978 ftl->cache_block, ftl->cache_data_invalid_bitmap)) in sm_cache_flush()
982 zone->lba_to_phys_table[ftl->cache_block] = write_sector; in sm_cache_flush()
997 queue_work(cache_flush_workqueue, &ftl->flush_work); in sm_cache_flush_timer()
1004 mutex_lock(&ftl->mutex); in sm_cache_flush_work()
1006 mutex_unlock(&ftl->mutex); in sm_cache_flush_work()
1010 /* ---------------- outside interface -------------------------------------- */
1016 struct sm_ftl *ftl = dev->priv; in sm_read()
1022 mutex_lock(&ftl->mutex); in sm_read()
1032 if (ftl->cache_zone == zone_num && ftl->cache_block == block) { in sm_read()
1039 block = zone->lba_to_phys_table[block]; in sm_read()
1041 if (block == -1) { in sm_read()
1047 error = -EIO; in sm_read()
1054 mutex_unlock(&ftl->mutex); in sm_read()
1062 struct sm_ftl *ftl = dev->priv; in sm_write()
1066 BUG_ON(ftl->readonly); in sm_write()
1070 del_timer(&ftl->timer); in sm_write()
1071 mutex_lock(&ftl->mutex); in sm_write()
1080 if (ftl->cache_block != block || ftl->cache_zone != zone_num) { in sm_write()
1086 ftl->cache_block = block; in sm_write()
1087 ftl->cache_zone = zone_num; in sm_write()
1092 mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout)); in sm_write()
1093 mutex_unlock(&ftl->mutex); in sm_write()
1100 struct sm_ftl *ftl = dev->priv; in sm_flush()
1103 mutex_lock(&ftl->mutex); in sm_flush()
1105 mutex_unlock(&ftl->mutex); in sm_flush()
1112 struct sm_ftl *ftl = dev->priv; in sm_release()
1114 del_timer_sync(&ftl->timer); in sm_release()
1115 cancel_work_sync(&ftl->flush_work); in sm_release()
1116 mutex_lock(&ftl->mutex); in sm_release()
1118 mutex_unlock(&ftl->mutex); in sm_release()
1124 struct sm_ftl *ftl = dev->priv; in sm_getgeo()
1125 geo->heads = ftl->heads; in sm_getgeo()
1126 geo->sectors = ftl->sectors; in sm_getgeo()
1127 geo->cylinders = ftl->cylinders; in sm_getgeo()
1143 mutex_init(&ftl->mutex); in sm_add_mtd()
1144 timer_setup(&ftl->timer, sm_cache_flush_timer, 0); in sm_add_mtd()
1145 INIT_WORK(&ftl->flush_work, sm_cache_flush_work); in sm_add_mtd()
1155 ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); in sm_add_mtd()
1156 if (!ftl->cis_buffer) in sm_add_mtd()
1160 ftl->zones = kcalloc(ftl->zone_count, sizeof(struct ftl_zone), in sm_add_mtd()
1162 if (!ftl->zones) in sm_add_mtd()
1166 ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); in sm_add_mtd()
1168 if (!ftl->cache_data) in sm_add_mtd()
1179 ftl->trans = trans; in sm_add_mtd()
1180 trans->priv = ftl; in sm_add_mtd()
1182 trans->tr = tr; in sm_add_mtd()
1183 trans->mtd = mtd; in sm_add_mtd()
1184 trans->devnum = -1; in sm_add_mtd()
1185 trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9; in sm_add_mtd()
1186 trans->readonly = ftl->readonly; in sm_add_mtd()
1193 ftl->disk_attributes = sm_create_sysfs_attributes(ftl); in sm_add_mtd()
1194 if (!ftl->disk_attributes) in sm_add_mtd()
1196 trans->disk_attributes = ftl->disk_attributes; in sm_add_mtd()
1199 (int)(mtd->size / (1024 * 1024)), mtd->index); in sm_add_mtd()
1203 ftl->zone_count, ftl->max_lba, in sm_add_mtd()
1204 ftl->zone_size - ftl->max_lba); in sm_add_mtd()
1206 ftl->block_size); in sm_add_mtd()
1218 kfree(ftl->cache_data); in sm_add_mtd()
1220 kfree(ftl->zones); in sm_add_mtd()
1222 kfree(ftl->cis_buffer); in sm_add_mtd()
1232 struct sm_ftl *ftl = dev->priv; in sm_remove_dev()
1236 ftl->trans = NULL; in sm_remove_dev()
1238 for (i = 0 ; i < ftl->zone_count; i++) { in sm_remove_dev()
1240 if (!ftl->zones[i].initialized) in sm_remove_dev()
1243 kfree(ftl->zones[i].lba_to_phys_table); in sm_remove_dev()
1244 kfifo_free(&ftl->zones[i].free_sectors); in sm_remove_dev()
1248 kfree(ftl->cis_buffer); in sm_remove_dev()
1249 kfree(ftl->zones); in sm_remove_dev()
1250 kfree(ftl->cache_data); in sm_remove_dev()
1279 return -ENOMEM; in sm_module_init()