18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * sharpslpart.c - MTD partition parser for NAND flash using the SHARP FTL 38c2ecf20Sopenharmony_ci * for logical addressing, as used on the PXA models of the SHARP SL Series. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Andrea Adami <andrea.adami@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on SHARP GPL 2.4 sources: 88c2ecf20Sopenharmony_ci * http://support.ezaurus.com/developer/source/source_dl.asp 98c2ecf20Sopenharmony_ci * drivers/mtd/nand/sharp_sl_logical.c 108c2ecf20Sopenharmony_ci * linux/include/asm-arm/sharp_nand_logical.h 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 2002 SHARP 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 158c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 168c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 178c2ecf20Sopenharmony_ci * (at your option) any later version. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 208c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 218c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 228c2ecf20Sopenharmony_ci * GNU General Public License for more details. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/types.h> 308c2ecf20Sopenharmony_ci#include <linux/bitops.h> 318c2ecf20Sopenharmony_ci#include <linux/sizes.h> 328c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 338c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* oob structure */ 368c2ecf20Sopenharmony_ci#define NAND_NOOB_LOGADDR_00 8 378c2ecf20Sopenharmony_ci#define NAND_NOOB_LOGADDR_01 9 388c2ecf20Sopenharmony_ci#define NAND_NOOB_LOGADDR_10 10 398c2ecf20Sopenharmony_ci#define NAND_NOOB_LOGADDR_11 11 408c2ecf20Sopenharmony_ci#define NAND_NOOB_LOGADDR_20 12 418c2ecf20Sopenharmony_ci#define NAND_NOOB_LOGADDR_21 13 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define BLOCK_IS_RESERVED 0xffff 448c2ecf20Sopenharmony_ci#define BLOCK_UNMASK_COMPLEMENT 1 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* factory defaults */ 478c2ecf20Sopenharmony_ci#define SHARPSL_NAND_PARTS 3 488c2ecf20Sopenharmony_ci#define SHARPSL_FTL_PART_SIZE (7 * SZ_1M) 498c2ecf20Sopenharmony_ci#define SHARPSL_PARTINFO1_LADDR 0x00060000 508c2ecf20Sopenharmony_ci#define SHARPSL_PARTINFO2_LADDR 0x00064000 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define BOOT_MAGIC 0x424f4f54 538c2ecf20Sopenharmony_ci#define FSRO_MAGIC 0x4653524f 548c2ecf20Sopenharmony_ci#define FSRW_MAGIC 0x46535257 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * struct sharpsl_ftl - Sharp FTL Logical Table 588c2ecf20Sopenharmony_ci * @logmax: number of logical blocks 598c2ecf20Sopenharmony_ci * @log2phy: the logical-to-physical table 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Structure containing the logical-to-physical translation table 628c2ecf20Sopenharmony_ci * used by the SHARP SL FTL. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistruct sharpsl_ftl { 658c2ecf20Sopenharmony_ci unsigned int logmax; 668c2ecf20Sopenharmony_ci unsigned int *log2phy; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* verify that the OOB bytes 8 to 15 are free and available for the FTL */ 708c2ecf20Sopenharmony_cistatic int sharpsl_nand_check_ooblayout(struct mtd_info *mtd) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u8 freebytes = 0; 738c2ecf20Sopenharmony_ci int section = 0; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci while (true) { 768c2ecf20Sopenharmony_ci struct mtd_oob_region oobfree = { }; 778c2ecf20Sopenharmony_ci int ret, i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ret = mtd_ooblayout_free(mtd, section++, &oobfree); 808c2ecf20Sopenharmony_ci if (ret) 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!oobfree.length || oobfree.offset > 15 || 848c2ecf20Sopenharmony_ci (oobfree.offset + oobfree.length) < 8) 858c2ecf20Sopenharmony_ci continue; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci i = oobfree.offset >= 8 ? oobfree.offset : 8; 888c2ecf20Sopenharmony_ci for (; i < oobfree.offset + oobfree.length && i < 16; i++) 898c2ecf20Sopenharmony_ci freebytes |= BIT(i - 8); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (freebytes == 0xff) 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return -ENOTSUPP; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int sharpsl_nand_read_oob(struct mtd_info *mtd, loff_t offs, u8 *buf) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct mtd_oob_ops ops = { }; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 1048c2ecf20Sopenharmony_ci ops.ooblen = mtd->oobsize; 1058c2ecf20Sopenharmony_ci ops.oobbuf = buf; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = mtd_read_oob(mtd, offs, &ops); 1088c2ecf20Sopenharmony_ci if (ret != 0 || mtd->oobsize != ops.oobretlen) 1098c2ecf20Sopenharmony_ci return -1; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * The logical block number assigned to a physical block is stored in the OOB 1168c2ecf20Sopenharmony_ci * of the first page, in 3 16-bit copies with the following layout: 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * 01234567 89abcdef 1198c2ecf20Sopenharmony_ci * -------- -------- 1208c2ecf20Sopenharmony_ci * ECC BB xyxyxy 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * When reading we check that the first two copies agree. 1238c2ecf20Sopenharmony_ci * In case of error, matching is tried using the following pairs. 1248c2ecf20Sopenharmony_ci * Reserved values 0xffff mean the block is kept for wear leveling. 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * 01234567 89abcdef 1278c2ecf20Sopenharmony_ci * -------- -------- 1288c2ecf20Sopenharmony_ci * ECC BB xyxy oob[8]==oob[10] && oob[9]==oob[11] -> byte0=8 byte1=9 1298c2ecf20Sopenharmony_ci * ECC BB xyxy oob[10]==oob[12] && oob[11]==oob[13] -> byte0=10 byte1=11 1308c2ecf20Sopenharmony_ci * ECC BB xy xy oob[12]==oob[8] && oob[13]==oob[9] -> byte0=12 byte1=13 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic int sharpsl_nand_get_logical_num(u8 *oob) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u16 us; 1358c2ecf20Sopenharmony_ci int good0, good1; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (oob[NAND_NOOB_LOGADDR_00] == oob[NAND_NOOB_LOGADDR_10] && 1388c2ecf20Sopenharmony_ci oob[NAND_NOOB_LOGADDR_01] == oob[NAND_NOOB_LOGADDR_11]) { 1398c2ecf20Sopenharmony_ci good0 = NAND_NOOB_LOGADDR_00; 1408c2ecf20Sopenharmony_ci good1 = NAND_NOOB_LOGADDR_01; 1418c2ecf20Sopenharmony_ci } else if (oob[NAND_NOOB_LOGADDR_10] == oob[NAND_NOOB_LOGADDR_20] && 1428c2ecf20Sopenharmony_ci oob[NAND_NOOB_LOGADDR_11] == oob[NAND_NOOB_LOGADDR_21]) { 1438c2ecf20Sopenharmony_ci good0 = NAND_NOOB_LOGADDR_10; 1448c2ecf20Sopenharmony_ci good1 = NAND_NOOB_LOGADDR_11; 1458c2ecf20Sopenharmony_ci } else if (oob[NAND_NOOB_LOGADDR_20] == oob[NAND_NOOB_LOGADDR_00] && 1468c2ecf20Sopenharmony_ci oob[NAND_NOOB_LOGADDR_21] == oob[NAND_NOOB_LOGADDR_01]) { 1478c2ecf20Sopenharmony_ci good0 = NAND_NOOB_LOGADDR_20; 1488c2ecf20Sopenharmony_ci good1 = NAND_NOOB_LOGADDR_21; 1498c2ecf20Sopenharmony_ci } else { 1508c2ecf20Sopenharmony_ci return -EINVAL; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci us = oob[good0] | oob[good1] << 8; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* parity check */ 1568c2ecf20Sopenharmony_ci if (hweight16(us) & BLOCK_UNMASK_COMPLEMENT) 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* reserved */ 1608c2ecf20Sopenharmony_ci if (us == BLOCK_IS_RESERVED) 1618c2ecf20Sopenharmony_ci return BLOCK_IS_RESERVED; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return (us >> 1) & GENMASK(9, 0); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int sharpsl_nand_init_ftl(struct mtd_info *mtd, struct sharpsl_ftl *ftl) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci unsigned int block_num, phymax; 1698c2ecf20Sopenharmony_ci int i, ret, log_num; 1708c2ecf20Sopenharmony_ci loff_t block_adr; 1718c2ecf20Sopenharmony_ci u8 *oob; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci oob = kzalloc(mtd->oobsize, GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!oob) 1758c2ecf20Sopenharmony_ci return -ENOMEM; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci phymax = mtd_div_by_eb(SHARPSL_FTL_PART_SIZE, mtd); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* FTL reserves 5% of the blocks + 1 spare */ 1808c2ecf20Sopenharmony_ci ftl->logmax = ((phymax * 95) / 100) - 1; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ftl->log2phy = kmalloc_array(ftl->logmax, sizeof(*ftl->log2phy), 1838c2ecf20Sopenharmony_ci GFP_KERNEL); 1848c2ecf20Sopenharmony_ci if (!ftl->log2phy) { 1858c2ecf20Sopenharmony_ci ret = -ENOMEM; 1868c2ecf20Sopenharmony_ci goto exit; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* initialize ftl->log2phy */ 1908c2ecf20Sopenharmony_ci for (i = 0; i < ftl->logmax; i++) 1918c2ecf20Sopenharmony_ci ftl->log2phy[i] = UINT_MAX; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* create physical-logical table */ 1948c2ecf20Sopenharmony_ci for (block_num = 0; block_num < phymax; block_num++) { 1958c2ecf20Sopenharmony_ci block_adr = (loff_t)block_num * mtd->erasesize; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (mtd_block_isbad(mtd, block_adr)) 1988c2ecf20Sopenharmony_ci continue; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (sharpsl_nand_read_oob(mtd, block_adr, oob)) 2018c2ecf20Sopenharmony_ci continue; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* get logical block */ 2048c2ecf20Sopenharmony_ci log_num = sharpsl_nand_get_logical_num(oob); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* cut-off errors and skip the out-of-range values */ 2078c2ecf20Sopenharmony_ci if (log_num > 0 && log_num < ftl->logmax) { 2088c2ecf20Sopenharmony_ci if (ftl->log2phy[log_num] == UINT_MAX) 2098c2ecf20Sopenharmony_ci ftl->log2phy[log_num] = block_num; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci pr_info("Sharp SL FTL: %d blocks used (%d logical, %d reserved)\n", 2148c2ecf20Sopenharmony_ci phymax, ftl->logmax, phymax - ftl->logmax); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = 0; 2178c2ecf20Sopenharmony_ciexit: 2188c2ecf20Sopenharmony_ci kfree(oob); 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void sharpsl_nand_cleanup_ftl(struct sharpsl_ftl *ftl) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci kfree(ftl->log2phy); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int sharpsl_nand_read_laddr(struct mtd_info *mtd, 2288c2ecf20Sopenharmony_ci loff_t from, 2298c2ecf20Sopenharmony_ci size_t len, 2308c2ecf20Sopenharmony_ci void *buf, 2318c2ecf20Sopenharmony_ci struct sharpsl_ftl *ftl) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci unsigned int log_num, final_log_num; 2348c2ecf20Sopenharmony_ci unsigned int block_num; 2358c2ecf20Sopenharmony_ci loff_t block_adr; 2368c2ecf20Sopenharmony_ci loff_t block_ofs; 2378c2ecf20Sopenharmony_ci size_t retlen; 2388c2ecf20Sopenharmony_ci int err; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci log_num = mtd_div_by_eb((u32)from, mtd); 2418c2ecf20Sopenharmony_ci final_log_num = mtd_div_by_eb(((u32)from + len - 1), mtd); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (len <= 0 || log_num >= ftl->logmax || final_log_num > log_num) 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci block_num = ftl->log2phy[log_num]; 2478c2ecf20Sopenharmony_ci block_adr = (loff_t)block_num * mtd->erasesize; 2488c2ecf20Sopenharmony_ci block_ofs = mtd_mod_by_eb((u32)from, mtd); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci err = mtd_read(mtd, block_adr + block_ofs, len, &retlen, buf); 2518c2ecf20Sopenharmony_ci /* Ignore corrected ECC errors */ 2528c2ecf20Sopenharmony_ci if (mtd_is_bitflip(err)) 2538c2ecf20Sopenharmony_ci err = 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (!err && retlen != len) 2568c2ecf20Sopenharmony_ci err = -EIO; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (err) 2598c2ecf20Sopenharmony_ci pr_err("sharpslpart: error, read failed at %#llx\n", 2608c2ecf20Sopenharmony_ci block_adr + block_ofs); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return err; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* 2668c2ecf20Sopenharmony_ci * MTD Partition Parser 2678c2ecf20Sopenharmony_ci * 2688c2ecf20Sopenharmony_ci * Sample values read from SL-C860 2698c2ecf20Sopenharmony_ci * 2708c2ecf20Sopenharmony_ci * # cat /proc/mtd 2718c2ecf20Sopenharmony_ci * dev: size erasesize name 2728c2ecf20Sopenharmony_ci * mtd0: 006d0000 00020000 "Filesystem" 2738c2ecf20Sopenharmony_ci * mtd1: 00700000 00004000 "smf" 2748c2ecf20Sopenharmony_ci * mtd2: 03500000 00004000 "root" 2758c2ecf20Sopenharmony_ci * mtd3: 04400000 00004000 "home" 2768c2ecf20Sopenharmony_ci * 2778c2ecf20Sopenharmony_ci * PARTITIONINFO1 2788c2ecf20Sopenharmony_ci * 0x00060000: 00 00 00 00 00 00 70 00 42 4f 4f 54 00 00 00 00 ......p.BOOT.... 2798c2ecf20Sopenharmony_ci * 0x00060010: 00 00 70 00 00 00 c0 03 46 53 52 4f 00 00 00 00 ..p.....FSRO.... 2808c2ecf20Sopenharmony_ci * 0x00060020: 00 00 c0 03 00 00 00 04 46 53 52 57 00 00 00 00 ........FSRW.... 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistruct sharpsl_nand_partinfo { 2838c2ecf20Sopenharmony_ci __le32 start; 2848c2ecf20Sopenharmony_ci __le32 end; 2858c2ecf20Sopenharmony_ci __be32 magic; 2868c2ecf20Sopenharmony_ci u32 reserved; 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int sharpsl_nand_read_partinfo(struct mtd_info *master, 2908c2ecf20Sopenharmony_ci loff_t from, 2918c2ecf20Sopenharmony_ci size_t len, 2928c2ecf20Sopenharmony_ci struct sharpsl_nand_partinfo *buf, 2938c2ecf20Sopenharmony_ci struct sharpsl_ftl *ftl) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = sharpsl_nand_read_laddr(master, from, len, buf, ftl); 2988c2ecf20Sopenharmony_ci if (ret) 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* check for magics */ 3028c2ecf20Sopenharmony_ci if (be32_to_cpu(buf[0].magic) != BOOT_MAGIC || 3038c2ecf20Sopenharmony_ci be32_to_cpu(buf[1].magic) != FSRO_MAGIC || 3048c2ecf20Sopenharmony_ci be32_to_cpu(buf[2].magic) != FSRW_MAGIC) { 3058c2ecf20Sopenharmony_ci pr_err("sharpslpart: magic values mismatch\n"); 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* fixup for hardcoded value 64 MiB (for older models) */ 3108c2ecf20Sopenharmony_ci buf[2].end = cpu_to_le32(master->size); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* extra sanity check */ 3138c2ecf20Sopenharmony_ci if (le32_to_cpu(buf[0].end) <= le32_to_cpu(buf[0].start) || 3148c2ecf20Sopenharmony_ci le32_to_cpu(buf[1].start) < le32_to_cpu(buf[0].end) || 3158c2ecf20Sopenharmony_ci le32_to_cpu(buf[1].end) <= le32_to_cpu(buf[1].start) || 3168c2ecf20Sopenharmony_ci le32_to_cpu(buf[2].start) < le32_to_cpu(buf[1].end) || 3178c2ecf20Sopenharmony_ci le32_to_cpu(buf[2].end) <= le32_to_cpu(buf[2].start)) { 3188c2ecf20Sopenharmony_ci pr_err("sharpslpart: partition sizes mismatch\n"); 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int sharpsl_parse_mtd_partitions(struct mtd_info *master, 3268c2ecf20Sopenharmony_ci const struct mtd_partition **pparts, 3278c2ecf20Sopenharmony_ci struct mtd_part_parser_data *data) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct sharpsl_ftl ftl; 3308c2ecf20Sopenharmony_ci struct sharpsl_nand_partinfo buf[SHARPSL_NAND_PARTS]; 3318c2ecf20Sopenharmony_ci struct mtd_partition *sharpsl_nand_parts; 3328c2ecf20Sopenharmony_ci int err; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* check that OOB bytes 8 to 15 used by the FTL are actually free */ 3358c2ecf20Sopenharmony_ci err = sharpsl_nand_check_ooblayout(master); 3368c2ecf20Sopenharmony_ci if (err) 3378c2ecf20Sopenharmony_ci return err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* init logical mgmt (FTL) */ 3408c2ecf20Sopenharmony_ci err = sharpsl_nand_init_ftl(master, &ftl); 3418c2ecf20Sopenharmony_ci if (err) 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* read and validate first partition table */ 3458c2ecf20Sopenharmony_ci pr_info("sharpslpart: try reading first partition table\n"); 3468c2ecf20Sopenharmony_ci err = sharpsl_nand_read_partinfo(master, 3478c2ecf20Sopenharmony_ci SHARPSL_PARTINFO1_LADDR, 3488c2ecf20Sopenharmony_ci sizeof(buf), buf, &ftl); 3498c2ecf20Sopenharmony_ci if (err) { 3508c2ecf20Sopenharmony_ci /* fallback: read second partition table */ 3518c2ecf20Sopenharmony_ci pr_warn("sharpslpart: first partition table is invalid, retry using the second\n"); 3528c2ecf20Sopenharmony_ci err = sharpsl_nand_read_partinfo(master, 3538c2ecf20Sopenharmony_ci SHARPSL_PARTINFO2_LADDR, 3548c2ecf20Sopenharmony_ci sizeof(buf), buf, &ftl); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* cleanup logical mgmt (FTL) */ 3588c2ecf20Sopenharmony_ci sharpsl_nand_cleanup_ftl(&ftl); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (err) { 3618c2ecf20Sopenharmony_ci pr_err("sharpslpart: both partition tables are invalid\n"); 3628c2ecf20Sopenharmony_ci return err; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci sharpsl_nand_parts = kcalloc(SHARPSL_NAND_PARTS, 3668c2ecf20Sopenharmony_ci sizeof(*sharpsl_nand_parts), 3678c2ecf20Sopenharmony_ci GFP_KERNEL); 3688c2ecf20Sopenharmony_ci if (!sharpsl_nand_parts) 3698c2ecf20Sopenharmony_ci return -ENOMEM; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* original names */ 3728c2ecf20Sopenharmony_ci sharpsl_nand_parts[0].name = "smf"; 3738c2ecf20Sopenharmony_ci sharpsl_nand_parts[0].offset = le32_to_cpu(buf[0].start); 3748c2ecf20Sopenharmony_ci sharpsl_nand_parts[0].size = le32_to_cpu(buf[0].end) - 3758c2ecf20Sopenharmony_ci le32_to_cpu(buf[0].start); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci sharpsl_nand_parts[1].name = "root"; 3788c2ecf20Sopenharmony_ci sharpsl_nand_parts[1].offset = le32_to_cpu(buf[1].start); 3798c2ecf20Sopenharmony_ci sharpsl_nand_parts[1].size = le32_to_cpu(buf[1].end) - 3808c2ecf20Sopenharmony_ci le32_to_cpu(buf[1].start); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci sharpsl_nand_parts[2].name = "home"; 3838c2ecf20Sopenharmony_ci sharpsl_nand_parts[2].offset = le32_to_cpu(buf[2].start); 3848c2ecf20Sopenharmony_ci sharpsl_nand_parts[2].size = le32_to_cpu(buf[2].end) - 3858c2ecf20Sopenharmony_ci le32_to_cpu(buf[2].start); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci *pparts = sharpsl_nand_parts; 3888c2ecf20Sopenharmony_ci return SHARPSL_NAND_PARTS; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic struct mtd_part_parser sharpsl_mtd_parser = { 3928c2ecf20Sopenharmony_ci .parse_fn = sharpsl_parse_mtd_partitions, 3938c2ecf20Sopenharmony_ci .name = "sharpslpart", 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_cimodule_mtd_part_parser(sharpsl_mtd_parser); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrea Adami <andrea.adami@gmail.com>"); 3998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD partitioning for NAND flash on Sharp SL Series"); 400