root/drivers/mtd/ssfdc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_chs
  2. get_valid_cis_sector
  3. read_physical_sector
  4. read_raw_oob
  5. get_parity
  6. get_logical_address
  7. build_logical_block_map
  8. ssfdcr_add_mtd
  9. ssfdcr_remove_dev
  10. ssfdcr_readsect
  11. ssfdcr_getgeo
  12. init_ssfdcr
  13. cleanup_ssfdcr

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Linux driver for SSFDC Flash Translation Layer (Read only)
   4  * © 2005 Eptar srl
   5  * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
   6  *
   7  * Based on NTFL and MTDBLOCK_RO drivers
   8  */
   9 
  10 #include <linux/kernel.h>
  11 #include <linux/module.h>
  12 #include <linux/init.h>
  13 #include <linux/slab.h>
  14 #include <linux/hdreg.h>
  15 #include <linux/mtd/mtd.h>
  16 #include <linux/mtd/rawnand.h>
  17 #include <linux/mtd/blktrans.h>
  18 
  19 struct ssfdcr_record {
  20         struct mtd_blktrans_dev mbd;
  21         int usecount;
  22         unsigned char heads;
  23         unsigned char sectors;
  24         unsigned short cylinders;
  25         int cis_block;                  /* block n. containing CIS/IDI */
  26         int erase_size;                 /* phys_block_size */
  27         unsigned short *logic_block_map; /* all zones (max 8192 phys blocks on
  28                                             the 128MiB) */
  29         int map_len;                    /* n. phys_blocks on the card */
  30 };
  31 
  32 #define SSFDCR_MAJOR            257
  33 #define SSFDCR_PARTN_BITS       3
  34 
  35 #define SECTOR_SIZE             512
  36 #define SECTOR_SHIFT            9
  37 #define OOB_SIZE                16
  38 
  39 #define MAX_LOGIC_BLK_PER_ZONE  1000
  40 #define MAX_PHYS_BLK_PER_ZONE   1024
  41 
  42 #define KiB(x)  ( (x) * 1024L )
  43 #define MiB(x)  ( KiB(x) * 1024L )
  44 
  45 /** CHS Table
  46                 1MiB    2MiB    4MiB    8MiB    16MiB   32MiB   64MiB   128MiB
  47 NCylinder       125     125     250     250     500     500     500     500
  48 NHead           4       4       4       4       4       8       8       16
  49 NSector         4       8       8       16      16      16      32      32
  50 SumSector       2,000   4,000   8,000   16,000  32,000  64,000  128,000 256,000
  51 SectorSize      512     512     512     512     512     512     512     512
  52 **/
  53 
  54 typedef struct {
  55         unsigned long size;
  56         unsigned short cyl;
  57         unsigned char head;
  58         unsigned char sec;
  59 } chs_entry_t;
  60 
  61 /* Must be ordered by size */
  62 static const chs_entry_t chs_table[] = {
  63         { MiB(  1), 125,  4,  4 },
  64         { MiB(  2), 125,  4,  8 },
  65         { MiB(  4), 250,  4,  8 },
  66         { MiB(  8), 250,  4, 16 },
  67         { MiB( 16), 500,  4, 16 },
  68         { MiB( 32), 500,  8, 16 },
  69         { MiB( 64), 500,  8, 32 },
  70         { MiB(128), 500, 16, 32 },
  71         { 0 },
  72 };
  73 
  74 static int get_chs(unsigned long size, unsigned short *cyl, unsigned char *head,
  75                         unsigned char *sec)
  76 {
  77         int k;
  78         int found = 0;
  79 
  80         k = 0;
  81         while (chs_table[k].size > 0 && size > chs_table[k].size)
  82                 k++;
  83 
  84         if (chs_table[k].size > 0) {
  85                 if (cyl)
  86                         *cyl = chs_table[k].cyl;
  87                 if (head)
  88                         *head = chs_table[k].head;
  89                 if (sec)
  90                         *sec = chs_table[k].sec;
  91                 found = 1;
  92         }
  93 
  94         return found;
  95 }
  96 
  97 /* These bytes are the signature for the CIS/IDI sector */
  98 static const uint8_t cis_numbers[] = {
  99         0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
 100 };
 101 
 102 /* Read and check for a valid CIS sector */
 103 static int get_valid_cis_sector(struct mtd_info *mtd)
 104 {
 105         int ret, k, cis_sector;
 106         size_t retlen;
 107         loff_t offset;
 108         uint8_t *sect_buf;
 109 
 110         cis_sector = -1;
 111 
 112         sect_buf = kmalloc(SECTOR_SIZE, GFP_KERNEL);
 113         if (!sect_buf)
 114                 goto out;
 115 
 116         /*
 117          * Look for CIS/IDI sector on the first GOOD block (give up after 4 bad
 118          * blocks). If the first good block doesn't contain CIS number the flash
 119          * is not SSFDC formatted
 120          */
 121         for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize) {
 122                 if (mtd_block_isbad(mtd, offset)) {
 123                         ret = mtd_read(mtd, offset, SECTOR_SIZE, &retlen,
 124                                        sect_buf);
 125 
 126                         /* CIS pattern match on the sector buffer */
 127                         if (ret < 0 || retlen != SECTOR_SIZE) {
 128                                 printk(KERN_WARNING
 129                                         "SSFDC_RO:can't read CIS/IDI sector\n");
 130                         } else if (!memcmp(sect_buf, cis_numbers,
 131                                         sizeof(cis_numbers))) {
 132                                 /* Found */
 133                                 cis_sector = (int)(offset >> SECTOR_SHIFT);
 134                         } else {
 135                                 pr_debug("SSFDC_RO: CIS/IDI sector not found"
 136                                         " on %s (mtd%d)\n", mtd->name,
 137                                         mtd->index);
 138                         }
 139                         break;
 140                 }
 141         }
 142 
 143         kfree(sect_buf);
 144  out:
 145         return cis_sector;
 146 }
 147 
 148 /* Read physical sector (wrapper to MTD_READ) */
 149 static int read_physical_sector(struct mtd_info *mtd, uint8_t *sect_buf,
 150                                 int sect_no)
 151 {
 152         int ret;
 153         size_t retlen;
 154         loff_t offset = (loff_t)sect_no << SECTOR_SHIFT;
 155 
 156         ret = mtd_read(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
 157         if (ret < 0 || retlen != SECTOR_SIZE)
 158                 return -1;
 159 
 160         return 0;
 161 }
 162 
 163 /* Read redundancy area (wrapper to MTD_READ_OOB */
 164 static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf)
 165 {
 166         struct mtd_oob_ops ops;
 167         int ret;
 168 
 169         ops.mode = MTD_OPS_RAW;
 170         ops.ooboffs = 0;
 171         ops.ooblen = OOB_SIZE;
 172         ops.oobbuf = buf;
 173         ops.datbuf = NULL;
 174 
 175         ret = mtd_read_oob(mtd, offs, &ops);
 176         if (ret < 0 || ops.oobretlen != OOB_SIZE)
 177                 return -1;
 178 
 179         return 0;
 180 }
 181 
 182 /* Parity calculator on a word of n bit size */
 183 static int get_parity(int number, int size)
 184 {
 185         int k;
 186         int parity;
 187 
 188         parity = 1;
 189         for (k = 0; k < size; k++) {
 190                 parity += (number >> k);
 191                 parity &= 1;
 192         }
 193         return parity;
 194 }
 195 
 196 /* Read and validate the logical block address field stored in the OOB */
 197 static int get_logical_address(uint8_t *oob_buf)
 198 {
 199         int block_address, parity;
 200         int offset[2] = {6, 11}; /* offset of the 2 address fields within OOB */
 201         int j;
 202         int ok = 0;
 203 
 204         /*
 205          * Look for the first valid logical address
 206          * Valid address has fixed pattern on most significant bits and
 207          * parity check
 208          */
 209         for (j = 0; j < ARRAY_SIZE(offset); j++) {
 210                 block_address = ((int)oob_buf[offset[j]] << 8) |
 211                         oob_buf[offset[j]+1];
 212 
 213                 /* Check for the signature bits in the address field (MSBits) */
 214                 if ((block_address & ~0x7FF) == 0x1000) {
 215                         parity = block_address & 0x01;
 216                         block_address &= 0x7FF;
 217                         block_address >>= 1;
 218 
 219                         if (get_parity(block_address, 10) != parity) {
 220                                 pr_debug("SSFDC_RO: logical address field%d"
 221                                         "parity error(0x%04X)\n", j+1,
 222                                         block_address);
 223                         } else {
 224                                 ok = 1;
 225                                 break;
 226                         }
 227                 }
 228         }
 229 
 230         if (!ok)
 231                 block_address = -2;
 232 
 233         pr_debug("SSFDC_RO: get_logical_address() %d\n",
 234                 block_address);
 235 
 236         return block_address;
 237 }
 238 
 239 /* Build the logic block map */
 240 static int build_logical_block_map(struct ssfdcr_record *ssfdc)
 241 {
 242         unsigned long offset;
 243         uint8_t oob_buf[OOB_SIZE];
 244         int ret, block_address, phys_block;
 245         struct mtd_info *mtd = ssfdc->mbd.mtd;
 246 
 247         pr_debug("SSFDC_RO: build_block_map() nblks=%d (%luK)\n",
 248               ssfdc->map_len,
 249               (unsigned long)ssfdc->map_len * ssfdc->erase_size / 1024);
 250 
 251         /* Scan every physical block, skip CIS block */
 252         for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len;
 253                         phys_block++) {
 254                 offset = (unsigned long)phys_block * ssfdc->erase_size;
 255                 if (mtd_block_isbad(mtd, offset))
 256                         continue;       /* skip bad blocks */
 257 
 258                 ret = read_raw_oob(mtd, offset, oob_buf);
 259                 if (ret < 0) {
 260                         pr_debug("SSFDC_RO: mtd read_oob() failed at %lu\n",
 261                                 offset);
 262                         return -1;
 263                 }
 264                 block_address = get_logical_address(oob_buf);
 265 
 266                 /* Skip invalid addresses */
 267                 if (block_address >= 0 &&
 268                                 block_address < MAX_LOGIC_BLK_PER_ZONE) {
 269                         int zone_index;
 270 
 271                         zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE;
 272                         block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE;
 273                         ssfdc->logic_block_map[block_address] =
 274                                 (unsigned short)phys_block;
 275 
 276                         pr_debug("SSFDC_RO: build_block_map() phys_block=%d,"
 277                                 "logic_block_addr=%d, zone=%d\n",
 278                                 phys_block, block_address, zone_index);
 279                 }
 280         }
 281         return 0;
 282 }
 283 
 284 static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 285 {
 286         struct ssfdcr_record *ssfdc;
 287         int cis_sector;
 288 
 289         /* Check for small page NAND flash */
 290         if (!mtd_type_is_nand(mtd) || mtd->oobsize != OOB_SIZE ||
 291             mtd->size > UINT_MAX)
 292                 return;
 293 
 294         /* Check for SSDFC format by reading CIS/IDI sector */
 295         cis_sector = get_valid_cis_sector(mtd);
 296         if (cis_sector == -1)
 297                 return;
 298 
 299         ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
 300         if (!ssfdc)
 301                 return;
 302 
 303         ssfdc->mbd.mtd = mtd;
 304         ssfdc->mbd.devnum = -1;
 305         ssfdc->mbd.tr = tr;
 306         ssfdc->mbd.readonly = 1;
 307 
 308         ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
 309         ssfdc->erase_size = mtd->erasesize;
 310         ssfdc->map_len = (u32)mtd->size / mtd->erasesize;
 311 
 312         pr_debug("SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n",
 313                 ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
 314                 DIV_ROUND_UP(ssfdc->map_len, MAX_PHYS_BLK_PER_ZONE));
 315 
 316         /* Set geometry */
 317         ssfdc->heads = 16;
 318         ssfdc->sectors = 32;
 319         get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
 320         ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) /
 321                         ((long)ssfdc->sectors * (long)ssfdc->heads));
 322 
 323         pr_debug("SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
 324                 ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
 325                 (long)ssfdc->cylinders * (long)ssfdc->heads *
 326                 (long)ssfdc->sectors);
 327 
 328         ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders *
 329                                 (long)ssfdc->sectors;
 330 
 331         /* Allocate logical block map */
 332         ssfdc->logic_block_map =
 333                 kmalloc_array(ssfdc->map_len,
 334                               sizeof(ssfdc->logic_block_map[0]), GFP_KERNEL);
 335         if (!ssfdc->logic_block_map)
 336                 goto out_err;
 337         memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) *
 338                 ssfdc->map_len);
 339 
 340         /* Build logical block map */
 341         if (build_logical_block_map(ssfdc) < 0)
 342                 goto out_err;
 343 
 344         /* Register device + partitions */
 345         if (add_mtd_blktrans_dev(&ssfdc->mbd))
 346                 goto out_err;
 347 
 348         printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n",
 349                 ssfdc->mbd.devnum + 'a', mtd->index, mtd->name);
 350         return;
 351 
 352 out_err:
 353         kfree(ssfdc->logic_block_map);
 354         kfree(ssfdc);
 355 }
 356 
 357 static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
 358 {
 359         struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
 360 
 361         pr_debug("SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
 362 
 363         del_mtd_blktrans_dev(dev);
 364         kfree(ssfdc->logic_block_map);
 365 }
 366 
 367 static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
 368                                 unsigned long logic_sect_no, char *buf)
 369 {
 370         struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
 371         int sectors_per_block, offset, block_address;
 372 
 373         sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT;
 374         offset = (int)(logic_sect_no % sectors_per_block);
 375         block_address = (int)(logic_sect_no / sectors_per_block);
 376 
 377         pr_debug("SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d,"
 378                 " block_addr=%d\n", logic_sect_no, sectors_per_block, offset,
 379                 block_address);
 380 
 381         BUG_ON(block_address >= ssfdc->map_len);
 382 
 383         block_address = ssfdc->logic_block_map[block_address];
 384 
 385         pr_debug("SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n",
 386                 block_address);
 387 
 388         if (block_address < 0xffff) {
 389                 unsigned long sect_no;
 390 
 391                 sect_no = (unsigned long)block_address * sectors_per_block +
 392                                 offset;
 393 
 394                 pr_debug("SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n",
 395                         sect_no);
 396 
 397                 if (read_physical_sector(ssfdc->mbd.mtd, buf, sect_no) < 0)
 398                         return -EIO;
 399         } else {
 400                 memset(buf, 0xff, SECTOR_SIZE);
 401         }
 402 
 403         return 0;
 404 }
 405 
 406 static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
 407 {
 408         struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
 409 
 410         pr_debug("SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
 411                         ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
 412 
 413         geo->heads = ssfdc->heads;
 414         geo->sectors = ssfdc->sectors;
 415         geo->cylinders = ssfdc->cylinders;
 416 
 417         return 0;
 418 }
 419 
 420 /****************************************************************************
 421  *
 422  * Module stuff
 423  *
 424  ****************************************************************************/
 425 
 426 static struct mtd_blktrans_ops ssfdcr_tr = {
 427         .name           = "ssfdc",
 428         .major          = SSFDCR_MAJOR,
 429         .part_bits      = SSFDCR_PARTN_BITS,
 430         .blksize        = SECTOR_SIZE,
 431         .getgeo         = ssfdcr_getgeo,
 432         .readsect       = ssfdcr_readsect,
 433         .add_mtd        = ssfdcr_add_mtd,
 434         .remove_dev     = ssfdcr_remove_dev,
 435         .owner          = THIS_MODULE,
 436 };
 437 
 438 static int __init init_ssfdcr(void)
 439 {
 440         printk(KERN_INFO "SSFDC read-only Flash Translation layer\n");
 441 
 442         return register_mtd_blktrans(&ssfdcr_tr);
 443 }
 444 
 445 static void __exit cleanup_ssfdcr(void)
 446 {
 447         deregister_mtd_blktrans(&ssfdcr_tr);
 448 }
 449 
 450 module_init(init_ssfdcr);
 451 module_exit(cleanup_ssfdcr);
 452 
 453 MODULE_LICENSE("GPL");
 454 MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
 455 MODULE_DESCRIPTION("Flash Translation Layer for read-only SSFDC SmartMedia card");

/* [<][>][^][v][top][bottom][index][help] */