blob: 4c6ddd92331522b75f48660bcbf1f73510b51356 [file] [log] [blame]
Michael Trimarchi66483b32022-07-20 18:22:14 +02001// 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
Arseniy Krasnovafa246a2023-11-30 14:24:05 +030019#include <dm/device_compat.h>
Michael Trimarchi66483b32022-07-20 18:22:14 +020020#include <linux/mtd/rawnand.h>
21
Arseniy Krasnovafa246a2023-11-30 14:24:05 +030022#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90
23#define MACRONIX_30LFXG18AC_OTP_START_PAGE 2
24#define MACRONIX_30LFXG18AC_OTP_PAGES 30
25#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112
26#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \
27 (MACRONIX_30LFXG18AC_OTP_PAGES * \
28 MACRONIX_30LFXG18AC_OTP_PAGE_SIZE)
29
30#define MACRONIX_30LFXG18AC_OTP_EN BIT(0)
31
32static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len,
33 size_t *retlen,
34 struct otp_info *buf)
35{
36 if (len < sizeof(*buf))
37 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 buf->locked = 0;
49 buf->start = 0;
50 buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES;
51
52 *retlen = sizeof(*buf);
53
54 return 0;
55}
56
57static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand)
58{
59 u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 };
60 struct mtd_info *mtd;
61
62 mtd = nand_to_mtd(nand);
63 feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN;
64
65 return nand->onfi_set_features(mtd, nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, feature_buf);
66}
67
68static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand)
69{
70 u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 };
71 struct mtd_info *mtd;
72
73 mtd = nand_to_mtd(nand);
74 return nand->onfi_set_features(mtd, nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, feature_buf);
75}
76
77static 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{
82 struct nand_chip *nand;
83 size_t bytes_handled;
84 off_t offs_in_page;
85 u64 page;
86 int ret;
87
88 nand = mtd_to_nand(mtd);
89 nand->select_chip(mtd, 0);
90
91 ret = macronix_30lfxg18ac_otp_enable(nand);
92 if (ret)
93 goto out_otp;
94
95 page = offs_in_flash;
96 /* 'page' will be result of division. */
97 offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE);
98 bytes_handled = 0;
99
100 while (bytes_handled < len &&
101 page < MACRONIX_30LFXG18AC_OTP_PAGES) {
102 size_t bytes_to_handle;
103 u64 phys_page = page + MACRONIX_30LFXG18AC_OTP_START_PAGE;
104
105 bytes_to_handle = min_t(size_t, len - bytes_handled,
106 MACRONIX_30LFXG18AC_OTP_PAGE_SIZE -
107 offs_in_page);
108
109 if (write)
110 ret = nand_prog_page_op(nand, phys_page, offs_in_page,
111 &buf[bytes_handled], bytes_to_handle);
112 else
113 ret = nand_read_page_op(nand, phys_page, offs_in_page,
114 &buf[bytes_handled], bytes_to_handle);
115 if (ret)
116 goto out_otp;
117
118 bytes_handled += bytes_to_handle;
119 offs_in_page = 0;
120 page++;
121 }
122
123 *retlen = bytes_handled;
124
125out_otp:
126 if (ret)
127 dev_err(mtd->dev, "failed to perform OTP IO: %i\n", ret);
128
129 ret = macronix_30lfxg18ac_otp_disable(nand);
130 if (ret)
131 dev_err(mtd->dev, "failed to leave OTP mode after %s\n",
132 write ? "write" : "read");
133
134 nand->select_chip(mtd, -1);
135
136 return ret;
137}
138
139static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to,
140 size_t len, size_t *rlen,
141 u_char *buf)
142{
143 return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf,
144 true);
145}
146
147static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from,
148 size_t len, size_t *rlen,
149 u_char *buf)
150{
151 return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false);
152}
153
154static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from,
155 size_t len)
156{
157 /* See comment in 'macronix_30lfxg18ac_get_otp_info()'. */
158 return -EOPNOTSUPP;
159}
160
161static void macronix_nand_setup_otp(struct nand_chip *chip)
162{
163 static const char * const supported_otp_models[] = {
164 "MX30LF1G18AC",
165 "MX30LF2G18AC",
166 "MX30LF4G18AC",
167 };
168 int i;
169
170 if (!chip->onfi_version ||
171 !(le16_to_cpu(chip->onfi_params.opt_cmd)
172 & ONFI_OPT_CMD_SET_GET_FEATURES))
173 return;
174
175 for (i = 0; i < ARRAY_SIZE(supported_otp_models); i++) {
176 if (!strcmp(chip->onfi_params.model, supported_otp_models[i])) {
177 struct mtd_info *mtd;
178
179 mtd = nand_to_mtd(chip);
180 mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info;
181 mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp;
182 mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp;
183 mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp;
184 return;
185 }
186 }
187}
188
Michael Trimarchi66483b32022-07-20 18:22:14 +0200189static int macronix_nand_init(struct nand_chip *chip)
190{
191 if (nand_is_slc(chip))
192 chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
193
Arseniy Krasnovafa246a2023-11-30 14:24:05 +0300194 macronix_nand_setup_otp(chip);
195
Michael Trimarchi66483b32022-07-20 18:22:14 +0200196 return 0;
197}
198
199const struct nand_manufacturer_ops macronix_nand_manuf_ops = {
200 .init = macronix_nand_init,
201};