18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux driver for SSFDC Flash Translation Layer (Read only) 48c2ecf20Sopenharmony_ci * © 2005 Eptar srl 58c2ecf20Sopenharmony_ci * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on NTFL and MTDBLOCK_RO drivers 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/blktrans.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct ssfdcr_record { 208c2ecf20Sopenharmony_ci struct mtd_blktrans_dev mbd; 218c2ecf20Sopenharmony_ci int usecount; 228c2ecf20Sopenharmony_ci unsigned char heads; 238c2ecf20Sopenharmony_ci unsigned char sectors; 248c2ecf20Sopenharmony_ci unsigned short cylinders; 258c2ecf20Sopenharmony_ci int cis_block; /* block n. containing CIS/IDI */ 268c2ecf20Sopenharmony_ci int erase_size; /* phys_block_size */ 278c2ecf20Sopenharmony_ci unsigned short *logic_block_map; /* all zones (max 8192 phys blocks on 288c2ecf20Sopenharmony_ci the 128MiB) */ 298c2ecf20Sopenharmony_ci int map_len; /* n. phys_blocks on the card */ 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define SSFDCR_MAJOR 257 338c2ecf20Sopenharmony_ci#define SSFDCR_PARTN_BITS 3 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define SECTOR_SIZE 512 368c2ecf20Sopenharmony_ci#define SECTOR_SHIFT 9 378c2ecf20Sopenharmony_ci#define OOB_SIZE 16 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MAX_LOGIC_BLK_PER_ZONE 1000 408c2ecf20Sopenharmony_ci#define MAX_PHYS_BLK_PER_ZONE 1024 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define KiB(x) ( (x) * 1024L ) 438c2ecf20Sopenharmony_ci#define MiB(x) ( KiB(x) * 1024L ) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** CHS Table 468c2ecf20Sopenharmony_ci 1MiB 2MiB 4MiB 8MiB 16MiB 32MiB 64MiB 128MiB 478c2ecf20Sopenharmony_ciNCylinder 125 125 250 250 500 500 500 500 488c2ecf20Sopenharmony_ciNHead 4 4 4 4 4 8 8 16 498c2ecf20Sopenharmony_ciNSector 4 8 8 16 16 16 32 32 508c2ecf20Sopenharmony_ciSumSector 2,000 4,000 8,000 16,000 32,000 64,000 128,000 256,000 518c2ecf20Sopenharmony_ciSectorSize 512 512 512 512 512 512 512 512 528c2ecf20Sopenharmony_ci**/ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_citypedef struct { 558c2ecf20Sopenharmony_ci unsigned long size; 568c2ecf20Sopenharmony_ci unsigned short cyl; 578c2ecf20Sopenharmony_ci unsigned char head; 588c2ecf20Sopenharmony_ci unsigned char sec; 598c2ecf20Sopenharmony_ci} chs_entry_t; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Must be ordered by size */ 628c2ecf20Sopenharmony_cistatic const chs_entry_t chs_table[] = { 638c2ecf20Sopenharmony_ci { MiB( 1), 125, 4, 4 }, 648c2ecf20Sopenharmony_ci { MiB( 2), 125, 4, 8 }, 658c2ecf20Sopenharmony_ci { MiB( 4), 250, 4, 8 }, 668c2ecf20Sopenharmony_ci { MiB( 8), 250, 4, 16 }, 678c2ecf20Sopenharmony_ci { MiB( 16), 500, 4, 16 }, 688c2ecf20Sopenharmony_ci { MiB( 32), 500, 8, 16 }, 698c2ecf20Sopenharmony_ci { MiB( 64), 500, 8, 32 }, 708c2ecf20Sopenharmony_ci { MiB(128), 500, 16, 32 }, 718c2ecf20Sopenharmony_ci { 0 }, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int get_chs(unsigned long size, unsigned short *cyl, unsigned char *head, 758c2ecf20Sopenharmony_ci unsigned char *sec) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int k; 788c2ecf20Sopenharmony_ci int found = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci k = 0; 818c2ecf20Sopenharmony_ci while (chs_table[k].size > 0 && size > chs_table[k].size) 828c2ecf20Sopenharmony_ci k++; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (chs_table[k].size > 0) { 858c2ecf20Sopenharmony_ci if (cyl) 868c2ecf20Sopenharmony_ci *cyl = chs_table[k].cyl; 878c2ecf20Sopenharmony_ci if (head) 888c2ecf20Sopenharmony_ci *head = chs_table[k].head; 898c2ecf20Sopenharmony_ci if (sec) 908c2ecf20Sopenharmony_ci *sec = chs_table[k].sec; 918c2ecf20Sopenharmony_ci found = 1; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return found; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* These bytes are the signature for the CIS/IDI sector */ 988c2ecf20Sopenharmony_cistatic const uint8_t cis_numbers[] = { 998c2ecf20Sopenharmony_ci 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Read and check for a valid CIS sector */ 1038c2ecf20Sopenharmony_cistatic int get_valid_cis_sector(struct mtd_info *mtd) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int ret, k, cis_sector; 1068c2ecf20Sopenharmony_ci size_t retlen; 1078c2ecf20Sopenharmony_ci loff_t offset; 1088c2ecf20Sopenharmony_ci uint8_t *sect_buf; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci cis_sector = -1; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci sect_buf = kmalloc(SECTOR_SIZE, GFP_KERNEL); 1138c2ecf20Sopenharmony_ci if (!sect_buf) 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * Look for CIS/IDI sector on the first GOOD block (give up after 4 bad 1188c2ecf20Sopenharmony_ci * blocks). If the first good block doesn't contain CIS number the flash 1198c2ecf20Sopenharmony_ci * is not SSFDC formatted 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize) { 1228c2ecf20Sopenharmony_ci if (mtd_block_isbad(mtd, offset)) { 1238c2ecf20Sopenharmony_ci ret = mtd_read(mtd, offset, SECTOR_SIZE, &retlen, 1248c2ecf20Sopenharmony_ci sect_buf); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* CIS pattern match on the sector buffer */ 1278c2ecf20Sopenharmony_ci if (ret < 0 || retlen != SECTOR_SIZE) { 1288c2ecf20Sopenharmony_ci printk(KERN_WARNING 1298c2ecf20Sopenharmony_ci "SSFDC_RO:can't read CIS/IDI sector\n"); 1308c2ecf20Sopenharmony_ci } else if (!memcmp(sect_buf, cis_numbers, 1318c2ecf20Sopenharmony_ci sizeof(cis_numbers))) { 1328c2ecf20Sopenharmony_ci /* Found */ 1338c2ecf20Sopenharmony_ci cis_sector = (int)(offset >> SECTOR_SHIFT); 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: CIS/IDI sector not found" 1368c2ecf20Sopenharmony_ci " on %s (mtd%d)\n", mtd->name, 1378c2ecf20Sopenharmony_ci mtd->index); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci kfree(sect_buf); 1448c2ecf20Sopenharmony_ci out: 1458c2ecf20Sopenharmony_ci return cis_sector; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* Read physical sector (wrapper to MTD_READ) */ 1498c2ecf20Sopenharmony_cistatic int read_physical_sector(struct mtd_info *mtd, uint8_t *sect_buf, 1508c2ecf20Sopenharmony_ci int sect_no) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int ret; 1538c2ecf20Sopenharmony_ci size_t retlen; 1548c2ecf20Sopenharmony_ci loff_t offset = (loff_t)sect_no << SECTOR_SHIFT; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = mtd_read(mtd, offset, SECTOR_SIZE, &retlen, sect_buf); 1578c2ecf20Sopenharmony_ci if (ret < 0 || retlen != SECTOR_SIZE) 1588c2ecf20Sopenharmony_ci return -1; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* Read redundancy area (wrapper to MTD_READ_OOB */ 1648c2ecf20Sopenharmony_cistatic int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct mtd_oob_ops ops; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ops.mode = MTD_OPS_RAW; 1708c2ecf20Sopenharmony_ci ops.ooboffs = 0; 1718c2ecf20Sopenharmony_ci ops.ooblen = OOB_SIZE; 1728c2ecf20Sopenharmony_ci ops.oobbuf = buf; 1738c2ecf20Sopenharmony_ci ops.datbuf = NULL; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = mtd_read_oob(mtd, offs, &ops); 1768c2ecf20Sopenharmony_ci if (ret < 0 || ops.oobretlen != OOB_SIZE) 1778c2ecf20Sopenharmony_ci return -1; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* Parity calculator on a word of n bit size */ 1838c2ecf20Sopenharmony_cistatic int get_parity(int number, int size) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int k; 1868c2ecf20Sopenharmony_ci int parity; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci parity = 1; 1898c2ecf20Sopenharmony_ci for (k = 0; k < size; k++) { 1908c2ecf20Sopenharmony_ci parity += (number >> k); 1918c2ecf20Sopenharmony_ci parity &= 1; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci return parity; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* Read and validate the logical block address field stored in the OOB */ 1978c2ecf20Sopenharmony_cistatic int get_logical_address(uint8_t *oob_buf) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int block_address, parity; 2008c2ecf20Sopenharmony_ci int offset[2] = {6, 11}; /* offset of the 2 address fields within OOB */ 2018c2ecf20Sopenharmony_ci int j; 2028c2ecf20Sopenharmony_ci int ok = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Look for the first valid logical address 2068c2ecf20Sopenharmony_ci * Valid address has fixed pattern on most significant bits and 2078c2ecf20Sopenharmony_ci * parity check 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(offset); j++) { 2108c2ecf20Sopenharmony_ci block_address = ((int)oob_buf[offset[j]] << 8) | 2118c2ecf20Sopenharmony_ci oob_buf[offset[j]+1]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Check for the signature bits in the address field (MSBits) */ 2148c2ecf20Sopenharmony_ci if ((block_address & ~0x7FF) == 0x1000) { 2158c2ecf20Sopenharmony_ci parity = block_address & 0x01; 2168c2ecf20Sopenharmony_ci block_address &= 0x7FF; 2178c2ecf20Sopenharmony_ci block_address >>= 1; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (get_parity(block_address, 10) != parity) { 2208c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: logical address field%d" 2218c2ecf20Sopenharmony_ci "parity error(0x%04X)\n", j+1, 2228c2ecf20Sopenharmony_ci block_address); 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci ok = 1; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (!ok) 2318c2ecf20Sopenharmony_ci block_address = -2; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: get_logical_address() %d\n", 2348c2ecf20Sopenharmony_ci block_address); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return block_address; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* Build the logic block map */ 2408c2ecf20Sopenharmony_cistatic int build_logical_block_map(struct ssfdcr_record *ssfdc) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci unsigned long offset; 2438c2ecf20Sopenharmony_ci uint8_t oob_buf[OOB_SIZE]; 2448c2ecf20Sopenharmony_ci int ret, block_address, phys_block; 2458c2ecf20Sopenharmony_ci struct mtd_info *mtd = ssfdc->mbd.mtd; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: build_block_map() nblks=%d (%luK)\n", 2488c2ecf20Sopenharmony_ci ssfdc->map_len, 2498c2ecf20Sopenharmony_ci (unsigned long)ssfdc->map_len * ssfdc->erase_size / 1024); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Scan every physical block, skip CIS block */ 2528c2ecf20Sopenharmony_ci for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len; 2538c2ecf20Sopenharmony_ci phys_block++) { 2548c2ecf20Sopenharmony_ci offset = (unsigned long)phys_block * ssfdc->erase_size; 2558c2ecf20Sopenharmony_ci if (mtd_block_isbad(mtd, offset)) 2568c2ecf20Sopenharmony_ci continue; /* skip bad blocks */ 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ret = read_raw_oob(mtd, offset, oob_buf); 2598c2ecf20Sopenharmony_ci if (ret < 0) { 2608c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: mtd read_oob() failed at %lu\n", 2618c2ecf20Sopenharmony_ci offset); 2628c2ecf20Sopenharmony_ci return -1; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci block_address = get_logical_address(oob_buf); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Skip invalid addresses */ 2678c2ecf20Sopenharmony_ci if (block_address >= 0 && 2688c2ecf20Sopenharmony_ci block_address < MAX_LOGIC_BLK_PER_ZONE) { 2698c2ecf20Sopenharmony_ci int zone_index; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE; 2728c2ecf20Sopenharmony_ci block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE; 2738c2ecf20Sopenharmony_ci ssfdc->logic_block_map[block_address] = 2748c2ecf20Sopenharmony_ci (unsigned short)phys_block; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: build_block_map() phys_block=%d," 2778c2ecf20Sopenharmony_ci "logic_block_addr=%d, zone=%d\n", 2788c2ecf20Sopenharmony_ci phys_block, block_address, zone_index); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct ssfdcr_record *ssfdc; 2878c2ecf20Sopenharmony_ci int cis_sector; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Check for small page NAND flash */ 2908c2ecf20Sopenharmony_ci if (!mtd_type_is_nand(mtd) || mtd->oobsize != OOB_SIZE || 2918c2ecf20Sopenharmony_ci mtd->size > UINT_MAX) 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Check for SSDFC format by reading CIS/IDI sector */ 2958c2ecf20Sopenharmony_ci cis_sector = get_valid_cis_sector(mtd); 2968c2ecf20Sopenharmony_ci if (cis_sector == -1) 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL); 3008c2ecf20Sopenharmony_ci if (!ssfdc) 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ssfdc->mbd.mtd = mtd; 3048c2ecf20Sopenharmony_ci ssfdc->mbd.devnum = -1; 3058c2ecf20Sopenharmony_ci ssfdc->mbd.tr = tr; 3068c2ecf20Sopenharmony_ci ssfdc->mbd.readonly = 1; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT); 3098c2ecf20Sopenharmony_ci ssfdc->erase_size = mtd->erasesize; 3108c2ecf20Sopenharmony_ci ssfdc->map_len = (u32)mtd->size / mtd->erasesize; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n", 3138c2ecf20Sopenharmony_ci ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len, 3148c2ecf20Sopenharmony_ci DIV_ROUND_UP(ssfdc->map_len, MAX_PHYS_BLK_PER_ZONE)); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Set geometry */ 3178c2ecf20Sopenharmony_ci ssfdc->heads = 16; 3188c2ecf20Sopenharmony_ci ssfdc->sectors = 32; 3198c2ecf20Sopenharmony_ci get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors); 3208c2ecf20Sopenharmony_ci ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) / 3218c2ecf20Sopenharmony_ci ((long)ssfdc->sectors * (long)ssfdc->heads)); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n", 3248c2ecf20Sopenharmony_ci ssfdc->cylinders, ssfdc->heads , ssfdc->sectors, 3258c2ecf20Sopenharmony_ci (long)ssfdc->cylinders * (long)ssfdc->heads * 3268c2ecf20Sopenharmony_ci (long)ssfdc->sectors); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders * 3298c2ecf20Sopenharmony_ci (long)ssfdc->sectors; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Allocate logical block map */ 3328c2ecf20Sopenharmony_ci ssfdc->logic_block_map = 3338c2ecf20Sopenharmony_ci kmalloc_array(ssfdc->map_len, 3348c2ecf20Sopenharmony_ci sizeof(ssfdc->logic_block_map[0]), GFP_KERNEL); 3358c2ecf20Sopenharmony_ci if (!ssfdc->logic_block_map) 3368c2ecf20Sopenharmony_ci goto out_err; 3378c2ecf20Sopenharmony_ci memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) * 3388c2ecf20Sopenharmony_ci ssfdc->map_len); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Build logical block map */ 3418c2ecf20Sopenharmony_ci if (build_logical_block_map(ssfdc) < 0) 3428c2ecf20Sopenharmony_ci goto out_err; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Register device + partitions */ 3458c2ecf20Sopenharmony_ci if (add_mtd_blktrans_dev(&ssfdc->mbd)) 3468c2ecf20Sopenharmony_ci goto out_err; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n", 3498c2ecf20Sopenharmony_ci ssfdc->mbd.devnum + 'a', mtd->index, mtd->name); 3508c2ecf20Sopenharmony_ci return; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ciout_err: 3538c2ecf20Sopenharmony_ci kfree(ssfdc->logic_block_map); 3548c2ecf20Sopenharmony_ci kfree(ssfdc); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: remove_dev (i=%d)\n", dev->devnum); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci del_mtd_blktrans_dev(dev); 3648c2ecf20Sopenharmony_ci kfree(ssfdc->logic_block_map); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int ssfdcr_readsect(struct mtd_blktrans_dev *dev, 3688c2ecf20Sopenharmony_ci unsigned long logic_sect_no, char *buf) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev; 3718c2ecf20Sopenharmony_ci int sectors_per_block, offset, block_address; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT; 3748c2ecf20Sopenharmony_ci offset = (int)(logic_sect_no % sectors_per_block); 3758c2ecf20Sopenharmony_ci block_address = (int)(logic_sect_no / sectors_per_block); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d," 3788c2ecf20Sopenharmony_ci " block_addr=%d\n", logic_sect_no, sectors_per_block, offset, 3798c2ecf20Sopenharmony_ci block_address); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci BUG_ON(block_address >= ssfdc->map_len); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci block_address = ssfdc->logic_block_map[block_address]; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n", 3868c2ecf20Sopenharmony_ci block_address); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (block_address < 0xffff) { 3898c2ecf20Sopenharmony_ci unsigned long sect_no; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci sect_no = (unsigned long)block_address * sectors_per_block + 3928c2ecf20Sopenharmony_ci offset; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n", 3958c2ecf20Sopenharmony_ci sect_no); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (read_physical_sector(ssfdc->mbd.mtd, buf, sect_no) < 0) 3988c2ecf20Sopenharmony_ci return -EIO; 3998c2ecf20Sopenharmony_ci } else { 4008c2ecf20Sopenharmony_ci memset(buf, 0xff, SECTOR_SIZE); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci pr_debug("SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n", 4118c2ecf20Sopenharmony_ci ssfdc->cylinders, ssfdc->heads, ssfdc->sectors); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci geo->heads = ssfdc->heads; 4148c2ecf20Sopenharmony_ci geo->sectors = ssfdc->sectors; 4158c2ecf20Sopenharmony_ci geo->cylinders = ssfdc->cylinders; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/**************************************************************************** 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Module stuff 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci ****************************************************************************/ 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic struct mtd_blktrans_ops ssfdcr_tr = { 4278c2ecf20Sopenharmony_ci .name = "ssfdc", 4288c2ecf20Sopenharmony_ci .major = SSFDCR_MAJOR, 4298c2ecf20Sopenharmony_ci .part_bits = SSFDCR_PARTN_BITS, 4308c2ecf20Sopenharmony_ci .blksize = SECTOR_SIZE, 4318c2ecf20Sopenharmony_ci .getgeo = ssfdcr_getgeo, 4328c2ecf20Sopenharmony_ci .readsect = ssfdcr_readsect, 4338c2ecf20Sopenharmony_ci .add_mtd = ssfdcr_add_mtd, 4348c2ecf20Sopenharmony_ci .remove_dev = ssfdcr_remove_dev, 4358c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int __init init_ssfdcr(void) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci printk(KERN_INFO "SSFDC read-only Flash Translation layer\n"); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return register_mtd_blktrans(&ssfdcr_tr); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void __exit cleanup_ssfdcr(void) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci deregister_mtd_blktrans(&ssfdcr_tr); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cimodule_init(init_ssfdcr); 4518c2ecf20Sopenharmony_cimodule_exit(cleanup_ssfdcr); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>"); 4558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Flash Translation Layer for read-only SSFDC SmartMedia card"); 456