/src/u-boot/drivers/mtd/nand/raw/nand_macronix.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | /* |
3 | | * Copyright (C) 2017 Free Electrons |
4 | | * Copyright (C) 2017 NextThing Co |
5 | | * |
6 | | * Author: Boris Brezillon <boris.brezillon@free-electrons.com> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | */ |
18 | | |
19 | | #include <dm/device_compat.h> |
20 | | #include <linux/mtd/rawnand.h> |
21 | | |
22 | 0 | #define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 |
23 | 0 | #define MACRONIX_30LFXG18AC_OTP_START_PAGE 2 |
24 | 0 | #define MACRONIX_30LFXG18AC_OTP_PAGES 30 |
25 | 0 | #define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 |
26 | | #define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ |
27 | 0 | (MACRONIX_30LFXG18AC_OTP_PAGES * \ |
28 | 0 | MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) |
29 | | |
30 | 0 | #define MACRONIX_30LFXG18AC_OTP_EN BIT(0) |
31 | | |
32 | | static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, |
33 | | size_t *retlen, |
34 | | struct otp_info *buf) |
35 | 0 | { |
36 | 0 | if (len < sizeof(*buf)) |
37 | 0 | return -EINVAL; |
38 | | |
39 | | /* Always report that OTP is unlocked. Reason is that this |
40 | | * type of flash chip doesn't provide way to check that OTP |
41 | | * is locked or not: subfeature parameter is implemented as |
42 | | * volatile register. Technically OTP region could be locked |
43 | | * and become readonly, but as there is no way to check it, |
44 | | * don't allow to lock it ('_lock_user_prot_reg' callback |
45 | | * always returns -EOPNOTSUPP) and thus we report that OTP |
46 | | * is unlocked. |
47 | | */ |
48 | 0 | buf->locked = 0; |
49 | 0 | buf->start = 0; |
50 | 0 | buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; |
51 | |
|
52 | 0 | *retlen = sizeof(*buf); |
53 | |
|
54 | 0 | return 0; |
55 | 0 | } |
56 | | |
57 | | static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) |
58 | 0 | { |
59 | 0 | u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; |
60 | 0 | struct mtd_info *mtd; |
61 | |
|
62 | 0 | mtd = nand_to_mtd(nand); |
63 | 0 | feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; |
64 | |
|
65 | 0 | return nand->onfi_set_features(mtd, nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, feature_buf); |
66 | 0 | } |
67 | | |
68 | | static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) |
69 | 0 | { |
70 | 0 | u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; |
71 | 0 | struct mtd_info *mtd; |
72 | |
|
73 | 0 | mtd = nand_to_mtd(nand); |
74 | 0 | return nand->onfi_set_features(mtd, nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, feature_buf); |
75 | 0 | } |
76 | | |
77 | | static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, |
78 | | loff_t offs_in_flash, |
79 | | size_t len, size_t *retlen, |
80 | | u_char *buf, bool write) |
81 | 0 | { |
82 | 0 | struct nand_chip *nand; |
83 | 0 | size_t bytes_handled; |
84 | 0 | off_t offs_in_page; |
85 | 0 | u64 page; |
86 | 0 | int ret; |
87 | |
|
88 | 0 | nand = mtd_to_nand(mtd); |
89 | 0 | nand->select_chip(mtd, 0); |
90 | |
|
91 | 0 | ret = macronix_30lfxg18ac_otp_enable(nand); |
92 | 0 | if (ret) |
93 | 0 | goto out_otp; |
94 | | |
95 | 0 | page = offs_in_flash; |
96 | | /* 'page' will be result of division. */ |
97 | 0 | offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); |
98 | 0 | bytes_handled = 0; |
99 | |
|
100 | 0 | while (bytes_handled < len && |
101 | 0 | page < MACRONIX_30LFXG18AC_OTP_PAGES) { |
102 | 0 | size_t bytes_to_handle; |
103 | 0 | u64 phys_page = page + MACRONIX_30LFXG18AC_OTP_START_PAGE; |
104 | |
|
105 | 0 | bytes_to_handle = min_t(size_t, len - bytes_handled, |
106 | 0 | MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - |
107 | 0 | offs_in_page); |
108 | |
|
109 | 0 | if (write) |
110 | 0 | ret = nand_prog_page_op(nand, phys_page, offs_in_page, |
111 | 0 | &buf[bytes_handled], bytes_to_handle); |
112 | 0 | else |
113 | 0 | ret = nand_read_page_op(nand, phys_page, offs_in_page, |
114 | 0 | &buf[bytes_handled], bytes_to_handle); |
115 | 0 | if (ret) |
116 | 0 | goto out_otp; |
117 | | |
118 | 0 | bytes_handled += bytes_to_handle; |
119 | 0 | offs_in_page = 0; |
120 | 0 | page++; |
121 | 0 | } |
122 | | |
123 | 0 | *retlen = bytes_handled; |
124 | |
|
125 | 0 | out_otp: |
126 | 0 | if (ret) |
127 | 0 | dev_err(mtd->dev, "failed to perform OTP IO: %i\n", ret); |
128 | |
|
129 | 0 | ret = macronix_30lfxg18ac_otp_disable(nand); |
130 | 0 | if (ret) |
131 | 0 | dev_err(mtd->dev, "failed to leave OTP mode after %s\n", |
132 | 0 | write ? "write" : "read"); |
133 | |
|
134 | 0 | nand->select_chip(mtd, -1); |
135 | |
|
136 | 0 | return ret; |
137 | 0 | } |
138 | | |
139 | | static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, |
140 | | size_t len, size_t *rlen, |
141 | | u_char *buf) |
142 | 0 | { |
143 | 0 | return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, |
144 | 0 | true); |
145 | 0 | } |
146 | | |
147 | | static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, |
148 | | size_t len, size_t *rlen, |
149 | | u_char *buf) |
150 | 0 | { |
151 | 0 | return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); |
152 | 0 | } |
153 | | |
154 | | static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, |
155 | | size_t len) |
156 | 0 | { |
157 | | /* See comment in 'macronix_30lfxg18ac_get_otp_info()'. */ |
158 | 0 | return -EOPNOTSUPP; |
159 | 0 | } |
160 | | |
161 | | static void macronix_nand_setup_otp(struct nand_chip *chip) |
162 | 0 | { |
163 | 0 | static const char * const supported_otp_models[] = { |
164 | 0 | "MX30LF1G18AC", |
165 | 0 | "MX30LF2G18AC", |
166 | 0 | "MX30LF4G18AC", |
167 | 0 | }; |
168 | 0 | int i; |
169 | |
|
170 | 0 | if (!chip->onfi_version || |
171 | 0 | !(le16_to_cpu(chip->onfi_params.opt_cmd) |
172 | 0 | & ONFI_OPT_CMD_SET_GET_FEATURES)) |
173 | 0 | return; |
174 | | |
175 | 0 | for (i = 0; i < ARRAY_SIZE(supported_otp_models); i++) { |
176 | 0 | if (!strcmp(chip->onfi_params.model, supported_otp_models[i])) { |
177 | 0 | struct mtd_info *mtd; |
178 | |
|
179 | 0 | mtd = nand_to_mtd(chip); |
180 | 0 | mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; |
181 | 0 | mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; |
182 | 0 | mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; |
183 | 0 | mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; |
184 | 0 | return; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | | static int macronix_nand_init(struct nand_chip *chip) |
190 | 0 | { |
191 | 0 | if (nand_is_slc(chip)) |
192 | 0 | chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; |
193 | |
|
194 | 0 | macronix_nand_setup_otp(chip); |
195 | |
|
196 | 0 | return 0; |
197 | 0 | } |
198 | | |
199 | | const struct nand_manufacturer_ops macronix_nand_manuf_ops = { |
200 | | .init = macronix_nand_init, |
201 | | }; |