Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

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