FILE
otp.c
File preview with syntax highlighting.
linux-6.19.14/drivers/mtd/nand/spi/otp.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.