linux/drivers/mtd/parsers/sharpslpart.c
<<
>>
Prefs
   1/*
   2 * sharpslpart.c - MTD partition parser for NAND flash using the SHARP FTL
   3 * for logical addressing, as used on the PXA models of the SHARP SL Series.
   4 *
   5 * Copyright (C) 2017 Andrea Adami <andrea.adami@gmail.com>
   6 *
   7 * Based on SHARP GPL 2.4 sources:
   8 *   http://support.ezaurus.com/developer/source/source_dl.asp
   9 *     drivers/mtd/nand/sharp_sl_logical.c
  10 *     linux/include/asm-arm/sharp_nand_logical.h
  11 *
  12 * Copyright (C) 2002 SHARP
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as published by
  16 * the Free Software Foundation; either version 2 of the License, or
  17 * (at your option) any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 * GNU General Public License for more details.
  23 *
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/slab.h>
  28#include <linux/module.h>
  29#include <linux/types.h>
  30#include <linux/bitops.h>
  31#include <linux/sizes.h>
  32#include <linux/mtd/mtd.h>
  33#include <linux/mtd/partitions.h>
  34
  35/* oob structure */
  36#define NAND_NOOB_LOGADDR_00            8
  37#define NAND_NOOB_LOGADDR_01            9
  38#define NAND_NOOB_LOGADDR_10            10
  39#define NAND_NOOB_LOGADDR_11            11
  40#define NAND_NOOB_LOGADDR_20            12
  41#define NAND_NOOB_LOGADDR_21            13
  42
  43#define BLOCK_IS_RESERVED               0xffff
  44#define BLOCK_UNMASK_COMPLEMENT         1
  45
  46/* factory defaults */
  47#define SHARPSL_NAND_PARTS              3
  48#define SHARPSL_FTL_PART_SIZE           (7 * SZ_1M)
  49#define SHARPSL_PARTINFO1_LADDR         0x00060000
  50#define SHARPSL_PARTINFO2_LADDR         0x00064000
  51
  52#define BOOT_MAGIC                      0x424f4f54
  53#define FSRO_MAGIC                      0x4653524f
  54#define FSRW_MAGIC                      0x46535257
  55
  56/**
  57 * struct sharpsl_ftl - Sharp FTL Logical Table
  58 * @logmax:             number of logical blocks
  59 * @log2phy:            the logical-to-physical table
  60 *
  61 * Structure containing the logical-to-physical translation table
  62 * used by the SHARP SL FTL.
  63 */
  64struct sharpsl_ftl {
  65        unsigned int logmax;
  66        unsigned int *log2phy;
  67};
  68
  69/* verify that the OOB bytes 8 to 15 are free and available for the FTL */
  70static int sharpsl_nand_check_ooblayout(struct mtd_info *mtd)
  71{
  72        u8 freebytes = 0;
  73        int section = 0;
  74
  75        while (true) {
  76                struct mtd_oob_region oobfree = { };
  77                int ret, i;
  78
  79                ret = mtd_ooblayout_free(mtd, section++, &oobfree);
  80                if (ret)
  81                        break;
  82
  83                if (!oobfree.length || oobfree.offset > 15 ||
  84                    (oobfree.offset + oobfree.length) < 8)
  85                        continue;
  86
  87                i = oobfree.offset >= 8 ? oobfree.offset : 8;
  88                for (; i < oobfree.offset + oobfree.length && i < 16; i++)
  89                        freebytes |= BIT(i - 8);
  90
  91                if (freebytes == 0xff)
  92                        return 0;
  93        }
  94
  95        return -ENOTSUPP;
  96}
  97
  98static int sharpsl_nand_read_oob(struct mtd_info *mtd, loff_t offs, u8 *buf)
  99{
 100        struct mtd_oob_ops ops = { };
 101        int ret;
 102
 103        ops.mode = MTD_OPS_PLACE_OOB;
 104        ops.ooblen = mtd->oobsize;
 105        ops.oobbuf = buf;
 106
 107        ret = mtd_read_oob(mtd, offs, &ops);
 108        if (ret != 0 || mtd->oobsize != ops.oobretlen)
 109                return -1;
 110
 111        return 0;
 112}
 113
 114/*
 115 * The logical block number assigned to a physical block is stored in the OOB
 116 * of the first page, in 3 16-bit copies with the following layout:
 117 *
 118 * 01234567 89abcdef
 119 * -------- --------
 120 * ECC BB   xyxyxy
 121 *
 122 * When reading we check that the first two copies agree.
 123 * In case of error, matching is tried using the following pairs.
 124 * Reserved values 0xffff mean the block is kept for wear leveling.
 125 *
 126 * 01234567 89abcdef
 127 * -------- --------
 128 * ECC BB   xyxy    oob[8]==oob[10] && oob[9]==oob[11]   -> byte0=8   byte1=9
 129 * ECC BB     xyxy  oob[10]==oob[12] && oob[11]==oob[13] -> byte0=10  byte1=11
 130 * ECC BB   xy  xy  oob[12]==oob[8] && oob[13]==oob[9]   -> byte0=12  byte1=13
 131 */
 132static int sharpsl_nand_get_logical_num(u8 *oob)
 133{
 134        u16 us;
 135        int good0, good1;
 136
 137        if (oob[NAND_NOOB_LOGADDR_00] == oob[NAND_NOOB_LOGADDR_10] &&
 138            oob[NAND_NOOB_LOGADDR_01] == oob[NAND_NOOB_LOGADDR_11]) {
 139                good0 = NAND_NOOB_LOGADDR_00;
 140                good1 = NAND_NOOB_LOGADDR_01;
 141        } else if (oob[NAND_NOOB_LOGADDR_10] == oob[NAND_NOOB_LOGADDR_20] &&
 142                   oob[NAND_NOOB_LOGADDR_11] == oob[NAND_NOOB_LOGADDR_21]) {
 143                good0 = NAND_NOOB_LOGADDR_10;
 144                good1 = NAND_NOOB_LOGADDR_11;
 145        } else if (oob[NAND_NOOB_LOGADDR_20] == oob[NAND_NOOB_LOGADDR_00] &&
 146                   oob[NAND_NOOB_LOGADDR_21] == oob[NAND_NOOB_LOGADDR_01]) {
 147                good0 = NAND_NOOB_LOGADDR_20;
 148                good1 = NAND_NOOB_LOGADDR_21;
 149        } else {
 150                return -EINVAL;
 151        }
 152
 153        us = oob[good0] | oob[good1] << 8;
 154
 155        /* parity check */
 156        if (hweight16(us) & BLOCK_UNMASK_COMPLEMENT)
 157                return -EINVAL;
 158
 159        /* reserved */
 160        if (us == BLOCK_IS_RESERVED)
 161                return BLOCK_IS_RESERVED;
 162
 163        return (us >> 1) & GENMASK(9, 0);
 164}
 165
 166static int sharpsl_nand_init_ftl(struct mtd_info *mtd, struct sharpsl_ftl *ftl)
 167{
 168        unsigned int block_num, log_num, phymax;
 169        loff_t block_adr;
 170        u8 *oob;
 171        int i, ret;
 172
 173        oob = kzalloc(mtd->oobsize, GFP_KERNEL);
 174        if (!oob)
 175                return -ENOMEM;
 176
 177        phymax = mtd_div_by_eb(SHARPSL_FTL_PART_SIZE, mtd);
 178
 179        /* FTL reserves 5% of the blocks + 1 spare  */
 180        ftl->logmax = ((phymax * 95) / 100) - 1;
 181
 182        ftl->log2phy = kmalloc_array(ftl->logmax, sizeof(*ftl->log2phy),
 183                                     GFP_KERNEL);
 184        if (!ftl->log2phy) {
 185                ret = -ENOMEM;
 186                goto exit;
 187        }
 188
 189        /* initialize ftl->log2phy */
 190        for (i = 0; i < ftl->logmax; i++)
 191                ftl->log2phy[i] = UINT_MAX;
 192
 193        /* create physical-logical table */
 194        for (block_num = 0; block_num < phymax; block_num++) {
 195                block_adr = (loff_t)block_num * mtd->erasesize;
 196
 197                if (mtd_block_isbad(mtd, block_adr))
 198                        continue;
 199
 200                if (sharpsl_nand_read_oob(mtd, block_adr, oob))
 201                        continue;
 202
 203                /* get logical block */
 204                log_num = sharpsl_nand_get_logical_num(oob);
 205
 206                /* cut-off errors and skip the out-of-range values */
 207                if (log_num > 0 && log_num < ftl->logmax) {
 208                        if (ftl->log2phy[log_num] == UINT_MAX)
 209                                ftl->log2phy[log_num] = block_num;
 210                }
 211        }
 212
 213        pr_info("Sharp SL FTL: %d blocks used (%d logical, %d reserved)\n",
 214                phymax, ftl->logmax, phymax - ftl->logmax);
 215
 216        ret = 0;
 217exit:
 218        kfree(oob);
 219        return ret;
 220}
 221
 222static void sharpsl_nand_cleanup_ftl(struct sharpsl_ftl *ftl)
 223{
 224        kfree(ftl->log2phy);
 225}
 226
 227static int sharpsl_nand_read_laddr(struct mtd_info *mtd,
 228                                   loff_t from,
 229                                   size_t len,
 230                                   void *buf,
 231                                   struct sharpsl_ftl *ftl)
 232{
 233        unsigned int log_num, final_log_num;
 234        unsigned int block_num;
 235        loff_t block_adr;
 236        loff_t block_ofs;
 237        size_t retlen;
 238        int err;
 239
 240        log_num = mtd_div_by_eb((u32)from, mtd);
 241        final_log_num = mtd_div_by_eb(((u32)from + len - 1), mtd);
 242
 243        if (len <= 0 || log_num >= ftl->logmax || final_log_num > log_num)
 244                return -EINVAL;
 245
 246        block_num = ftl->log2phy[log_num];
 247        block_adr = (loff_t)block_num * mtd->erasesize;
 248        block_ofs = mtd_mod_by_eb((u32)from, mtd);
 249
 250        err = mtd_read(mtd, block_adr + block_ofs, len, &retlen, buf);
 251        /* Ignore corrected ECC errors */
 252        if (mtd_is_bitflip(err))
 253                err = 0;
 254
 255        if (!err && retlen != len)
 256                err = -EIO;
 257
 258        if (err)
 259                pr_err("sharpslpart: error, read failed at %#llx\n",
 260                       block_adr + block_ofs);
 261
 262        return err;
 263}
 264
 265/*
 266 * MTD Partition Parser
 267 *
 268 * Sample values read from SL-C860
 269 *
 270 * # cat /proc/mtd
 271 * dev:    size   erasesize  name
 272 * mtd0: 006d0000 00020000 "Filesystem"
 273 * mtd1: 00700000 00004000 "smf"
 274 * mtd2: 03500000 00004000 "root"
 275 * mtd3: 04400000 00004000 "home"
 276 *
 277 * PARTITIONINFO1
 278 * 0x00060000: 00 00 00 00 00 00 70 00 42 4f 4f 54 00 00 00 00  ......p.BOOT....
 279 * 0x00060010: 00 00 70 00 00 00 c0 03 46 53 52 4f 00 00 00 00  ..p.....FSRO....
 280 * 0x00060020: 00 00 c0 03 00 00 00 04 46 53 52 57 00 00 00 00  ........FSRW....
 281 */
 282struct sharpsl_nand_partinfo {
 283        __le32 start;
 284        __le32 end;
 285        __be32 magic;
 286        u32 reserved;
 287};
 288
 289static int sharpsl_nand_read_partinfo(struct mtd_info *master,
 290                                      loff_t from,
 291                                      size_t len,
 292                                      struct sharpsl_nand_partinfo *buf,
 293                                      struct sharpsl_ftl *ftl)
 294{
 295        int ret;
 296
 297        ret = sharpsl_nand_read_laddr(master, from, len, buf, ftl);
 298        if (ret)
 299                return ret;
 300
 301        /* check for magics */
 302        if (be32_to_cpu(buf[0].magic) != BOOT_MAGIC ||
 303            be32_to_cpu(buf[1].magic) != FSRO_MAGIC ||
 304            be32_to_cpu(buf[2].magic) != FSRW_MAGIC) {
 305                pr_err("sharpslpart: magic values mismatch\n");
 306                return -EINVAL;
 307        }
 308
 309        /* fixup for hardcoded value 64 MiB (for older models) */
 310        buf[2].end = cpu_to_le32(master->size);
 311
 312        /* extra sanity check */
 313        if (le32_to_cpu(buf[0].end) <= le32_to_cpu(buf[0].start) ||
 314            le32_to_cpu(buf[1].start) < le32_to_cpu(buf[0].end) ||
 315            le32_to_cpu(buf[1].end) <= le32_to_cpu(buf[1].start) ||
 316            le32_to_cpu(buf[2].start) < le32_to_cpu(buf[1].end) ||
 317            le32_to_cpu(buf[2].end) <= le32_to_cpu(buf[2].start)) {
 318                pr_err("sharpslpart: partition sizes mismatch\n");
 319                return -EINVAL;
 320        }
 321
 322        return 0;
 323}
 324
 325static int sharpsl_parse_mtd_partitions(struct mtd_info *master,
 326                                        const struct mtd_partition **pparts,
 327                                        struct mtd_part_parser_data *data)
 328{
 329        struct sharpsl_ftl ftl;
 330        struct sharpsl_nand_partinfo buf[SHARPSL_NAND_PARTS];
 331        struct mtd_partition *sharpsl_nand_parts;
 332        int err;
 333
 334        /* check that OOB bytes 8 to 15 used by the FTL are actually free */
 335        err = sharpsl_nand_check_ooblayout(master);
 336        if (err)
 337                return err;
 338
 339        /* init logical mgmt (FTL) */
 340        err = sharpsl_nand_init_ftl(master, &ftl);
 341        if (err)
 342                return err;
 343
 344        /* read and validate first partition table */
 345        pr_info("sharpslpart: try reading first partition table\n");
 346        err = sharpsl_nand_read_partinfo(master,
 347                                         SHARPSL_PARTINFO1_LADDR,
 348                                         sizeof(buf), buf, &ftl);
 349        if (err) {
 350                /* fallback: read second partition table */
 351                pr_warn("sharpslpart: first partition table is invalid, retry using the second\n");
 352                err = sharpsl_nand_read_partinfo(master,
 353                                                 SHARPSL_PARTINFO2_LADDR,
 354                                                 sizeof(buf), buf, &ftl);
 355        }
 356
 357        /* cleanup logical mgmt (FTL) */
 358        sharpsl_nand_cleanup_ftl(&ftl);
 359
 360        if (err) {
 361                pr_err("sharpslpart: both partition tables are invalid\n");
 362                return err;
 363        }
 364
 365        sharpsl_nand_parts = kcalloc(SHARPSL_NAND_PARTS,
 366                                     sizeof(*sharpsl_nand_parts),
 367                                     GFP_KERNEL);
 368        if (!sharpsl_nand_parts)
 369                return -ENOMEM;
 370
 371        /* original names */
 372        sharpsl_nand_parts[0].name = "smf";
 373        sharpsl_nand_parts[0].offset = le32_to_cpu(buf[0].start);
 374        sharpsl_nand_parts[0].size = le32_to_cpu(buf[0].end) -
 375                                     le32_to_cpu(buf[0].start);
 376
 377        sharpsl_nand_parts[1].name = "root";
 378        sharpsl_nand_parts[1].offset = le32_to_cpu(buf[1].start);
 379        sharpsl_nand_parts[1].size = le32_to_cpu(buf[1].end) -
 380                                     le32_to_cpu(buf[1].start);
 381
 382        sharpsl_nand_parts[2].name = "home";
 383        sharpsl_nand_parts[2].offset = le32_to_cpu(buf[2].start);
 384        sharpsl_nand_parts[2].size = le32_to_cpu(buf[2].end) -
 385                                     le32_to_cpu(buf[2].start);
 386
 387        *pparts = sharpsl_nand_parts;
 388        return SHARPSL_NAND_PARTS;
 389}
 390
 391static struct mtd_part_parser sharpsl_mtd_parser = {
 392        .parse_fn = sharpsl_parse_mtd_partitions,
 393        .name = "sharpslpart",
 394};
 395module_mtd_part_parser(sharpsl_mtd_parser);
 396
 397MODULE_LICENSE("GPL");
 398MODULE_AUTHOR("Andrea Adami <andrea.adami@gmail.com>");
 399MODULE_DESCRIPTION("MTD partitioning for NAND flash on Sharp SL Series");
 400