162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * sharpslpart.c - MTD partition parser for NAND flash using the SHARP FTL 362306a36Sopenharmony_ci * for logical addressing, as used on the PXA models of the SHARP SL Series. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Andrea Adami <andrea.adami@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on SHARP GPL 2.4 sources: 862306a36Sopenharmony_ci * http://support.ezaurus.com/developer/source/source_dl.asp 962306a36Sopenharmony_ci * drivers/mtd/nand/sharp_sl_logical.c 1062306a36Sopenharmony_ci * linux/include/asm-arm/sharp_nand_logical.h 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (C) 2002 SHARP 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1562306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1662306a36Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 1762306a36Sopenharmony_ci * (at your option) any later version. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 2062306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 2162306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2262306a36Sopenharmony_ci * GNU General Public License for more details. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/types.h> 3062306a36Sopenharmony_ci#include <linux/bitops.h> 3162306a36Sopenharmony_ci#include <linux/sizes.h> 3262306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 3362306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* oob structure */ 3662306a36Sopenharmony_ci#define NAND_NOOB_LOGADDR_00 8 3762306a36Sopenharmony_ci#define NAND_NOOB_LOGADDR_01 9 3862306a36Sopenharmony_ci#define NAND_NOOB_LOGADDR_10 10 3962306a36Sopenharmony_ci#define NAND_NOOB_LOGADDR_11 11 4062306a36Sopenharmony_ci#define NAND_NOOB_LOGADDR_20 12 4162306a36Sopenharmony_ci#define NAND_NOOB_LOGADDR_21 13 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define BLOCK_IS_RESERVED 0xffff 4462306a36Sopenharmony_ci#define BLOCK_UNMASK_COMPLEMENT 1 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* factory defaults */ 4762306a36Sopenharmony_ci#define SHARPSL_NAND_PARTS 3 4862306a36Sopenharmony_ci#define SHARPSL_FTL_PART_SIZE (7 * SZ_1M) 4962306a36Sopenharmony_ci#define SHARPSL_PARTINFO1_LADDR 0x00060000 5062306a36Sopenharmony_ci#define SHARPSL_PARTINFO2_LADDR 0x00064000 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define BOOT_MAGIC 0x424f4f54 5362306a36Sopenharmony_ci#define FSRO_MAGIC 0x4653524f 5462306a36Sopenharmony_ci#define FSRW_MAGIC 0x46535257 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * struct sharpsl_ftl - Sharp FTL Logical Table 5862306a36Sopenharmony_ci * @logmax: number of logical blocks 5962306a36Sopenharmony_ci * @log2phy: the logical-to-physical table 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Structure containing the logical-to-physical translation table 6262306a36Sopenharmony_ci * used by the SHARP SL FTL. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistruct sharpsl_ftl { 6562306a36Sopenharmony_ci unsigned int logmax; 6662306a36Sopenharmony_ci unsigned int *log2phy; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* verify that the OOB bytes 8 to 15 are free and available for the FTL */ 7062306a36Sopenharmony_cistatic int sharpsl_nand_check_ooblayout(struct mtd_info *mtd) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u8 freebytes = 0; 7362306a36Sopenharmony_ci int section = 0; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci while (true) { 7662306a36Sopenharmony_ci struct mtd_oob_region oobfree = { }; 7762306a36Sopenharmony_ci int ret, i; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = mtd_ooblayout_free(mtd, section++, &oobfree); 8062306a36Sopenharmony_ci if (ret) 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!oobfree.length || oobfree.offset > 15 || 8462306a36Sopenharmony_ci (oobfree.offset + oobfree.length) < 8) 8562306a36Sopenharmony_ci continue; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci i = oobfree.offset >= 8 ? oobfree.offset : 8; 8862306a36Sopenharmony_ci for (; i < oobfree.offset + oobfree.length && i < 16; i++) 8962306a36Sopenharmony_ci freebytes |= BIT(i - 8); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (freebytes == 0xff) 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return -ENOTSUPP; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int sharpsl_nand_read_oob(struct mtd_info *mtd, loff_t offs, u8 *buf) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct mtd_oob_ops ops = { }; 10162306a36Sopenharmony_ci int ret; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ops.mode = MTD_OPS_PLACE_OOB; 10462306a36Sopenharmony_ci ops.ooblen = mtd->oobsize; 10562306a36Sopenharmony_ci ops.oobbuf = buf; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = mtd_read_oob(mtd, offs, &ops); 10862306a36Sopenharmony_ci if (ret != 0 || mtd->oobsize != ops.oobretlen) 10962306a36Sopenharmony_ci return -1; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * The logical block number assigned to a physical block is stored in the OOB 11662306a36Sopenharmony_ci * of the first page, in 3 16-bit copies with the following layout: 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * 01234567 89abcdef 11962306a36Sopenharmony_ci * -------- -------- 12062306a36Sopenharmony_ci * ECC BB xyxyxy 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * When reading we check that the first two copies agree. 12362306a36Sopenharmony_ci * In case of error, matching is tried using the following pairs. 12462306a36Sopenharmony_ci * Reserved values 0xffff mean the block is kept for wear leveling. 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * 01234567 89abcdef 12762306a36Sopenharmony_ci * -------- -------- 12862306a36Sopenharmony_ci * ECC BB xyxy oob[8]==oob[10] && oob[9]==oob[11] -> byte0=8 byte1=9 12962306a36Sopenharmony_ci * ECC BB xyxy oob[10]==oob[12] && oob[11]==oob[13] -> byte0=10 byte1=11 13062306a36Sopenharmony_ci * ECC BB xy xy oob[12]==oob[8] && oob[13]==oob[9] -> byte0=12 byte1=13 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic int sharpsl_nand_get_logical_num(u8 *oob) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u16 us; 13562306a36Sopenharmony_ci int good0, good1; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (oob[NAND_NOOB_LOGADDR_00] == oob[NAND_NOOB_LOGADDR_10] && 13862306a36Sopenharmony_ci oob[NAND_NOOB_LOGADDR_01] == oob[NAND_NOOB_LOGADDR_11]) { 13962306a36Sopenharmony_ci good0 = NAND_NOOB_LOGADDR_00; 14062306a36Sopenharmony_ci good1 = NAND_NOOB_LOGADDR_01; 14162306a36Sopenharmony_ci } else if (oob[NAND_NOOB_LOGADDR_10] == oob[NAND_NOOB_LOGADDR_20] && 14262306a36Sopenharmony_ci oob[NAND_NOOB_LOGADDR_11] == oob[NAND_NOOB_LOGADDR_21]) { 14362306a36Sopenharmony_ci good0 = NAND_NOOB_LOGADDR_10; 14462306a36Sopenharmony_ci good1 = NAND_NOOB_LOGADDR_11; 14562306a36Sopenharmony_ci } else if (oob[NAND_NOOB_LOGADDR_20] == oob[NAND_NOOB_LOGADDR_00] && 14662306a36Sopenharmony_ci oob[NAND_NOOB_LOGADDR_21] == oob[NAND_NOOB_LOGADDR_01]) { 14762306a36Sopenharmony_ci good0 = NAND_NOOB_LOGADDR_20; 14862306a36Sopenharmony_ci good1 = NAND_NOOB_LOGADDR_21; 14962306a36Sopenharmony_ci } else { 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci us = oob[good0] | oob[good1] << 8; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* parity check */ 15662306a36Sopenharmony_ci if (hweight16(us) & BLOCK_UNMASK_COMPLEMENT) 15762306a36Sopenharmony_ci return -EINVAL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* reserved */ 16062306a36Sopenharmony_ci if (us == BLOCK_IS_RESERVED) 16162306a36Sopenharmony_ci return BLOCK_IS_RESERVED; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return (us >> 1) & GENMASK(9, 0); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int sharpsl_nand_init_ftl(struct mtd_info *mtd, struct sharpsl_ftl *ftl) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci unsigned int block_num, phymax; 16962306a36Sopenharmony_ci int i, ret, log_num; 17062306a36Sopenharmony_ci loff_t block_adr; 17162306a36Sopenharmony_ci u8 *oob; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci oob = kzalloc(mtd->oobsize, GFP_KERNEL); 17462306a36Sopenharmony_ci if (!oob) 17562306a36Sopenharmony_ci return -ENOMEM; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci phymax = mtd_div_by_eb(SHARPSL_FTL_PART_SIZE, mtd); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* FTL reserves 5% of the blocks + 1 spare */ 18062306a36Sopenharmony_ci ftl->logmax = ((phymax * 95) / 100) - 1; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ftl->log2phy = kmalloc_array(ftl->logmax, sizeof(*ftl->log2phy), 18362306a36Sopenharmony_ci GFP_KERNEL); 18462306a36Sopenharmony_ci if (!ftl->log2phy) { 18562306a36Sopenharmony_ci ret = -ENOMEM; 18662306a36Sopenharmony_ci goto exit; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* initialize ftl->log2phy */ 19062306a36Sopenharmony_ci for (i = 0; i < ftl->logmax; i++) 19162306a36Sopenharmony_ci ftl->log2phy[i] = UINT_MAX; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* create physical-logical table */ 19462306a36Sopenharmony_ci for (block_num = 0; block_num < phymax; block_num++) { 19562306a36Sopenharmony_ci block_adr = (loff_t)block_num * mtd->erasesize; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (mtd_block_isbad(mtd, block_adr)) 19862306a36Sopenharmony_ci continue; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (sharpsl_nand_read_oob(mtd, block_adr, oob)) 20162306a36Sopenharmony_ci continue; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* get logical block */ 20462306a36Sopenharmony_ci log_num = sharpsl_nand_get_logical_num(oob); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* cut-off errors and skip the out-of-range values */ 20762306a36Sopenharmony_ci if (log_num > 0 && log_num < ftl->logmax) { 20862306a36Sopenharmony_ci if (ftl->log2phy[log_num] == UINT_MAX) 20962306a36Sopenharmony_ci ftl->log2phy[log_num] = block_num; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pr_info("Sharp SL FTL: %d blocks used (%d logical, %d reserved)\n", 21462306a36Sopenharmony_ci phymax, ftl->logmax, phymax - ftl->logmax); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = 0; 21762306a36Sopenharmony_ciexit: 21862306a36Sopenharmony_ci kfree(oob); 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void sharpsl_nand_cleanup_ftl(struct sharpsl_ftl *ftl) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci kfree(ftl->log2phy); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int sharpsl_nand_read_laddr(struct mtd_info *mtd, 22862306a36Sopenharmony_ci loff_t from, 22962306a36Sopenharmony_ci size_t len, 23062306a36Sopenharmony_ci void *buf, 23162306a36Sopenharmony_ci struct sharpsl_ftl *ftl) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci unsigned int log_num, final_log_num; 23462306a36Sopenharmony_ci unsigned int block_num; 23562306a36Sopenharmony_ci loff_t block_adr; 23662306a36Sopenharmony_ci loff_t block_ofs; 23762306a36Sopenharmony_ci size_t retlen; 23862306a36Sopenharmony_ci int err; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci log_num = mtd_div_by_eb((u32)from, mtd); 24162306a36Sopenharmony_ci final_log_num = mtd_div_by_eb(((u32)from + len - 1), mtd); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (len <= 0 || log_num >= ftl->logmax || final_log_num > log_num) 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci block_num = ftl->log2phy[log_num]; 24762306a36Sopenharmony_ci block_adr = (loff_t)block_num * mtd->erasesize; 24862306a36Sopenharmony_ci block_ofs = mtd_mod_by_eb((u32)from, mtd); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci err = mtd_read(mtd, block_adr + block_ofs, len, &retlen, buf); 25162306a36Sopenharmony_ci /* Ignore corrected ECC errors */ 25262306a36Sopenharmony_ci if (mtd_is_bitflip(err)) 25362306a36Sopenharmony_ci err = 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!err && retlen != len) 25662306a36Sopenharmony_ci err = -EIO; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (err) 25962306a36Sopenharmony_ci pr_err("sharpslpart: error, read failed at %#llx\n", 26062306a36Sopenharmony_ci block_adr + block_ofs); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return err; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * MTD Partition Parser 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * Sample values read from SL-C860 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * # cat /proc/mtd 27162306a36Sopenharmony_ci * dev: size erasesize name 27262306a36Sopenharmony_ci * mtd0: 006d0000 00020000 "Filesystem" 27362306a36Sopenharmony_ci * mtd1: 00700000 00004000 "smf" 27462306a36Sopenharmony_ci * mtd2: 03500000 00004000 "root" 27562306a36Sopenharmony_ci * mtd3: 04400000 00004000 "home" 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * PARTITIONINFO1 27862306a36Sopenharmony_ci * 0x00060000: 00 00 00 00 00 00 70 00 42 4f 4f 54 00 00 00 00 ......p.BOOT.... 27962306a36Sopenharmony_ci * 0x00060010: 00 00 70 00 00 00 c0 03 46 53 52 4f 00 00 00 00 ..p.....FSRO.... 28062306a36Sopenharmony_ci * 0x00060020: 00 00 c0 03 00 00 00 04 46 53 52 57 00 00 00 00 ........FSRW.... 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_cistruct sharpsl_nand_partinfo { 28362306a36Sopenharmony_ci __le32 start; 28462306a36Sopenharmony_ci __le32 end; 28562306a36Sopenharmony_ci __be32 magic; 28662306a36Sopenharmony_ci u32 reserved; 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int sharpsl_nand_read_partinfo(struct mtd_info *master, 29062306a36Sopenharmony_ci loff_t from, 29162306a36Sopenharmony_ci size_t len, 29262306a36Sopenharmony_ci struct sharpsl_nand_partinfo *buf, 29362306a36Sopenharmony_ci struct sharpsl_ftl *ftl) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = sharpsl_nand_read_laddr(master, from, len, buf, ftl); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* check for magics */ 30262306a36Sopenharmony_ci if (be32_to_cpu(buf[0].magic) != BOOT_MAGIC || 30362306a36Sopenharmony_ci be32_to_cpu(buf[1].magic) != FSRO_MAGIC || 30462306a36Sopenharmony_ci be32_to_cpu(buf[2].magic) != FSRW_MAGIC) { 30562306a36Sopenharmony_ci pr_err("sharpslpart: magic values mismatch\n"); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* fixup for hardcoded value 64 MiB (for older models) */ 31062306a36Sopenharmony_ci buf[2].end = cpu_to_le32(master->size); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* extra sanity check */ 31362306a36Sopenharmony_ci if (le32_to_cpu(buf[0].end) <= le32_to_cpu(buf[0].start) || 31462306a36Sopenharmony_ci le32_to_cpu(buf[1].start) < le32_to_cpu(buf[0].end) || 31562306a36Sopenharmony_ci le32_to_cpu(buf[1].end) <= le32_to_cpu(buf[1].start) || 31662306a36Sopenharmony_ci le32_to_cpu(buf[2].start) < le32_to_cpu(buf[1].end) || 31762306a36Sopenharmony_ci le32_to_cpu(buf[2].end) <= le32_to_cpu(buf[2].start)) { 31862306a36Sopenharmony_ci pr_err("sharpslpart: partition sizes mismatch\n"); 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int sharpsl_parse_mtd_partitions(struct mtd_info *master, 32662306a36Sopenharmony_ci const struct mtd_partition **pparts, 32762306a36Sopenharmony_ci struct mtd_part_parser_data *data) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct sharpsl_ftl ftl; 33062306a36Sopenharmony_ci struct sharpsl_nand_partinfo buf[SHARPSL_NAND_PARTS]; 33162306a36Sopenharmony_ci struct mtd_partition *sharpsl_nand_parts; 33262306a36Sopenharmony_ci int err; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* check that OOB bytes 8 to 15 used by the FTL are actually free */ 33562306a36Sopenharmony_ci err = sharpsl_nand_check_ooblayout(master); 33662306a36Sopenharmony_ci if (err) 33762306a36Sopenharmony_ci return err; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* init logical mgmt (FTL) */ 34062306a36Sopenharmony_ci err = sharpsl_nand_init_ftl(master, &ftl); 34162306a36Sopenharmony_ci if (err) 34262306a36Sopenharmony_ci return err; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* read and validate first partition table */ 34562306a36Sopenharmony_ci pr_info("sharpslpart: try reading first partition table\n"); 34662306a36Sopenharmony_ci err = sharpsl_nand_read_partinfo(master, 34762306a36Sopenharmony_ci SHARPSL_PARTINFO1_LADDR, 34862306a36Sopenharmony_ci sizeof(buf), buf, &ftl); 34962306a36Sopenharmony_ci if (err) { 35062306a36Sopenharmony_ci /* fallback: read second partition table */ 35162306a36Sopenharmony_ci pr_warn("sharpslpart: first partition table is invalid, retry using the second\n"); 35262306a36Sopenharmony_ci err = sharpsl_nand_read_partinfo(master, 35362306a36Sopenharmony_ci SHARPSL_PARTINFO2_LADDR, 35462306a36Sopenharmony_ci sizeof(buf), buf, &ftl); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* cleanup logical mgmt (FTL) */ 35862306a36Sopenharmony_ci sharpsl_nand_cleanup_ftl(&ftl); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (err) { 36162306a36Sopenharmony_ci pr_err("sharpslpart: both partition tables are invalid\n"); 36262306a36Sopenharmony_ci return err; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci sharpsl_nand_parts = kcalloc(SHARPSL_NAND_PARTS, 36662306a36Sopenharmony_ci sizeof(*sharpsl_nand_parts), 36762306a36Sopenharmony_ci GFP_KERNEL); 36862306a36Sopenharmony_ci if (!sharpsl_nand_parts) 36962306a36Sopenharmony_ci return -ENOMEM; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* original names */ 37262306a36Sopenharmony_ci sharpsl_nand_parts[0].name = "smf"; 37362306a36Sopenharmony_ci sharpsl_nand_parts[0].offset = le32_to_cpu(buf[0].start); 37462306a36Sopenharmony_ci sharpsl_nand_parts[0].size = le32_to_cpu(buf[0].end) - 37562306a36Sopenharmony_ci le32_to_cpu(buf[0].start); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci sharpsl_nand_parts[1].name = "root"; 37862306a36Sopenharmony_ci sharpsl_nand_parts[1].offset = le32_to_cpu(buf[1].start); 37962306a36Sopenharmony_ci sharpsl_nand_parts[1].size = le32_to_cpu(buf[1].end) - 38062306a36Sopenharmony_ci le32_to_cpu(buf[1].start); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci sharpsl_nand_parts[2].name = "home"; 38362306a36Sopenharmony_ci sharpsl_nand_parts[2].offset = le32_to_cpu(buf[2].start); 38462306a36Sopenharmony_ci sharpsl_nand_parts[2].size = le32_to_cpu(buf[2].end) - 38562306a36Sopenharmony_ci le32_to_cpu(buf[2].start); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci *pparts = sharpsl_nand_parts; 38862306a36Sopenharmony_ci return SHARPSL_NAND_PARTS; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic struct mtd_part_parser sharpsl_mtd_parser = { 39262306a36Sopenharmony_ci .parse_fn = sharpsl_parse_mtd_partitions, 39362306a36Sopenharmony_ci .name = "sharpslpart", 39462306a36Sopenharmony_ci}; 39562306a36Sopenharmony_cimodule_mtd_part_parser(sharpsl_mtd_parser); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 39862306a36Sopenharmony_ciMODULE_AUTHOR("Andrea Adami <andrea.adami@gmail.com>"); 39962306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD partitioning for NAND flash on Sharp SL Series"); 400