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