xref: /OK3568_Linux_fs/kernel/drivers/mtd/nand/raw/sm_common.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright © 2009 - Maxim Levitsky
4*4882a593Smuzhiyun  * Common routines & support for xD format
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun #include <linux/kernel.h>
7*4882a593Smuzhiyun #include <linux/mtd/rawnand.h>
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/sizes.h>
10*4882a593Smuzhiyun #include "sm_common.h"
11*4882a593Smuzhiyun 
oob_sm_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)12*4882a593Smuzhiyun static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section,
13*4882a593Smuzhiyun 				struct mtd_oob_region *oobregion)
14*4882a593Smuzhiyun {
15*4882a593Smuzhiyun 	if (section > 1)
16*4882a593Smuzhiyun 		return -ERANGE;
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun 	oobregion->length = 3;
19*4882a593Smuzhiyun 	oobregion->offset = ((section + 1) * 8) - 3;
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 	return 0;
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun 
oob_sm_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)24*4882a593Smuzhiyun static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section,
25*4882a593Smuzhiyun 				 struct mtd_oob_region *oobregion)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun 	switch (section) {
28*4882a593Smuzhiyun 	case 0:
29*4882a593Smuzhiyun 		/* reserved */
30*4882a593Smuzhiyun 		oobregion->offset = 0;
31*4882a593Smuzhiyun 		oobregion->length = 4;
32*4882a593Smuzhiyun 		break;
33*4882a593Smuzhiyun 	case 1:
34*4882a593Smuzhiyun 		/* LBA1 */
35*4882a593Smuzhiyun 		oobregion->offset = 6;
36*4882a593Smuzhiyun 		oobregion->length = 2;
37*4882a593Smuzhiyun 		break;
38*4882a593Smuzhiyun 	case 2:
39*4882a593Smuzhiyun 		/* LBA2 */
40*4882a593Smuzhiyun 		oobregion->offset = 11;
41*4882a593Smuzhiyun 		oobregion->length = 2;
42*4882a593Smuzhiyun 		break;
43*4882a593Smuzhiyun 	default:
44*4882a593Smuzhiyun 		return -ERANGE;
45*4882a593Smuzhiyun 	}
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	return 0;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static const struct mtd_ooblayout_ops oob_sm_ops = {
51*4882a593Smuzhiyun 	.ecc = oob_sm_ooblayout_ecc,
52*4882a593Smuzhiyun 	.free = oob_sm_ooblayout_free,
53*4882a593Smuzhiyun };
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun /* NOTE: This layout is is not compatabable with SmartMedia, */
56*4882a593Smuzhiyun /* because the 256 byte devices have page depenent oob layout */
57*4882a593Smuzhiyun /* However it does preserve the bad block markers */
58*4882a593Smuzhiyun /* If you use smftl, it will bypass this and work correctly */
59*4882a593Smuzhiyun /* If you not, then you break SmartMedia compliance anyway */
60*4882a593Smuzhiyun 
oob_sm_small_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)61*4882a593Smuzhiyun static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section,
62*4882a593Smuzhiyun 				      struct mtd_oob_region *oobregion)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	if (section)
65*4882a593Smuzhiyun 		return -ERANGE;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	oobregion->length = 3;
68*4882a593Smuzhiyun 	oobregion->offset = 0;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	return 0;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
oob_sm_small_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)73*4882a593Smuzhiyun static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section,
74*4882a593Smuzhiyun 				       struct mtd_oob_region *oobregion)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	switch (section) {
77*4882a593Smuzhiyun 	case 0:
78*4882a593Smuzhiyun 		/* reserved */
79*4882a593Smuzhiyun 		oobregion->offset = 3;
80*4882a593Smuzhiyun 		oobregion->length = 2;
81*4882a593Smuzhiyun 		break;
82*4882a593Smuzhiyun 	case 1:
83*4882a593Smuzhiyun 		/* LBA1 */
84*4882a593Smuzhiyun 		oobregion->offset = 6;
85*4882a593Smuzhiyun 		oobregion->length = 2;
86*4882a593Smuzhiyun 		break;
87*4882a593Smuzhiyun 	default:
88*4882a593Smuzhiyun 		return -ERANGE;
89*4882a593Smuzhiyun 	}
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	return 0;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun static const struct mtd_ooblayout_ops oob_sm_small_ops = {
95*4882a593Smuzhiyun 	.ecc = oob_sm_small_ooblayout_ecc,
96*4882a593Smuzhiyun 	.free = oob_sm_small_ooblayout_free,
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun 
sm_block_markbad(struct nand_chip * chip,loff_t ofs)99*4882a593Smuzhiyun static int sm_block_markbad(struct nand_chip *chip, loff_t ofs)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct mtd_info *mtd = nand_to_mtd(chip);
102*4882a593Smuzhiyun 	struct mtd_oob_ops ops;
103*4882a593Smuzhiyun 	struct sm_oob oob;
104*4882a593Smuzhiyun 	int ret;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	memset(&oob, -1, SM_OOB_SIZE);
107*4882a593Smuzhiyun 	oob.block_status = 0x0F;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	/* As long as this function is called on erase block boundaries
110*4882a593Smuzhiyun 		it will work correctly for 256 byte nand */
111*4882a593Smuzhiyun 	ops.mode = MTD_OPS_PLACE_OOB;
112*4882a593Smuzhiyun 	ops.ooboffs = 0;
113*4882a593Smuzhiyun 	ops.ooblen = mtd->oobsize;
114*4882a593Smuzhiyun 	ops.oobbuf = (void *)&oob;
115*4882a593Smuzhiyun 	ops.datbuf = NULL;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	ret = mtd_write_oob(mtd, ofs, &ops);
119*4882a593Smuzhiyun 	if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
120*4882a593Smuzhiyun 		pr_notice("sm_common: can't mark sector at %i as bad\n",
121*4882a593Smuzhiyun 			  (int)ofs);
122*4882a593Smuzhiyun 		return -EIO;
123*4882a593Smuzhiyun 	}
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	return 0;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
129*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM",   0x5d, 2,   SZ_8K, NAND_ROM),
130*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 4MiB 3,3V",       0xe3, 4,   SZ_8K, 0),
131*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V",     0xe5, 4,   SZ_8K, 0),
132*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 4MiB 5V",         0x6b, 4,   SZ_8K, 0),
133*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM",   0xd5, 4,   SZ_8K, NAND_ROM),
134*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 8MiB 3,3V",       0xe6, 8,   SZ_8K, 0),
135*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM",   0xd6, 8,   SZ_8K, NAND_ROM),
136*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 16MiB 3,3V",      0x73, 16,  SZ_16K, 0),
137*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM",  0x57, 16,  SZ_16K, NAND_ROM),
138*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 32MiB 3,3V",      0x75, 32,  SZ_16K, 0),
139*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM",  0x58, 32,  SZ_16K, NAND_ROM),
140*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 64MiB 3,3V",      0x76, 64,  SZ_16K, 0),
141*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM",  0xd9, 64,  SZ_16K, NAND_ROM),
142*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 128MiB 3,3V",     0x79, 128, SZ_16K, 0),
143*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM),
144*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V",    0x71, 256, SZ_16K, 0),
145*4882a593Smuzhiyun 	LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM),
146*4882a593Smuzhiyun 	{NULL}
147*4882a593Smuzhiyun };
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static struct nand_flash_dev nand_xd_flash_ids[] = {
150*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 16MiB 3,3V",  0x73, 16,   SZ_16K, 0),
151*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 32MiB 3,3V",  0x75, 32,   SZ_16K, 0),
152*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 64MiB 3,3V",  0x76, 64,   SZ_16K, 0),
153*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128,  SZ_16K, 0),
154*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256,  SZ_16K, NAND_BROKEN_XD),
155*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512,  SZ_16K, NAND_BROKEN_XD),
156*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 1GiB 3,3V",   0xd3, 1024, SZ_16K, NAND_BROKEN_XD),
157*4882a593Smuzhiyun 	LEGACY_ID_NAND("xD 2GiB 3,3V",   0xd5, 2048, SZ_16K, NAND_BROKEN_XD),
158*4882a593Smuzhiyun 	{NULL}
159*4882a593Smuzhiyun };
160*4882a593Smuzhiyun 
sm_attach_chip(struct nand_chip * chip)161*4882a593Smuzhiyun static int sm_attach_chip(struct nand_chip *chip)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct mtd_info *mtd = nand_to_mtd(chip);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	/* Bad block marker position */
166*4882a593Smuzhiyun 	chip->badblockpos = 0x05;
167*4882a593Smuzhiyun 	chip->badblockbits = 7;
168*4882a593Smuzhiyun 	chip->legacy.block_markbad = sm_block_markbad;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/* ECC layout */
171*4882a593Smuzhiyun 	if (mtd->writesize == SM_SECTOR_SIZE)
172*4882a593Smuzhiyun 		mtd_set_ooblayout(mtd, &oob_sm_ops);
173*4882a593Smuzhiyun 	else if (mtd->writesize == SM_SMALL_PAGE)
174*4882a593Smuzhiyun 		mtd_set_ooblayout(mtd, &oob_sm_small_ops);
175*4882a593Smuzhiyun 	else
176*4882a593Smuzhiyun 		return -ENODEV;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	return 0;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun static const struct nand_controller_ops sm_controller_ops = {
182*4882a593Smuzhiyun 	.attach_chip = sm_attach_chip,
183*4882a593Smuzhiyun };
184*4882a593Smuzhiyun 
sm_register_device(struct mtd_info * mtd,int smartmedia)185*4882a593Smuzhiyun int sm_register_device(struct mtd_info *mtd, int smartmedia)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	struct nand_chip *chip = mtd_to_nand(mtd);
188*4882a593Smuzhiyun 	struct nand_flash_dev *flash_ids;
189*4882a593Smuzhiyun 	int ret;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	chip->options |= NAND_SKIP_BBTSCAN;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	/* Scan for card properties */
194*4882a593Smuzhiyun 	chip->legacy.dummy_controller.ops = &sm_controller_ops;
195*4882a593Smuzhiyun 	flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids;
196*4882a593Smuzhiyun 	ret = nand_scan_with_ids(chip, 1, flash_ids);
197*4882a593Smuzhiyun 	if (ret)
198*4882a593Smuzhiyun 		return ret;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	ret = mtd_device_register(mtd, NULL, 0);
201*4882a593Smuzhiyun 	if (ret)
202*4882a593Smuzhiyun 		nand_cleanup(chip);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	return ret;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(sm_register_device);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun MODULE_LICENSE("GPL");
209*4882a593Smuzhiyun MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
210*4882a593Smuzhiyun MODULE_DESCRIPTION("Common SmartMedia/xD functions");
211