xref: /linux/drivers/mtd/nand/spi/micron.c (revision a508e8875e135d7a1df26d8131b5443cb07005ff)
1*a508e887SPeter Pan // SPDX-License-Identifier: GPL-2.0
2*a508e887SPeter Pan /*
3*a508e887SPeter Pan  * Copyright (c) 2016-2017 Micron Technology, Inc.
4*a508e887SPeter Pan  *
5*a508e887SPeter Pan  * Authors:
6*a508e887SPeter Pan  *	Peter Pan <peterpandong@micron.com>
7*a508e887SPeter Pan  */
8*a508e887SPeter Pan 
9*a508e887SPeter Pan #include <linux/device.h>
10*a508e887SPeter Pan #include <linux/kernel.h>
11*a508e887SPeter Pan #include <linux/mtd/spinand.h>
12*a508e887SPeter Pan 
13*a508e887SPeter Pan #define SPINAND_MFR_MICRON		0x2c
14*a508e887SPeter Pan 
15*a508e887SPeter Pan #define MICRON_STATUS_ECC_MASK		GENMASK(7, 4)
16*a508e887SPeter Pan #define MICRON_STATUS_ECC_NO_BITFLIPS	(0 << 4)
17*a508e887SPeter Pan #define MICRON_STATUS_ECC_1TO3_BITFLIPS	(1 << 4)
18*a508e887SPeter Pan #define MICRON_STATUS_ECC_4TO6_BITFLIPS	(3 << 4)
19*a508e887SPeter Pan #define MICRON_STATUS_ECC_7TO8_BITFLIPS	(5 << 4)
20*a508e887SPeter Pan 
21*a508e887SPeter Pan static SPINAND_OP_VARIANTS(read_cache_variants,
22*a508e887SPeter Pan 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
23*a508e887SPeter Pan 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
24*a508e887SPeter Pan 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
25*a508e887SPeter Pan 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
26*a508e887SPeter Pan 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
27*a508e887SPeter Pan 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
28*a508e887SPeter Pan 
29*a508e887SPeter Pan static SPINAND_OP_VARIANTS(write_cache_variants,
30*a508e887SPeter Pan 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
31*a508e887SPeter Pan 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
32*a508e887SPeter Pan 
33*a508e887SPeter Pan static SPINAND_OP_VARIANTS(update_cache_variants,
34*a508e887SPeter Pan 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
35*a508e887SPeter Pan 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
36*a508e887SPeter Pan 
37*a508e887SPeter Pan static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section,
38*a508e887SPeter Pan 					struct mtd_oob_region *region)
39*a508e887SPeter Pan {
40*a508e887SPeter Pan 	if (section)
41*a508e887SPeter Pan 		return -ERANGE;
42*a508e887SPeter Pan 
43*a508e887SPeter Pan 	region->offset = 64;
44*a508e887SPeter Pan 	region->length = 64;
45*a508e887SPeter Pan 
46*a508e887SPeter Pan 	return 0;
47*a508e887SPeter Pan }
48*a508e887SPeter Pan 
49*a508e887SPeter Pan static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section,
50*a508e887SPeter Pan 					 struct mtd_oob_region *region)
51*a508e887SPeter Pan {
52*a508e887SPeter Pan 	if (section)
53*a508e887SPeter Pan 		return -ERANGE;
54*a508e887SPeter Pan 
55*a508e887SPeter Pan 	/* Reserve 2 bytes for the BBM. */
56*a508e887SPeter Pan 	region->offset = 2;
57*a508e887SPeter Pan 	region->length = 62;
58*a508e887SPeter Pan 
59*a508e887SPeter Pan 	return 0;
60*a508e887SPeter Pan }
61*a508e887SPeter Pan 
62*a508e887SPeter Pan static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = {
63*a508e887SPeter Pan 	.ecc = mt29f2g01abagd_ooblayout_ecc,
64*a508e887SPeter Pan 	.free = mt29f2g01abagd_ooblayout_free,
65*a508e887SPeter Pan };
66*a508e887SPeter Pan 
67*a508e887SPeter Pan static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand,
68*a508e887SPeter Pan 					 u8 status)
69*a508e887SPeter Pan {
70*a508e887SPeter Pan 	switch (status & MICRON_STATUS_ECC_MASK) {
71*a508e887SPeter Pan 	case STATUS_ECC_NO_BITFLIPS:
72*a508e887SPeter Pan 		return 0;
73*a508e887SPeter Pan 
74*a508e887SPeter Pan 	case STATUS_ECC_UNCOR_ERROR:
75*a508e887SPeter Pan 		return -EBADMSG;
76*a508e887SPeter Pan 
77*a508e887SPeter Pan 	case MICRON_STATUS_ECC_1TO3_BITFLIPS:
78*a508e887SPeter Pan 		return 3;
79*a508e887SPeter Pan 
80*a508e887SPeter Pan 	case MICRON_STATUS_ECC_4TO6_BITFLIPS:
81*a508e887SPeter Pan 		return 6;
82*a508e887SPeter Pan 
83*a508e887SPeter Pan 	case MICRON_STATUS_ECC_7TO8_BITFLIPS:
84*a508e887SPeter Pan 		return 8;
85*a508e887SPeter Pan 
86*a508e887SPeter Pan 	default:
87*a508e887SPeter Pan 		break;
88*a508e887SPeter Pan 	}
89*a508e887SPeter Pan 
90*a508e887SPeter Pan 	return -EINVAL;
91*a508e887SPeter Pan }
92*a508e887SPeter Pan 
93*a508e887SPeter Pan static const struct spinand_info micron_spinand_table[] = {
94*a508e887SPeter Pan 	SPINAND_INFO("MT29F2G01ABAGD", 0x24,
95*a508e887SPeter Pan 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
96*a508e887SPeter Pan 		     NAND_ECCREQ(8, 512),
97*a508e887SPeter Pan 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
98*a508e887SPeter Pan 					      &write_cache_variants,
99*a508e887SPeter Pan 					      &update_cache_variants),
100*a508e887SPeter Pan 		     0,
101*a508e887SPeter Pan 		     SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout,
102*a508e887SPeter Pan 				     mt29f2g01abagd_ecc_get_status)),
103*a508e887SPeter Pan };
104*a508e887SPeter Pan 
105*a508e887SPeter Pan static int micron_spinand_detect(struct spinand_device *spinand)
106*a508e887SPeter Pan {
107*a508e887SPeter Pan 	u8 *id = spinand->id.data;
108*a508e887SPeter Pan 	int ret;
109*a508e887SPeter Pan 
110*a508e887SPeter Pan 	/*
111*a508e887SPeter Pan 	 * Micron SPI NAND read ID need a dummy byte,
112*a508e887SPeter Pan 	 * so the first byte in raw_id is dummy.
113*a508e887SPeter Pan 	 */
114*a508e887SPeter Pan 	if (id[1] != SPINAND_MFR_MICRON)
115*a508e887SPeter Pan 		return 0;
116*a508e887SPeter Pan 
117*a508e887SPeter Pan 	ret = spinand_match_and_init(spinand, micron_spinand_table,
118*a508e887SPeter Pan 				     ARRAY_SIZE(micron_spinand_table), id[2]);
119*a508e887SPeter Pan 	if (ret)
120*a508e887SPeter Pan 		return ret;
121*a508e887SPeter Pan 
122*a508e887SPeter Pan 	return 1;
123*a508e887SPeter Pan }
124*a508e887SPeter Pan 
125*a508e887SPeter Pan static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
126*a508e887SPeter Pan 	.detect = micron_spinand_detect,
127*a508e887SPeter Pan };
128*a508e887SPeter Pan 
129*a508e887SPeter Pan const struct spinand_manufacturer micron_spinand_manufacturer = {
130*a508e887SPeter Pan 	.id = SPINAND_MFR_MICRON,
131*a508e887SPeter Pan 	.name = "Micron",
132*a508e887SPeter Pan 	.ops = &micron_spinand_manuf_ops,
133*a508e887SPeter Pan };
134