File Index
Files and directories
Sign in
FILE

otp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mtd/nand/spi/otp.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2025, SaluteDevices. All Rights Reserved.
  4 *
  5 * Author: Martin Kurbanov <mmkurbanov@salutedevices.com>
  6 */
  7
  8#include <linux/mtd/mtd.h>
  9#include <linux/mtd/spinand.h>
 10
 11/**
 12 * spinand_otp_page_size() - Get SPI-NAND OTP page size
 13 * @spinand: the spinand device
 14 *
 15 * Return: the OTP page size.
 16 */
 17size_t spinand_otp_page_size(struct spinand_device *spinand)
 18{
 19	struct nand_device *nand = spinand_to_nand(spinand);
 20
 21	return nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
 22}
 23
 24static size_t spinand_otp_size(struct spinand_device *spinand,
 25			       const struct spinand_otp_layout *layout)
 26{
 27	return layout->npages * spinand_otp_page_size(spinand);
 28}
 29
 30/**
 31 * spinand_fact_otp_size() - Get SPI-NAND factory OTP area size
 32 * @spinand: the spinand device
 33 *
 34 * Return: the OTP size.
 35 */
 36size_t spinand_fact_otp_size(struct spinand_device *spinand)
 37{
 38	return spinand_otp_size(spinand, &spinand->fact_otp->layout);
 39}
 40
 41/**
 42 * spinand_user_otp_size() - Get SPI-NAND user OTP area size
 43 * @spinand: the spinand device
 44 *
 45 * Return: the OTP size.
 46 */
 47size_t spinand_user_otp_size(struct spinand_device *spinand)
 48{
 49	return spinand_otp_size(spinand, &spinand->user_otp->layout);
 50}
 51
 52static int spinand_otp_check_bounds(struct spinand_device *spinand, loff_t ofs,
 53				    size_t len,
 54				    const struct spinand_otp_layout *layout)
 55{
 56	if (ofs < 0 || ofs + len > spinand_otp_size(spinand, layout))
 57		return -EINVAL;
 58
 59	return 0;
 60}
 61
 62static int spinand_user_otp_check_bounds(struct spinand_device *spinand,
 63					 loff_t ofs, size_t len)
 64{
 65	return spinand_otp_check_bounds(spinand, ofs, len,
 66					&spinand->user_otp->layout);
 67}
 68
 69static int spinand_otp_rw(struct spinand_device *spinand, loff_t ofs,
 70			  size_t len, size_t *retlen, u8 *buf, bool is_write,
 71			  const struct spinand_otp_layout *layout)
 72{
 73	struct nand_page_io_req req = {};
 74	unsigned long long page;
 75	size_t copied = 0;
 76	size_t otp_pagesize = spinand_otp_page_size(spinand);
 77	int ret;
 78
 79	if (!len)
 80		return 0;
 81
 82	ret = spinand_otp_check_bounds(spinand, ofs, len, layout);
 83	if (ret)
 84		return ret;
 85
 86	ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, CFG_OTP_ENABLE);
 87	if (ret)
 88		return ret;
 89
 90	page = ofs;
 91	req.dataoffs = do_div(page, otp_pagesize);
 92	req.pos.page = page + layout->start_page;
 93	req.type = is_write ? NAND_PAGE_WRITE : NAND_PAGE_READ;
 94	req.mode = MTD_OPS_RAW;
 95	req.databuf.in = buf;
 96
 97	while (copied < len) {
 98		req.datalen = min_t(unsigned int,
 99				    otp_pagesize - req.dataoffs,
100				    len - copied);
101
102		if (is_write)
103			ret = spinand_write_page(spinand, &req);
104		else
105			ret = spinand_read_page(spinand, &req);
106
107		if (ret < 0)
108			break;
109
110		req.databuf.in += req.datalen;
111		req.pos.page++;
112		req.dataoffs = 0;
113		copied += req.datalen;
114	}
115
116	*retlen = copied;
117
118	if (spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0)) {
119		dev_warn(&spinand_to_mtd(spinand)->dev,
120			 "Can not disable OTP mode\n");
121		ret = -EIO;
122	}
123
124	return ret;
125}
126
127/**
128 * spinand_fact_otp_read() - Read from OTP area
129 * @spinand: the spinand device
130 * @ofs: the offset to read
131 * @len: the number of data bytes to read
132 * @retlen: the pointer to variable to store the number of read bytes
133 * @buf: the buffer to store the read data
134 *
135 * Return: 0 on success, an error code otherwise.
136 */
137int spinand_fact_otp_read(struct spinand_device *spinand, loff_t ofs,
138			  size_t len, size_t *retlen, u8 *buf)
139{
140	return spinand_otp_rw(spinand, ofs, len, retlen, buf, false,
141			      &spinand->fact_otp->layout);
142}
143
144/**
145 * spinand_user_otp_read() - Read from OTP area
146 * @spinand: the spinand device
147 * @ofs: the offset to read
148 * @len: the number of data bytes to read
149 * @retlen: the pointer to variable to store the number of read bytes
150 * @buf: the buffer to store the read data
151 *
152 * Return: 0 on success, an error code otherwise.
153 */
154int spinand_user_otp_read(struct spinand_device *spinand, loff_t ofs,
155			  size_t len, size_t *retlen, u8 *buf)
156{
157	return spinand_otp_rw(spinand, ofs, len, retlen, buf, false,
158			      &spinand->user_otp->layout);
159}
160
161/**
162 * spinand_user_otp_write() - Write to OTP area
163 * @spinand:  the spinand device
164 * @ofs: the offset to write to
165 * @len: the number of bytes to write
166 * @retlen: the pointer to variable to store the number of written bytes
167 * @buf: the buffer with data to write
168 *
169 * Return: 0 on success, an error code otherwise.
170 */
171int spinand_user_otp_write(struct spinand_device *spinand, loff_t ofs,
172			   size_t len, size_t *retlen, const u8 *buf)
173{
174	return spinand_otp_rw(spinand, ofs, len, retlen, (u8 *)buf, true,
175			      &spinand->user_otp->layout);
176}
177
178static int spinand_mtd_otp_info(struct mtd_info *mtd, size_t len,
179				size_t *retlen, struct otp_info *buf,
180				bool is_fact)
181{
182	struct spinand_device *spinand = mtd_to_spinand(mtd);
183	int ret;
184
185	*retlen = 0;
186
187	mutex_lock(&spinand->lock);
188
189	if (is_fact)
190		ret = spinand->fact_otp->ops->info(spinand, len, buf, retlen);
191	else
192		ret = spinand->user_otp->ops->info(spinand, len, buf, retlen);
193
194	mutex_unlock(&spinand->lock);
195
196	return ret;
197}
198
199static int spinand_mtd_fact_otp_info(struct mtd_info *mtd, size_t len,
200				     size_t *retlen, struct otp_info *buf)
201{
202	return spinand_mtd_otp_info(mtd, len, retlen, buf, true);
203}
204
205static int spinand_mtd_user_otp_info(struct mtd_info *mtd, size_t len,
206				     size_t *retlen, struct otp_info *buf)
207{
208	return spinand_mtd_otp_info(mtd, len, retlen, buf, false);
209}
210
211static int spinand_mtd_otp_read(struct mtd_info *mtd, loff_t ofs, size_t len,
212				size_t *retlen, u8 *buf, bool is_fact)
213{
214	struct spinand_device *spinand = mtd_to_spinand(mtd);
215	int ret;
216
217	*retlen = 0;
218
219	if (!len)
220		return 0;
221
222	ret = spinand_otp_check_bounds(spinand, ofs, len,
223				       is_fact ? &spinand->fact_otp->layout :
224						 &spinand->user_otp->layout);
225	if (ret)
226		return ret;
227
228	mutex_lock(&spinand->lock);
229
230	if (is_fact)
231		ret = spinand->fact_otp->ops->read(spinand, ofs, len, retlen,
232						   buf);
233	else
234		ret = spinand->user_otp->ops->read(spinand, ofs, len, retlen,
235						   buf);
236
237	mutex_unlock(&spinand->lock);
238
239	return ret;
240}
241
242static int spinand_mtd_fact_otp_read(struct mtd_info *mtd, loff_t ofs,
243				     size_t len, size_t *retlen, u8 *buf)
244{
245	return spinand_mtd_otp_read(mtd, ofs, len, retlen, buf, true);
246}
247
248static int spinand_mtd_user_otp_read(struct mtd_info *mtd, loff_t ofs,
249				     size_t len, size_t *retlen, u8 *buf)
250{
251	return spinand_mtd_otp_read(mtd, ofs, len, retlen, buf, false);
252}
253
254static int spinand_mtd_user_otp_write(struct mtd_info *mtd, loff_t ofs,
255				      size_t len, size_t *retlen, const u8 *buf)
256{
257	struct spinand_device *spinand = mtd_to_spinand(mtd);
258	const struct spinand_user_otp_ops *ops = spinand->user_otp->ops;
259	int ret;
260
261	*retlen = 0;
262
263	if (!len)
264		return 0;
265
266	ret = spinand_user_otp_check_bounds(spinand, ofs, len);
267	if (ret)
268		return ret;
269
270	mutex_lock(&spinand->lock);
271	ret = ops->write(spinand, ofs, len, retlen, buf);
272	mutex_unlock(&spinand->lock);
273
274	return ret;
275}
276
277static int spinand_mtd_user_otp_erase(struct mtd_info *mtd, loff_t ofs,
278				      size_t len)
279{
280	struct spinand_device *spinand = mtd_to_spinand(mtd);
281	const struct spinand_user_otp_ops *ops = spinand->user_otp->ops;
282	int ret;
283
284	if (!len)
285		return 0;
286
287	ret = spinand_user_otp_check_bounds(spinand, ofs, len);
288	if (ret)
289		return ret;
290
291	mutex_lock(&spinand->lock);
292	ret = ops->erase(spinand, ofs, len);
293	mutex_unlock(&spinand->lock);
294
295	return ret;
296}
297
298static int spinand_mtd_user_otp_lock(struct mtd_info *mtd, loff_t ofs,
299				     size_t len)
300{
301	struct spinand_device *spinand = mtd_to_spinand(mtd);
302	const struct spinand_user_otp_ops *ops = spinand->user_otp->ops;
303	int ret;
304
305	if (!len)
306		return 0;
307
308	ret = spinand_user_otp_check_bounds(spinand, ofs, len);
309	if (ret)
310		return ret;
311
312	mutex_lock(&spinand->lock);
313	ret = ops->lock(spinand, ofs, len);
314	mutex_unlock(&spinand->lock);
315
316	return ret;
317}
318
319/**
320 * spinand_set_mtd_otp_ops() - Setup OTP methods
321 * @spinand: the spinand device
322 *
323 * Setup OTP methods.
324 *
325 * Return: 0 on success, a negative error code otherwise.
326 */
327int spinand_set_mtd_otp_ops(struct spinand_device *spinand)
328{
329	struct mtd_info *mtd = spinand_to_mtd(spinand);
330	const struct spinand_fact_otp_ops *fact_ops = spinand->fact_otp->ops;
331	const struct spinand_user_otp_ops *user_ops = spinand->user_otp->ops;
332
333	if (!user_ops && !fact_ops)
334		return -EINVAL;
335
336	if (user_ops) {
337		if (user_ops->info)
338			mtd->_get_user_prot_info = spinand_mtd_user_otp_info;
339
340		if (user_ops->read)
341			mtd->_read_user_prot_reg = spinand_mtd_user_otp_read;
342
343		if (user_ops->write)
344			mtd->_write_user_prot_reg = spinand_mtd_user_otp_write;
345
346		if (user_ops->lock)
347			mtd->_lock_user_prot_reg = spinand_mtd_user_otp_lock;
348
349		if (user_ops->erase)
350			mtd->_erase_user_prot_reg = spinand_mtd_user_otp_erase;
351	}
352
353	if (fact_ops) {
354		if (fact_ops->info)
355			mtd->_get_fact_prot_info = spinand_mtd_fact_otp_info;
356
357		if (fact_ops->read)
358			mtd->_read_fact_prot_reg = spinand_mtd_fact_otp_read;
359	}
360
361	return 0;
362}
~/ Read-only file preview.