xref: /openbmc/u-boot/drivers/mtd/nand/spi/gigadevice.c (revision ad8c9f614620ec77d3c0f8963d61535038c39f09)
1*9e5c2a75SStefan Roese // SPDX-License-Identifier: GPL-2.0
2*9e5c2a75SStefan Roese /*
3*9e5c2a75SStefan Roese  * Copyright (C) 2018 Stefan Roese <sr@denx.de>
4*9e5c2a75SStefan Roese  *
5*9e5c2a75SStefan Roese  * Derived from drivers/mtd/nand/spi/micron.c
6*9e5c2a75SStefan Roese  *   Copyright (c) 2016-2017 Micron Technology, Inc.
7*9e5c2a75SStefan Roese  */
8*9e5c2a75SStefan Roese 
9*9e5c2a75SStefan Roese #ifndef __UBOOT__
10*9e5c2a75SStefan Roese #include <linux/device.h>
11*9e5c2a75SStefan Roese #include <linux/kernel.h>
12*9e5c2a75SStefan Roese #endif
13*9e5c2a75SStefan Roese #include <linux/mtd/spinand.h>
14*9e5c2a75SStefan Roese 
15*9e5c2a75SStefan Roese #define SPINAND_MFR_GIGADEVICE			0xc8
16*9e5c2a75SStefan Roese 
17*9e5c2a75SStefan Roese #define GIGADEVICE_STATUS_ECC_MASK		GENMASK(5, 4)
18*9e5c2a75SStefan Roese #define GIGADEVICE_STATUS_ECC_NO_BITFLIPS	(0 << 4)
19*9e5c2a75SStefan Roese #define GIGADEVICE_STATUS_ECC_1TO7_BITFLIPS	(1 << 4)
20*9e5c2a75SStefan Roese #define GIGADEVICE_STATUS_ECC_8_BITFLIPS	(3 << 4)
21*9e5c2a75SStefan Roese 
22*9e5c2a75SStefan Roese static SPINAND_OP_VARIANTS(read_cache_variants,
23*9e5c2a75SStefan Roese 		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
24*9e5c2a75SStefan Roese 		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
25*9e5c2a75SStefan Roese 		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
26*9e5c2a75SStefan Roese 		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
27*9e5c2a75SStefan Roese 		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
28*9e5c2a75SStefan Roese 		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
29*9e5c2a75SStefan Roese 
30*9e5c2a75SStefan Roese static SPINAND_OP_VARIANTS(write_cache_variants,
31*9e5c2a75SStefan Roese 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
32*9e5c2a75SStefan Roese 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
33*9e5c2a75SStefan Roese 
34*9e5c2a75SStefan Roese static SPINAND_OP_VARIANTS(update_cache_variants,
35*9e5c2a75SStefan Roese 		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
36*9e5c2a75SStefan Roese 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
37*9e5c2a75SStefan Roese 
gd5f1gq4u_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * region)38*9e5c2a75SStefan Roese static int gd5f1gq4u_ooblayout_ecc(struct mtd_info *mtd, int section,
39*9e5c2a75SStefan Roese 				   struct mtd_oob_region *region)
40*9e5c2a75SStefan Roese {
41*9e5c2a75SStefan Roese 	if (section)
42*9e5c2a75SStefan Roese 		return -ERANGE;
43*9e5c2a75SStefan Roese 
44*9e5c2a75SStefan Roese 	region->offset = 64;
45*9e5c2a75SStefan Roese 	region->length = 64;
46*9e5c2a75SStefan Roese 
47*9e5c2a75SStefan Roese 	return 0;
48*9e5c2a75SStefan Roese }
49*9e5c2a75SStefan Roese 
gd5f1gq4u_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * region)50*9e5c2a75SStefan Roese static int gd5f1gq4u_ooblayout_free(struct mtd_info *mtd, int section,
51*9e5c2a75SStefan Roese 				    struct mtd_oob_region *region)
52*9e5c2a75SStefan Roese {
53*9e5c2a75SStefan Roese 	if (section)
54*9e5c2a75SStefan Roese 		return -ERANGE;
55*9e5c2a75SStefan Roese 
56*9e5c2a75SStefan Roese 	/* Reserve 2 bytes for the BBM. */
57*9e5c2a75SStefan Roese 	region->offset = 2;
58*9e5c2a75SStefan Roese 	region->length = 62;
59*9e5c2a75SStefan Roese 
60*9e5c2a75SStefan Roese 	return 0;
61*9e5c2a75SStefan Roese }
62*9e5c2a75SStefan Roese 
63*9e5c2a75SStefan Roese static const struct mtd_ooblayout_ops gd5f1gq4u_ooblayout = {
64*9e5c2a75SStefan Roese 	.ecc = gd5f1gq4u_ooblayout_ecc,
65*9e5c2a75SStefan Roese 	.free = gd5f1gq4u_ooblayout_free,
66*9e5c2a75SStefan Roese };
67*9e5c2a75SStefan Roese 
gd5f1gq4u_ecc_get_status(struct spinand_device * spinand,u8 status)68*9e5c2a75SStefan Roese static int gd5f1gq4u_ecc_get_status(struct spinand_device *spinand,
69*9e5c2a75SStefan Roese 				    u8 status)
70*9e5c2a75SStefan Roese {
71*9e5c2a75SStefan Roese 	if (status)
72*9e5c2a75SStefan Roese 		debug("%s (%d): status=%02x\n", __func__, __LINE__, status);
73*9e5c2a75SStefan Roese 
74*9e5c2a75SStefan Roese 	switch (status & GIGADEVICE_STATUS_ECC_MASK) {
75*9e5c2a75SStefan Roese 	case STATUS_ECC_NO_BITFLIPS:
76*9e5c2a75SStefan Roese 		return 0;
77*9e5c2a75SStefan Roese 
78*9e5c2a75SStefan Roese 	case GIGADEVICE_STATUS_ECC_1TO7_BITFLIPS:
79*9e5c2a75SStefan Roese 		return 7;
80*9e5c2a75SStefan Roese 
81*9e5c2a75SStefan Roese 	case GIGADEVICE_STATUS_ECC_8_BITFLIPS:
82*9e5c2a75SStefan Roese 		return 8;
83*9e5c2a75SStefan Roese 
84*9e5c2a75SStefan Roese 	case STATUS_ECC_UNCOR_ERROR:
85*9e5c2a75SStefan Roese 		return -EBADMSG;
86*9e5c2a75SStefan Roese 
87*9e5c2a75SStefan Roese 	default:
88*9e5c2a75SStefan Roese 		break;
89*9e5c2a75SStefan Roese 	}
90*9e5c2a75SStefan Roese 
91*9e5c2a75SStefan Roese 	return -EINVAL;
92*9e5c2a75SStefan Roese }
93*9e5c2a75SStefan Roese 
94*9e5c2a75SStefan Roese static const struct spinand_info gigadevice_spinand_table[] = {
95*9e5c2a75SStefan Roese 	SPINAND_INFO("GD5F1GQ4UC", 0xd1,
96*9e5c2a75SStefan Roese 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
97*9e5c2a75SStefan Roese 		     NAND_ECCREQ(8, 2048),
98*9e5c2a75SStefan Roese 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
99*9e5c2a75SStefan Roese 					      &write_cache_variants,
100*9e5c2a75SStefan Roese 					      &update_cache_variants),
101*9e5c2a75SStefan Roese 		     0,
102*9e5c2a75SStefan Roese 		     SPINAND_ECCINFO(&gd5f1gq4u_ooblayout,
103*9e5c2a75SStefan Roese 				     gd5f1gq4u_ecc_get_status)),
104*9e5c2a75SStefan Roese };
105*9e5c2a75SStefan Roese 
gigadevice_spinand_detect(struct spinand_device * spinand)106*9e5c2a75SStefan Roese static int gigadevice_spinand_detect(struct spinand_device *spinand)
107*9e5c2a75SStefan Roese {
108*9e5c2a75SStefan Roese 	u8 *id = spinand->id.data;
109*9e5c2a75SStefan Roese 	int ret;
110*9e5c2a75SStefan Roese 
111*9e5c2a75SStefan Roese 	/*
112*9e5c2a75SStefan Roese 	 * Gigadevice SPI NAND read ID need a dummy byte,
113*9e5c2a75SStefan Roese 	 * so the first byte in raw_id is dummy.
114*9e5c2a75SStefan Roese 	 */
115*9e5c2a75SStefan Roese 	if (id[1] != SPINAND_MFR_GIGADEVICE)
116*9e5c2a75SStefan Roese 		return 0;
117*9e5c2a75SStefan Roese 
118*9e5c2a75SStefan Roese 	ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
119*9e5c2a75SStefan Roese 				     ARRAY_SIZE(gigadevice_spinand_table),
120*9e5c2a75SStefan Roese 				     id[2]);
121*9e5c2a75SStefan Roese 	if (ret)
122*9e5c2a75SStefan Roese 		return ret;
123*9e5c2a75SStefan Roese 
124*9e5c2a75SStefan Roese 	return 1;
125*9e5c2a75SStefan Roese }
126*9e5c2a75SStefan Roese 
127*9e5c2a75SStefan Roese static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
128*9e5c2a75SStefan Roese 	.detect = gigadevice_spinand_detect,
129*9e5c2a75SStefan Roese };
130*9e5c2a75SStefan Roese 
131*9e5c2a75SStefan Roese const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
132*9e5c2a75SStefan Roese 	.id = SPINAND_MFR_GIGADEVICE,
133*9e5c2a75SStefan Roese 	.name = "GigaDevice",
134*9e5c2a75SStefan Roese 	.ops = &gigadevice_spinand_manuf_ops,
135*9e5c2a75SStefan Roese };
136