blob: ba0013e7f10b28820725318d2c5d051195453d43 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 ASR Micro Limited
4 *
5 * Authors:
6 * Fei Lv <feilv@asrmicro.com>
7 */
8
9#include <linux/device.h>
10#include <linux/kernel.h>
11#include <linux/mtd/spinand.h>
12
13#define SPINAND_MFR_FMSH 0xA1
14
15#define FM25LG02B_STATUS_ECC_MASK (7 << 4)
16#define FM25LG02B_STATUS_ECC_NO_BITFLIPS (0 << 4)
17#define FM25LG02B_STATUS_ECC_1_3_BITFLIPS (1 << 4)
18#define FM25LG02B_STATUS_ECC_4_BITFLIPS (2 << 4)
19#define FM25LG02B_STATUS_ECC_5_BITFLIPS (3 << 4)
20#define FM25LG02B_STATUS_ECC_6_BITFLIPS (4 << 4)
21#define FM25LG02B_STATUS_ECC_7_BITFLIPS (5 << 4)
22#define FM25LG02B_STATUS_ECC_8_BITFLIPS (6 << 4)
23#define FM25LG02B_STATUS_ECC_UNCOR_ERROR (7 << 4)
24
25static SPINAND_OP_VARIANTS(read_cache_variants,
26 /* 0xEB only can work @40MHz, not use now */
27 //SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
28 SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
29 SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
30 SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
31 SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
32 SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
33
34static SPINAND_OP_VARIANTS(write_cache_variants,
35 SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
36 SPINAND_PROG_LOAD(true, 0, NULL, 0));
37
38static SPINAND_OP_VARIANTS(update_cache_variants,
39 SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
40 SPINAND_PROG_LOAD(false, 0, NULL, 0));
41
42
43static int fm25lg02_ecc_get_status(struct spinand_device *spinand, u8 status)
44{
45 switch (status & FM25LG02B_STATUS_ECC_MASK) {
46 case FM25LG02B_STATUS_ECC_NO_BITFLIPS:
47 return 0;
48
49 case FM25LG02B_STATUS_ECC_1_3_BITFLIPS:
50 return 3;
51
52 case FM25LG02B_STATUS_ECC_4_BITFLIPS:
53 return 4;
54
55 case FM25LG02B_STATUS_ECC_5_BITFLIPS:
56 return 5;
57
58 case FM25LG02B_STATUS_ECC_6_BITFLIPS:
59 return 6;
60
61 case FM25LG02B_STATUS_ECC_7_BITFLIPS:
62 return 7;
63
64 case FM25LG02B_STATUS_ECC_8_BITFLIPS:
65 return 8;
66
67 case FM25LG02B_STATUS_ECC_UNCOR_ERROR:
68 return -EBADMSG;
69
70 default:
71 break;
72 }
73
74 return -EINVAL;
75}
76
77static int fm25ls01_ooblayout_ecc(struct mtd_info *mtd, int section,
78 struct mtd_oob_region *region)
79{
80 if (section)
81 return -ERANGE;
82
83 region->offset = 64;
84 region->length = 64;
85
86 return 0;
87}
88
89static int fm25ls01_ooblayout_free(struct mtd_info *mtd, int section,
90 struct mtd_oob_region *region)
91{
92 if (section)
93 return -ERANGE;
94
95 /* Reserve 2 bytes for the BBM. */
96 region->offset = 2;
97 region->length = 62;
98
99 return 0;
100}
101
102static const struct mtd_ooblayout_ops fm25ls01_ooblayout = {
103 .ecc = fm25ls01_ooblayout_ecc,
104 .free = fm25ls01_ooblayout_free,
105};
106
107static const struct spinand_info fmsh_spinand_table[] = {
108 SPINAND_INFO("FM25LS01", 0xA5,
109 NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
110 NAND_ECCREQ(1, 512),
111 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
112 &write_cache_variants,
113 &update_cache_variants),
114 0,
115 SPINAND_ECCINFO(&fm25ls01_ooblayout, NULL)),
116
117 SPINAND_INFO("FM25LG02B", 0xB2,
118 NAND_MEMORG(1, 2048, 128, 64, 2048, 20, 1, 1, 1),
119 NAND_ECCREQ(1, 512),
120 SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
121 &write_cache_variants,
122 &update_cache_variants),
123 0,
124 SPINAND_ECCINFO(&fm25ls01_ooblayout,
125 &fm25lg02_ecc_get_status)),
126};
127
128/**
129 * fmsh_spinand_detect - initialize device related part in spinand_device
130 * struct if it is a FudanMicro device.
131 * @spinand: SPI NAND device structure
132 */
133static int fmsh_spinand_detect(struct spinand_device *spinand)
134{
135 u8 *id = spinand->id.data;
136 int ret;
137
138 /*
139 * FudanMicro SPI NAND read ID need a dummy byte,
140 * so the first byte in raw_id is dummy.
141 */
142 if (id[1] != SPINAND_MFR_FMSH)
143 return 0;
144
145 ret = spinand_match_and_init(spinand, fmsh_spinand_table,
146 ARRAY_SIZE(fmsh_spinand_table), id[2]);
147 if (ret)
148 return ret;
149
150 return 1;
151}
152
153static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
154 .detect = fmsh_spinand_detect,
155};
156
157const struct spinand_manufacturer fmsh_spinand_manufacturer = {
158 .id = SPINAND_MFR_FMSH,
159 .name = "FudanMicro",
160 .ops = &fmsh_spinand_manuf_ops,
161};