xref: /openbmc/linux/drivers/mtd/spi-nor/otp.c (revision 32046347)
1069089acSMichael Walle // SPDX-License-Identifier: GPL-2.0
2069089acSMichael Walle /*
3069089acSMichael Walle  * OTP support for SPI NOR flashes
4069089acSMichael Walle  *
5069089acSMichael Walle  * Copyright (C) 2021 Michael Walle <michael@walle.cc>
6069089acSMichael Walle  */
7069089acSMichael Walle 
8069089acSMichael Walle #include <linux/log2.h>
9069089acSMichael Walle #include <linux/mtd/mtd.h>
10069089acSMichael Walle #include <linux/mtd/spi-nor.h>
11069089acSMichael Walle 
12069089acSMichael Walle #include "core.h"
13069089acSMichael Walle 
14069089acSMichael Walle #define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
15069089acSMichael Walle #define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
16069089acSMichael Walle 
17cad3193fSMichael Walle /**
18d5b813e4SMichael Walle  * spi_nor_otp_read_secr() - read security register
19cad3193fSMichael Walle  * @nor:	pointer to 'struct spi_nor'
20a6e2cd4dSMichael Walle  * @addr:       offset to read from
21cad3193fSMichael Walle  * @len:        number of bytes to read
22cad3193fSMichael Walle  * @buf:        pointer to dst buffer
23cad3193fSMichael Walle  *
24d5b813e4SMichael Walle  * Read a security register by using the SPINOR_OP_RSECR commands.
25d5b813e4SMichael Walle  *
26d5b813e4SMichael Walle  * In Winbond/GigaDevice datasheets the term "security register" stands for
27d5b813e4SMichael Walle  * an one-time-programmable memory area, consisting of multiple bytes (usually
28d5b813e4SMichael Walle  * 256). Thus one "security register" maps to one OTP region.
29d5b813e4SMichael Walle  *
30d5b813e4SMichael Walle  * This method is used on GigaDevice and Winbond flashes.
31d5b813e4SMichael Walle  *
32d5b813e4SMichael Walle  * Please note, the read must not span multiple registers.
33cad3193fSMichael Walle  *
34cad3193fSMichael Walle  * Return: number of bytes read successfully, -errno otherwise
35cad3193fSMichael Walle  */
spi_nor_otp_read_secr(struct spi_nor * nor,loff_t addr,size_t len,u8 * buf)36cad3193fSMichael Walle int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
37cad3193fSMichael Walle {
38c452d498STudor Ambarus 	u8 addr_nbytes, read_opcode, read_dummy;
39cad3193fSMichael Walle 	struct spi_mem_dirmap_desc *rdesc;
40cad3193fSMichael Walle 	enum spi_nor_protocol read_proto;
41cad3193fSMichael Walle 	int ret;
42cad3193fSMichael Walle 
43cad3193fSMichael Walle 	read_opcode = nor->read_opcode;
44c452d498STudor Ambarus 	addr_nbytes = nor->addr_nbytes;
45cad3193fSMichael Walle 	read_dummy = nor->read_dummy;
46cad3193fSMichael Walle 	read_proto = nor->read_proto;
47cad3193fSMichael Walle 	rdesc = nor->dirmap.rdesc;
48cad3193fSMichael Walle 
49cad3193fSMichael Walle 	nor->read_opcode = SPINOR_OP_RSECR;
50cad3193fSMichael Walle 	nor->read_dummy = 8;
51cad3193fSMichael Walle 	nor->read_proto = SNOR_PROTO_1_1_1;
52cad3193fSMichael Walle 	nor->dirmap.rdesc = NULL;
53cad3193fSMichael Walle 
54cad3193fSMichael Walle 	ret = spi_nor_read_data(nor, addr, len, buf);
55cad3193fSMichael Walle 
56cad3193fSMichael Walle 	nor->read_opcode = read_opcode;
57c452d498STudor Ambarus 	nor->addr_nbytes = addr_nbytes;
58cad3193fSMichael Walle 	nor->read_dummy = read_dummy;
59cad3193fSMichael Walle 	nor->read_proto = read_proto;
60cad3193fSMichael Walle 	nor->dirmap.rdesc = rdesc;
61cad3193fSMichael Walle 
62cad3193fSMichael Walle 	return ret;
63cad3193fSMichael Walle }
64cad3193fSMichael Walle 
65cad3193fSMichael Walle /**
66d5b813e4SMichael Walle  * spi_nor_otp_write_secr() - write security register
67cad3193fSMichael Walle  * @nor:        pointer to 'struct spi_nor'
68a6e2cd4dSMichael Walle  * @addr:       offset to write to
69cad3193fSMichael Walle  * @len:        number of bytes to write
70cad3193fSMichael Walle  * @buf:        pointer to src buffer
71cad3193fSMichael Walle  *
72d5b813e4SMichael Walle  * Write a security register by using the SPINOR_OP_PSECR commands.
73cad3193fSMichael Walle  *
74d5b813e4SMichael Walle  * For more information on the term "security register", see the documentation
75d5b813e4SMichael Walle  * of spi_nor_otp_read_secr().
76d5b813e4SMichael Walle  *
77d5b813e4SMichael Walle  * This method is used on GigaDevice and Winbond flashes.
78d5b813e4SMichael Walle  *
79d5b813e4SMichael Walle  * Please note, the write must not span multiple registers.
80cad3193fSMichael Walle  *
81cad3193fSMichael Walle  * Return: number of bytes written successfully, -errno otherwise
82cad3193fSMichael Walle  */
spi_nor_otp_write_secr(struct spi_nor * nor,loff_t addr,size_t len,const u8 * buf)831df1fc8cSTudor Ambarus int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len,
841df1fc8cSTudor Ambarus 			   const u8 *buf)
85cad3193fSMichael Walle {
86cad3193fSMichael Walle 	enum spi_nor_protocol write_proto;
87cad3193fSMichael Walle 	struct spi_mem_dirmap_desc *wdesc;
88c452d498STudor Ambarus 	u8 addr_nbytes, program_opcode;
89cad3193fSMichael Walle 	int ret, written;
90cad3193fSMichael Walle 
91cad3193fSMichael Walle 	program_opcode = nor->program_opcode;
92c452d498STudor Ambarus 	addr_nbytes = nor->addr_nbytes;
93cad3193fSMichael Walle 	write_proto = nor->write_proto;
94cad3193fSMichael Walle 	wdesc = nor->dirmap.wdesc;
95cad3193fSMichael Walle 
96cad3193fSMichael Walle 	nor->program_opcode = SPINOR_OP_PSECR;
97cad3193fSMichael Walle 	nor->write_proto = SNOR_PROTO_1_1_1;
98cad3193fSMichael Walle 	nor->dirmap.wdesc = NULL;
99cad3193fSMichael Walle 
100cad3193fSMichael Walle 	/*
101cad3193fSMichael Walle 	 * We only support a write to one single page. For now all winbond
102d5b813e4SMichael Walle 	 * flashes only have one page per security register.
103cad3193fSMichael Walle 	 */
104cad3193fSMichael Walle 	ret = spi_nor_write_enable(nor);
105cad3193fSMichael Walle 	if (ret)
106cad3193fSMichael Walle 		goto out;
107cad3193fSMichael Walle 
108cad3193fSMichael Walle 	written = spi_nor_write_data(nor, addr, len, buf);
109cad3193fSMichael Walle 	if (written < 0)
110cad3193fSMichael Walle 		goto out;
111cad3193fSMichael Walle 
112cad3193fSMichael Walle 	ret = spi_nor_wait_till_ready(nor);
113cad3193fSMichael Walle 
114cad3193fSMichael Walle out:
115cad3193fSMichael Walle 	nor->program_opcode = program_opcode;
116c452d498STudor Ambarus 	nor->addr_nbytes = addr_nbytes;
117cad3193fSMichael Walle 	nor->write_proto = write_proto;
118cad3193fSMichael Walle 	nor->dirmap.wdesc = wdesc;
119cad3193fSMichael Walle 
120cad3193fSMichael Walle 	return ret ?: written;
121cad3193fSMichael Walle }
122cad3193fSMichael Walle 
123c6ec3e1eSMichael Walle /**
124c6ec3e1eSMichael Walle  * spi_nor_otp_erase_secr() - erase a security register
125c6ec3e1eSMichael Walle  * @nor:        pointer to 'struct spi_nor'
126c6ec3e1eSMichael Walle  * @addr:       offset of the security register to be erased
127c6ec3e1eSMichael Walle  *
128c6ec3e1eSMichael Walle  * Erase a security register by using the SPINOR_OP_ESECR command.
129c6ec3e1eSMichael Walle  *
130c6ec3e1eSMichael Walle  * For more information on the term "security register", see the documentation
131c6ec3e1eSMichael Walle  * of spi_nor_otp_read_secr().
132c6ec3e1eSMichael Walle  *
133c6ec3e1eSMichael Walle  * This method is used on GigaDevice and Winbond flashes.
134c6ec3e1eSMichael Walle  *
135c6ec3e1eSMichael Walle  * Return: 0 on success, -errno otherwise
136c6ec3e1eSMichael Walle  */
spi_nor_otp_erase_secr(struct spi_nor * nor,loff_t addr)137c6ec3e1eSMichael Walle int spi_nor_otp_erase_secr(struct spi_nor *nor, loff_t addr)
138c6ec3e1eSMichael Walle {
139c6ec3e1eSMichael Walle 	u8 erase_opcode = nor->erase_opcode;
140c6ec3e1eSMichael Walle 	int ret;
141c6ec3e1eSMichael Walle 
142c6ec3e1eSMichael Walle 	ret = spi_nor_write_enable(nor);
143c6ec3e1eSMichael Walle 	if (ret)
144c6ec3e1eSMichael Walle 		return ret;
145c6ec3e1eSMichael Walle 
146c6ec3e1eSMichael Walle 	nor->erase_opcode = SPINOR_OP_ESECR;
147c6ec3e1eSMichael Walle 	ret = spi_nor_erase_sector(nor, addr);
148c6ec3e1eSMichael Walle 	nor->erase_opcode = erase_opcode;
149c6ec3e1eSMichael Walle 	if (ret)
150c6ec3e1eSMichael Walle 		return ret;
151c6ec3e1eSMichael Walle 
152c6ec3e1eSMichael Walle 	return spi_nor_wait_till_ready(nor);
153c6ec3e1eSMichael Walle }
154c6ec3e1eSMichael Walle 
spi_nor_otp_lock_bit_cr(unsigned int region)155cad3193fSMichael Walle static int spi_nor_otp_lock_bit_cr(unsigned int region)
156cad3193fSMichael Walle {
157cad3193fSMichael Walle 	static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 };
158cad3193fSMichael Walle 
159cad3193fSMichael Walle 	if (region >= ARRAY_SIZE(lock_bits))
160cad3193fSMichael Walle 		return -EINVAL;
161cad3193fSMichael Walle 
162cad3193fSMichael Walle 	return lock_bits[region];
163cad3193fSMichael Walle }
164cad3193fSMichael Walle 
165cad3193fSMichael Walle /**
166cad3193fSMichael Walle  * spi_nor_otp_lock_sr2() - lock the OTP region
167cad3193fSMichael Walle  * @nor:        pointer to 'struct spi_nor'
168cad3193fSMichael Walle  * @region:     OTP region
169cad3193fSMichael Walle  *
170cad3193fSMichael Walle  * Lock the OTP region by writing the status register-2. This method is used on
171cad3193fSMichael Walle  * GigaDevice and Winbond flashes.
172cad3193fSMichael Walle  *
173cad3193fSMichael Walle  * Return: 0 on success, -errno otherwise.
174cad3193fSMichael Walle  */
spi_nor_otp_lock_sr2(struct spi_nor * nor,unsigned int region)175cad3193fSMichael Walle int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region)
176cad3193fSMichael Walle {
177cad3193fSMichael Walle 	u8 *cr = nor->bouncebuf;
178cad3193fSMichael Walle 	int ret, lock_bit;
179cad3193fSMichael Walle 
180cad3193fSMichael Walle 	lock_bit = spi_nor_otp_lock_bit_cr(region);
181cad3193fSMichael Walle 	if (lock_bit < 0)
182cad3193fSMichael Walle 		return lock_bit;
183cad3193fSMichael Walle 
184cad3193fSMichael Walle 	ret = spi_nor_read_cr(nor, cr);
185cad3193fSMichael Walle 	if (ret)
186cad3193fSMichael Walle 		return ret;
187cad3193fSMichael Walle 
188cad3193fSMichael Walle 	/* no need to write the register if region is already locked */
189cad3193fSMichael Walle 	if (cr[0] & lock_bit)
190cad3193fSMichael Walle 		return 0;
191cad3193fSMichael Walle 
192cad3193fSMichael Walle 	cr[0] |= lock_bit;
193cad3193fSMichael Walle 
194cad3193fSMichael Walle 	return spi_nor_write_16bit_cr_and_check(nor, cr[0]);
195cad3193fSMichael Walle }
196cad3193fSMichael Walle 
197cad3193fSMichael Walle /**
198cad3193fSMichael Walle  * spi_nor_otp_is_locked_sr2() - get the OTP region lock status
199cad3193fSMichael Walle  * @nor:        pointer to 'struct spi_nor'
200cad3193fSMichael Walle  * @region:     OTP region
201cad3193fSMichael Walle  *
202cad3193fSMichael Walle  * Retrieve the OTP region lock bit by reading the status register-2. This
203cad3193fSMichael Walle  * method is used on GigaDevice and Winbond flashes.
204cad3193fSMichael Walle  *
205cad3193fSMichael Walle  * Return: 0 on success, -errno otherwise.
206cad3193fSMichael Walle  */
spi_nor_otp_is_locked_sr2(struct spi_nor * nor,unsigned int region)207cad3193fSMichael Walle int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region)
208cad3193fSMichael Walle {
209cad3193fSMichael Walle 	u8 *cr = nor->bouncebuf;
210cad3193fSMichael Walle 	int ret, lock_bit;
211cad3193fSMichael Walle 
212cad3193fSMichael Walle 	lock_bit = spi_nor_otp_lock_bit_cr(region);
213cad3193fSMichael Walle 	if (lock_bit < 0)
214cad3193fSMichael Walle 		return lock_bit;
215cad3193fSMichael Walle 
216cad3193fSMichael Walle 	ret = spi_nor_read_cr(nor, cr);
217cad3193fSMichael Walle 	if (ret)
218cad3193fSMichael Walle 		return ret;
219cad3193fSMichael Walle 
220cad3193fSMichael Walle 	return cr[0] & lock_bit;
221cad3193fSMichael Walle }
222cad3193fSMichael Walle 
spi_nor_otp_region_start(const struct spi_nor * nor,unsigned int region)223069089acSMichael Walle static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region)
224069089acSMichael Walle {
225069089acSMichael Walle 	const struct spi_nor_otp_organization *org = nor->params->otp.org;
226069089acSMichael Walle 
227069089acSMichael Walle 	return org->base + region * org->offset;
228069089acSMichael Walle }
229069089acSMichael Walle 
spi_nor_otp_size(struct spi_nor * nor)230069089acSMichael Walle static size_t spi_nor_otp_size(struct spi_nor *nor)
231069089acSMichael Walle {
232069089acSMichael Walle 	return spi_nor_otp_n_regions(nor) * spi_nor_otp_region_len(nor);
233069089acSMichael Walle }
234069089acSMichael Walle 
235069089acSMichael Walle /* Translate the file offsets from and to OTP regions. */
spi_nor_otp_region_to_offset(struct spi_nor * nor,unsigned int region)236069089acSMichael Walle static loff_t spi_nor_otp_region_to_offset(struct spi_nor *nor, unsigned int region)
237069089acSMichael Walle {
238069089acSMichael Walle 	return region * spi_nor_otp_region_len(nor);
239069089acSMichael Walle }
240069089acSMichael Walle 
spi_nor_otp_offset_to_region(struct spi_nor * nor,loff_t ofs)241069089acSMichael Walle static unsigned int spi_nor_otp_offset_to_region(struct spi_nor *nor, loff_t ofs)
242069089acSMichael Walle {
243069089acSMichael Walle 	return div64_u64(ofs, spi_nor_otp_region_len(nor));
244069089acSMichael Walle }
245069089acSMichael Walle 
spi_nor_mtd_otp_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)246069089acSMichael Walle static int spi_nor_mtd_otp_info(struct mtd_info *mtd, size_t len,
247069089acSMichael Walle 				size_t *retlen, struct otp_info *buf)
248069089acSMichael Walle {
249069089acSMichael Walle 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
250069089acSMichael Walle 	const struct spi_nor_otp_ops *ops = nor->params->otp.ops;
251069089acSMichael Walle 	unsigned int n_regions = spi_nor_otp_n_regions(nor);
252069089acSMichael Walle 	unsigned int i;
253069089acSMichael Walle 	int ret, locked;
254069089acSMichael Walle 
255069089acSMichael Walle 	if (len < n_regions * sizeof(*buf))
256069089acSMichael Walle 		return -ENOSPC;
257069089acSMichael Walle 
258*32046347SMiquel Raynal 	ret = spi_nor_prep_and_lock(nor);
259069089acSMichael Walle 	if (ret)
260069089acSMichael Walle 		return ret;
261069089acSMichael Walle 
262069089acSMichael Walle 	for (i = 0; i < n_regions; i++) {
263069089acSMichael Walle 		buf->start = spi_nor_otp_region_to_offset(nor, i);
264069089acSMichael Walle 		buf->length = spi_nor_otp_region_len(nor);
265069089acSMichael Walle 
266069089acSMichael Walle 		locked = ops->is_locked(nor, i);
267069089acSMichael Walle 		if (locked < 0) {
268069089acSMichael Walle 			ret = locked;
269069089acSMichael Walle 			goto out;
270069089acSMichael Walle 		}
271069089acSMichael Walle 
272069089acSMichael Walle 		buf->locked = !!locked;
273069089acSMichael Walle 		buf++;
274069089acSMichael Walle 	}
275069089acSMichael Walle 
276069089acSMichael Walle 	*retlen = n_regions * sizeof(*buf);
277069089acSMichael Walle 
278069089acSMichael Walle out:
279069089acSMichael Walle 	spi_nor_unlock_and_unprep(nor);
280069089acSMichael Walle 
281069089acSMichael Walle 	return ret;
282069089acSMichael Walle }
283069089acSMichael Walle 
spi_nor_mtd_otp_range_is_locked(struct spi_nor * nor,loff_t ofs,size_t len)284388161caSMichael Walle static int spi_nor_mtd_otp_range_is_locked(struct spi_nor *nor, loff_t ofs,
285388161caSMichael Walle 					   size_t len)
286388161caSMichael Walle {
287388161caSMichael Walle 	const struct spi_nor_otp_ops *ops = nor->params->otp.ops;
288388161caSMichael Walle 	unsigned int region;
289388161caSMichael Walle 	int locked;
290388161caSMichael Walle 
291388161caSMichael Walle 	/*
292388161caSMichael Walle 	 * If any of the affected OTP regions are locked the entire range is
293388161caSMichael Walle 	 * considered locked.
294388161caSMichael Walle 	 */
295388161caSMichael Walle 	for (region = spi_nor_otp_offset_to_region(nor, ofs);
296388161caSMichael Walle 	     region <= spi_nor_otp_offset_to_region(nor, ofs + len - 1);
297388161caSMichael Walle 	     region++) {
298388161caSMichael Walle 		locked = ops->is_locked(nor, region);
299388161caSMichael Walle 		/* take the branch it is locked or in case of an error */
300388161caSMichael Walle 		if (locked)
301388161caSMichael Walle 			return locked;
302388161caSMichael Walle 	}
303388161caSMichael Walle 
304388161caSMichael Walle 	return 0;
305388161caSMichael Walle }
306388161caSMichael Walle 
spi_nor_mtd_otp_read_write(struct mtd_info * mtd,loff_t ofs,size_t total_len,size_t * retlen,const u8 * buf,bool is_write)307069089acSMichael Walle static int spi_nor_mtd_otp_read_write(struct mtd_info *mtd, loff_t ofs,
308069089acSMichael Walle 				      size_t total_len, size_t *retlen,
3091df1fc8cSTudor Ambarus 				      const u8 *buf, bool is_write)
310069089acSMichael Walle {
311069089acSMichael Walle 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
312069089acSMichael Walle 	const struct spi_nor_otp_ops *ops = nor->params->otp.ops;
313069089acSMichael Walle 	const size_t rlen = spi_nor_otp_region_len(nor);
314069089acSMichael Walle 	loff_t rstart, rofs;
315069089acSMichael Walle 	unsigned int region;
316069089acSMichael Walle 	size_t len;
317069089acSMichael Walle 	int ret;
318069089acSMichael Walle 
319069089acSMichael Walle 	if (ofs < 0 || ofs >= spi_nor_otp_size(nor))
320069089acSMichael Walle 		return 0;
321069089acSMichael Walle 
322388161caSMichael Walle 	/* don't access beyond the end */
323388161caSMichael Walle 	total_len = min_t(size_t, total_len, spi_nor_otp_size(nor) - ofs);
324388161caSMichael Walle 
325388161caSMichael Walle 	if (!total_len)
326388161caSMichael Walle 		return 0;
327388161caSMichael Walle 
328*32046347SMiquel Raynal 	ret = spi_nor_prep_and_lock(nor);
329069089acSMichael Walle 	if (ret)
330069089acSMichael Walle 		return ret;
331069089acSMichael Walle 
332388161caSMichael Walle 	if (is_write) {
333388161caSMichael Walle 		ret = spi_nor_mtd_otp_range_is_locked(nor, ofs, total_len);
334388161caSMichael Walle 		if (ret < 0) {
335388161caSMichael Walle 			goto out;
336388161caSMichael Walle 		} else if (ret) {
337388161caSMichael Walle 			ret = -EROFS;
338388161caSMichael Walle 			goto out;
339388161caSMichael Walle 		}
340388161caSMichael Walle 	}
341069089acSMichael Walle 
342069089acSMichael Walle 	while (total_len) {
343069089acSMichael Walle 		/*
344069089acSMichael Walle 		 * The OTP regions are mapped into a contiguous area starting
345069089acSMichael Walle 		 * at 0 as expected by the MTD layer. This will map the MTD
346069089acSMichael Walle 		 * file offsets to the address of an OTP region as used in the
347069089acSMichael Walle 		 * actual SPI commands.
348069089acSMichael Walle 		 */
349069089acSMichael Walle 		region = spi_nor_otp_offset_to_region(nor, ofs);
350069089acSMichael Walle 		rstart = spi_nor_otp_region_start(nor, region);
351069089acSMichael Walle 
352069089acSMichael Walle 		/*
353069089acSMichael Walle 		 * The size of a OTP region is expected to be a power of two,
354069089acSMichael Walle 		 * thus we can just mask the lower bits and get the offset into
355069089acSMichael Walle 		 * a region.
356069089acSMichael Walle 		 */
357069089acSMichael Walle 		rofs = ofs & (rlen - 1);
358069089acSMichael Walle 
359069089acSMichael Walle 		/* don't access beyond one OTP region */
360069089acSMichael Walle 		len = min_t(size_t, total_len, rlen - rofs);
361069089acSMichael Walle 
362069089acSMichael Walle 		if (is_write)
363069089acSMichael Walle 			ret = ops->write(nor, rstart + rofs, len, buf);
364069089acSMichael Walle 		else
3651df1fc8cSTudor Ambarus 			ret = ops->read(nor, rstart + rofs, len, (u8 *)buf);
366069089acSMichael Walle 		if (ret == 0)
367069089acSMichael Walle 			ret = -EIO;
368069089acSMichael Walle 		if (ret < 0)
369069089acSMichael Walle 			goto out;
370069089acSMichael Walle 
371069089acSMichael Walle 		*retlen += ret;
372069089acSMichael Walle 		ofs += ret;
373069089acSMichael Walle 		buf += ret;
374069089acSMichael Walle 		total_len -= ret;
375069089acSMichael Walle 	}
376069089acSMichael Walle 	ret = 0;
377069089acSMichael Walle 
378069089acSMichael Walle out:
379069089acSMichael Walle 	spi_nor_unlock_and_unprep(nor);
380069089acSMichael Walle 	return ret;
381069089acSMichael Walle }
382069089acSMichael Walle 
spi_nor_mtd_otp_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u8 * buf)383069089acSMichael Walle static int spi_nor_mtd_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
384069089acSMichael Walle 				size_t *retlen, u8 *buf)
385069089acSMichael Walle {
386069089acSMichael Walle 	return spi_nor_mtd_otp_read_write(mtd, from, len, retlen, buf, false);
387069089acSMichael Walle }
388069089acSMichael Walle 
spi_nor_mtd_otp_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u8 * buf)389069089acSMichael Walle static int spi_nor_mtd_otp_write(struct mtd_info *mtd, loff_t to, size_t len,
3901df1fc8cSTudor Ambarus 				 size_t *retlen, const u8 *buf)
391069089acSMichael Walle {
392069089acSMichael Walle 	return spi_nor_mtd_otp_read_write(mtd, to, len, retlen, buf, true);
393069089acSMichael Walle }
394069089acSMichael Walle 
spi_nor_mtd_otp_erase(struct mtd_info * mtd,loff_t from,size_t len)395c6ec3e1eSMichael Walle static int spi_nor_mtd_otp_erase(struct mtd_info *mtd, loff_t from, size_t len)
396c6ec3e1eSMichael Walle {
397c6ec3e1eSMichael Walle 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
398c6ec3e1eSMichael Walle 	const struct spi_nor_otp_ops *ops = nor->params->otp.ops;
399c6ec3e1eSMichael Walle 	const size_t rlen = spi_nor_otp_region_len(nor);
400c6ec3e1eSMichael Walle 	unsigned int region;
401c6ec3e1eSMichael Walle 	loff_t rstart;
402c6ec3e1eSMichael Walle 	int ret;
403c6ec3e1eSMichael Walle 
404c6ec3e1eSMichael Walle 	/* OTP erase is optional */
405c6ec3e1eSMichael Walle 	if (!ops->erase)
406c6ec3e1eSMichael Walle 		return -EOPNOTSUPP;
407c6ec3e1eSMichael Walle 
408c6ec3e1eSMichael Walle 	if (!len)
409c6ec3e1eSMichael Walle 		return 0;
410c6ec3e1eSMichael Walle 
411c6ec3e1eSMichael Walle 	if (from < 0 || (from + len) > spi_nor_otp_size(nor))
412c6ec3e1eSMichael Walle 		return -EINVAL;
413c6ec3e1eSMichael Walle 
414c6ec3e1eSMichael Walle 	/* the user has to explicitly ask for whole regions */
415c6ec3e1eSMichael Walle 	if (!IS_ALIGNED(len, rlen) || !IS_ALIGNED(from, rlen))
416c6ec3e1eSMichael Walle 		return -EINVAL;
417c6ec3e1eSMichael Walle 
418*32046347SMiquel Raynal 	ret = spi_nor_prep_and_lock(nor);
419c6ec3e1eSMichael Walle 	if (ret)
420c6ec3e1eSMichael Walle 		return ret;
421c6ec3e1eSMichael Walle 
422c6ec3e1eSMichael Walle 	ret = spi_nor_mtd_otp_range_is_locked(nor, from, len);
423c6ec3e1eSMichael Walle 	if (ret < 0) {
424c6ec3e1eSMichael Walle 		goto out;
425c6ec3e1eSMichael Walle 	} else if (ret) {
426c6ec3e1eSMichael Walle 		ret = -EROFS;
427c6ec3e1eSMichael Walle 		goto out;
428c6ec3e1eSMichael Walle 	}
429c6ec3e1eSMichael Walle 
430c6ec3e1eSMichael Walle 	while (len) {
431c6ec3e1eSMichael Walle 		region = spi_nor_otp_offset_to_region(nor, from);
432c6ec3e1eSMichael Walle 		rstart = spi_nor_otp_region_start(nor, region);
433c6ec3e1eSMichael Walle 
434c6ec3e1eSMichael Walle 		ret = ops->erase(nor, rstart);
435c6ec3e1eSMichael Walle 		if (ret)
436c6ec3e1eSMichael Walle 			goto out;
437c6ec3e1eSMichael Walle 
438c6ec3e1eSMichael Walle 		len -= rlen;
439c6ec3e1eSMichael Walle 		from += rlen;
440c6ec3e1eSMichael Walle 	}
441c6ec3e1eSMichael Walle 
442c6ec3e1eSMichael Walle out:
443c6ec3e1eSMichael Walle 	spi_nor_unlock_and_unprep(nor);
444c6ec3e1eSMichael Walle 
445c6ec3e1eSMichael Walle 	return ret;
446c6ec3e1eSMichael Walle }
447c6ec3e1eSMichael Walle 
spi_nor_mtd_otp_lock(struct mtd_info * mtd,loff_t from,size_t len)448069089acSMichael Walle static int spi_nor_mtd_otp_lock(struct mtd_info *mtd, loff_t from, size_t len)
449069089acSMichael Walle {
450069089acSMichael Walle 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
451069089acSMichael Walle 	const struct spi_nor_otp_ops *ops = nor->params->otp.ops;
452069089acSMichael Walle 	const size_t rlen = spi_nor_otp_region_len(nor);
453069089acSMichael Walle 	unsigned int region;
454069089acSMichael Walle 	int ret;
455069089acSMichael Walle 
456069089acSMichael Walle 	if (from < 0 || (from + len) > spi_nor_otp_size(nor))
457069089acSMichael Walle 		return -EINVAL;
458069089acSMichael Walle 
459069089acSMichael Walle 	/* the user has to explicitly ask for whole regions */
460069089acSMichael Walle 	if (!IS_ALIGNED(len, rlen) || !IS_ALIGNED(from, rlen))
461069089acSMichael Walle 		return -EINVAL;
462069089acSMichael Walle 
463*32046347SMiquel Raynal 	ret = spi_nor_prep_and_lock(nor);
464069089acSMichael Walle 	if (ret)
465069089acSMichael Walle 		return ret;
466069089acSMichael Walle 
467069089acSMichael Walle 	while (len) {
468069089acSMichael Walle 		region = spi_nor_otp_offset_to_region(nor, from);
469069089acSMichael Walle 		ret = ops->lock(nor, region);
470069089acSMichael Walle 		if (ret)
471069089acSMichael Walle 			goto out;
472069089acSMichael Walle 
473069089acSMichael Walle 		len -= rlen;
474069089acSMichael Walle 		from += rlen;
475069089acSMichael Walle 	}
476069089acSMichael Walle 
477069089acSMichael Walle out:
478069089acSMichael Walle 	spi_nor_unlock_and_unprep(nor);
479069089acSMichael Walle 
480069089acSMichael Walle 	return ret;
481069089acSMichael Walle }
482069089acSMichael Walle 
spi_nor_set_mtd_otp_ops(struct spi_nor * nor)483ff67592cSTudor Ambarus void spi_nor_set_mtd_otp_ops(struct spi_nor *nor)
484069089acSMichael Walle {
485069089acSMichael Walle 	struct mtd_info *mtd = &nor->mtd;
486069089acSMichael Walle 
487069089acSMichael Walle 	if (!nor->params->otp.ops)
488069089acSMichael Walle 		return;
489069089acSMichael Walle 
490069089acSMichael Walle 	if (WARN_ON(!is_power_of_2(spi_nor_otp_region_len(nor))))
491069089acSMichael Walle 		return;
492069089acSMichael Walle 
493069089acSMichael Walle 	/*
494069089acSMichael Walle 	 * We only support user_prot callbacks (yet).
495069089acSMichael Walle 	 *
496069089acSMichael Walle 	 * Some SPI NOR flashes like Macronix ones can be ordered in two
497069089acSMichael Walle 	 * different variants. One with a factory locked OTP area and one where
498069089acSMichael Walle 	 * it is left to the user to write to it. The factory locked OTP is
499069089acSMichael Walle 	 * usually preprogrammed with an "electrical serial number". We don't
500069089acSMichael Walle 	 * support these for now.
501069089acSMichael Walle 	 */
502069089acSMichael Walle 	mtd->_get_user_prot_info = spi_nor_mtd_otp_info;
503069089acSMichael Walle 	mtd->_read_user_prot_reg = spi_nor_mtd_otp_read;
504069089acSMichael Walle 	mtd->_write_user_prot_reg = spi_nor_mtd_otp_write;
505069089acSMichael Walle 	mtd->_lock_user_prot_reg = spi_nor_mtd_otp_lock;
506c6ec3e1eSMichael Walle 	mtd->_erase_user_prot_reg = spi_nor_mtd_otp_erase;
507069089acSMichael Walle }
508