mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 04:36:13 +02:00
mtd: spinand: add OTP support
The code was ported from linux-6.15 based on a linux commit c06b1f753bea (mtd: spinand: add OTP support) created by Martin Kurbanov <mmkurbanov@salutedevices.com> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de> Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
This commit is contained in:
parent
49b855662f
commit
37f0fc45f5
@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o
|
||||
spinand-objs := core.o otp.o
|
||||
spinand-objs += alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o
|
||||
spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o
|
||||
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
|
||||
|
||||
@ -56,7 +56,7 @@ static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
|
||||
int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
|
||||
{
|
||||
struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg,
|
||||
spinand->scratchbuf);
|
||||
@ -543,10 +543,10 @@ static int spinand_erase_op(struct spinand_device *spinand,
|
||||
*
|
||||
* Return: 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
static int spinand_wait(struct spinand_device *spinand,
|
||||
unsigned long initial_delay_us,
|
||||
unsigned long poll_delay_us,
|
||||
u8 *s)
|
||||
int spinand_wait(struct spinand_device *spinand,
|
||||
unsigned long initial_delay_us,
|
||||
unsigned long poll_delay_us,
|
||||
u8 *s)
|
||||
{
|
||||
unsigned long start, stop;
|
||||
u8 status;
|
||||
@ -617,8 +617,16 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
|
||||
return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
|
||||
}
|
||||
|
||||
static int spinand_read_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
/**
|
||||
* spinand_read_page() - Read a page
|
||||
* @spinand: the spinand device
|
||||
* @req: the I/O request
|
||||
*
|
||||
* Return: 0 or a positive number of bitflips corrected on success.
|
||||
* A negative error code otherwise.
|
||||
*/
|
||||
int spinand_read_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 status;
|
||||
@ -648,8 +656,16 @@ static int spinand_read_page(struct spinand_device *spinand,
|
||||
return spinand_ondie_ecc_finish_io_req(nand, (struct nand_page_io_req *)req);
|
||||
}
|
||||
|
||||
static int spinand_write_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
/**
|
||||
* spinand_write_page() - Write a page
|
||||
* @spinand: the spinand device
|
||||
* @req: the I/O request
|
||||
*
|
||||
* Return: 0 or a positive number of bitflips corrected on success.
|
||||
* A negative error code otherwise.
|
||||
*/
|
||||
int spinand_write_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
u8 status;
|
||||
@ -1338,6 +1354,8 @@ int spinand_match_and_init(struct spinand_device *spinand,
|
||||
spinand->id.len = 1 + table[i].devid.len;
|
||||
spinand->select_target = table[i].select_target;
|
||||
spinand->set_cont_read = table[i].set_cont_read;
|
||||
spinand->fact_otp = &table[i].fact_otp;
|
||||
spinand->user_otp = &table[i].user_otp;
|
||||
spinand->read_retries = table[i].read_retries;
|
||||
spinand->set_read_retry = table[i].set_read_retry;
|
||||
|
||||
@ -1504,6 +1522,12 @@ static int spinand_init(struct spinand_device *spinand)
|
||||
mtd->_block_isreserved = spinand_mtd_block_isreserved;
|
||||
mtd->_erase = spinand_mtd_erase;
|
||||
|
||||
if (spinand_user_otp_size(spinand) || spinand_fact_otp_size(spinand)) {
|
||||
ret = spinand_set_mtd_otp_ops(spinand);
|
||||
if (ret)
|
||||
goto err_cleanup_ecc_engine;
|
||||
}
|
||||
|
||||
ret = mtd_ooblayout_count_freebytes(mtd);
|
||||
if (ret < 0)
|
||||
goto err_cleanup_ecc_engine;
|
||||
|
||||
369
drivers/mtd/nand/spi/otp.c
Normal file
369
drivers/mtd/nand/spi/otp.c
Normal file
@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2025, SaluteDevices. All Rights Reserved.
|
||||
*
|
||||
* Author: Martin Kurbanov <mmkurbanov@salutedevices.com>
|
||||
*/
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
#ifdef __UBOOT__
|
||||
#include <spi.h>
|
||||
#include <dm/device_compat.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* spinand_otp_page_size() - Get SPI-NAND OTP page size
|
||||
* @spinand: the spinand device
|
||||
*
|
||||
* Return: the OTP page size.
|
||||
*/
|
||||
size_t spinand_otp_page_size(struct spinand_device *spinand)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
|
||||
return nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
|
||||
}
|
||||
|
||||
static size_t spinand_otp_size(struct spinand_device *spinand,
|
||||
const struct spinand_otp_layout *layout)
|
||||
{
|
||||
return layout->npages * spinand_otp_page_size(spinand);
|
||||
}
|
||||
|
||||
/**
|
||||
* spinand_fact_otp_size() - Get SPI-NAND factory OTP area size
|
||||
* @spinand: the spinand device
|
||||
*
|
||||
* Return: the OTP size.
|
||||
*/
|
||||
size_t spinand_fact_otp_size(struct spinand_device *spinand)
|
||||
{
|
||||
return spinand_otp_size(spinand, &spinand->fact_otp->layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* spinand_user_otp_size() - Get SPI-NAND user OTP area size
|
||||
* @spinand: the spinand device
|
||||
*
|
||||
* Return: the OTP size.
|
||||
*/
|
||||
size_t spinand_user_otp_size(struct spinand_device *spinand)
|
||||
{
|
||||
return spinand_otp_size(spinand, &spinand->user_otp->layout);
|
||||
}
|
||||
|
||||
static int spinand_otp_check_bounds(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len,
|
||||
const struct spinand_otp_layout *layout)
|
||||
{
|
||||
if (ofs < 0 || ofs + len > spinand_otp_size(spinand, layout))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spinand_user_otp_check_bounds(struct spinand_device *spinand,
|
||||
loff_t ofs, size_t len)
|
||||
{
|
||||
return spinand_otp_check_bounds(spinand, ofs, len,
|
||||
&spinand->user_otp->layout);
|
||||
}
|
||||
|
||||
static int spinand_otp_rw(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf, bool is_write,
|
||||
const struct spinand_otp_layout *layout)
|
||||
{
|
||||
struct nand_page_io_req req = {};
|
||||
unsigned long long page;
|
||||
size_t copied = 0;
|
||||
size_t otp_pagesize = spinand_otp_page_size(spinand);
|
||||
int ret;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
ret = spinand_otp_check_bounds(spinand, ofs, len, layout);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, CFG_OTP_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
page = ofs;
|
||||
req.dataoffs = do_div(page, otp_pagesize);
|
||||
req.pos.page = page + layout->start_page;
|
||||
req.type = is_write ? NAND_PAGE_WRITE : NAND_PAGE_READ;
|
||||
req.mode = MTD_OPS_RAW;
|
||||
req.databuf.in = buf;
|
||||
|
||||
while (copied < len) {
|
||||
req.datalen = min_t(unsigned int,
|
||||
otp_pagesize - req.dataoffs,
|
||||
len - copied);
|
||||
|
||||
if (is_write)
|
||||
ret = spinand_write_page(spinand, &req);
|
||||
else
|
||||
ret = spinand_read_page(spinand, &req);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
req.databuf.in += req.datalen;
|
||||
req.pos.page++;
|
||||
req.dataoffs = 0;
|
||||
copied += req.datalen;
|
||||
}
|
||||
|
||||
*retlen = copied;
|
||||
|
||||
if (spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0)) {
|
||||
dev_warn(spinand->slave->dev,
|
||||
"Can not disable OTP mode\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* spinand_fact_otp_read() - Read from OTP area
|
||||
* @spinand: the spinand device
|
||||
* @ofs: the offset to read
|
||||
* @len: the number of data bytes to read
|
||||
* @retlen: the pointer to variable to store the number of read bytes
|
||||
* @buf: the buffer to store the read data
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
int spinand_fact_otp_read(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf)
|
||||
{
|
||||
return spinand_otp_rw(spinand, ofs, len, retlen, buf, false,
|
||||
&spinand->fact_otp->layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* spinand_user_otp_read() - Read from OTP area
|
||||
* @spinand: the spinand device
|
||||
* @ofs: the offset to read
|
||||
* @len: the number of data bytes to read
|
||||
* @retlen: the pointer to variable to store the number of read bytes
|
||||
* @buf: the buffer to store the read data
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
int spinand_user_otp_read(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf)
|
||||
{
|
||||
return spinand_otp_rw(spinand, ofs, len, retlen, buf, false,
|
||||
&spinand->user_otp->layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* spinand_user_otp_write() - Write to OTP area
|
||||
* @spinand: the spinand device
|
||||
* @ofs: the offset to write to
|
||||
* @len: the number of bytes to write
|
||||
* @retlen: the pointer to variable to store the number of written bytes
|
||||
* @buf: the buffer with data to write
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
int spinand_user_otp_write(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, const u8 *buf)
|
||||
{
|
||||
return spinand_otp_rw(spinand, ofs, len, retlen, (u8 *)buf, true,
|
||||
&spinand->user_otp->layout);
|
||||
}
|
||||
|
||||
static int spinand_mtd_otp_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf,
|
||||
bool is_fact)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
int ret;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
|
||||
if (is_fact)
|
||||
ret = spinand->fact_otp->ops->info(spinand, len, buf, retlen);
|
||||
else
|
||||
ret = spinand->user_otp->ops->info(spinand, len, buf, retlen);
|
||||
|
||||
mutex_unlock(&spinand->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spinand_mtd_fact_otp_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
return spinand_mtd_otp_info(mtd, len, retlen, buf, true);
|
||||
}
|
||||
|
||||
static int spinand_mtd_user_otp_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
return spinand_mtd_otp_info(mtd, len, retlen, buf, false);
|
||||
}
|
||||
|
||||
static int spinand_mtd_otp_read(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, u8 *buf, bool is_fact)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
int ret;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
ret = spinand_otp_check_bounds(spinand, ofs, len,
|
||||
is_fact ? &spinand->fact_otp->layout :
|
||||
&spinand->user_otp->layout);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
|
||||
if (is_fact)
|
||||
ret = spinand->fact_otp->ops->read(spinand, ofs, len, retlen,
|
||||
buf);
|
||||
else
|
||||
ret = spinand->user_otp->ops->read(spinand, ofs, len, retlen,
|
||||
buf);
|
||||
|
||||
mutex_unlock(&spinand->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spinand_mtd_fact_otp_read(struct mtd_info *mtd, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf)
|
||||
{
|
||||
return spinand_mtd_otp_read(mtd, ofs, len, retlen, buf, true);
|
||||
}
|
||||
|
||||
static int spinand_mtd_user_otp_read(struct mtd_info *mtd, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf)
|
||||
{
|
||||
return spinand_mtd_otp_read(mtd, ofs, len, retlen, buf, false);
|
||||
}
|
||||
|
||||
static int spinand_mtd_user_otp_write(struct mtd_info *mtd, loff_t ofs,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
const struct spinand_user_otp_ops *ops = spinand->user_otp->ops;
|
||||
int ret;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
ret = spinand_user_otp_check_bounds(spinand, ofs, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
ret = ops->write(spinand, ofs, len, retlen, buf);
|
||||
mutex_unlock(&spinand->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
static int spinand_mtd_user_otp_erase(struct mtd_info *mtd, loff_t ofs,
|
||||
size_t len)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
const struct spinand_user_otp_ops *ops = spinand->user_otp->ops;
|
||||
int ret;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
ret = spinand_user_otp_check_bounds(spinand, ofs, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
ret = ops->erase(spinand, ofs, len);
|
||||
mutex_unlock(&spinand->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int spinand_mtd_user_otp_lock(struct mtd_info *mtd, loff_t ofs,
|
||||
size_t len)
|
||||
{
|
||||
struct spinand_device *spinand = mtd_to_spinand(mtd);
|
||||
const struct spinand_user_otp_ops *ops = spinand->user_otp->ops;
|
||||
int ret;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
ret = spinand_user_otp_check_bounds(spinand, ofs, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&spinand->lock);
|
||||
ret = ops->lock(spinand, ofs, len);
|
||||
mutex_unlock(&spinand->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* spinand_set_mtd_otp_ops() - Setup OTP methods
|
||||
* @spinand: the spinand device
|
||||
*
|
||||
* Setup OTP methods.
|
||||
*
|
||||
* Return: 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int spinand_set_mtd_otp_ops(struct spinand_device *spinand)
|
||||
{
|
||||
struct mtd_info *mtd = spinand_to_mtd(spinand);
|
||||
const struct spinand_fact_otp_ops *fact_ops = spinand->fact_otp->ops;
|
||||
const struct spinand_user_otp_ops *user_ops = spinand->user_otp->ops;
|
||||
|
||||
if (!user_ops && !fact_ops)
|
||||
return -EINVAL;
|
||||
|
||||
if (user_ops) {
|
||||
if (user_ops->info)
|
||||
mtd->_get_user_prot_info = spinand_mtd_user_otp_info;
|
||||
|
||||
if (user_ops->read)
|
||||
mtd->_read_user_prot_reg = spinand_mtd_user_otp_read;
|
||||
|
||||
if (user_ops->write)
|
||||
mtd->_write_user_prot_reg = spinand_mtd_user_otp_write;
|
||||
|
||||
if (user_ops->lock)
|
||||
mtd->_lock_user_prot_reg = spinand_mtd_user_otp_lock;
|
||||
#ifndef __UBOOT__
|
||||
if (user_ops->erase)
|
||||
mtd->_erase_user_prot_reg = spinand_mtd_user_otp_erase;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (fact_ops) {
|
||||
if (fact_ops->info)
|
||||
mtd->_get_fact_prot_info = spinand_mtd_fact_otp_info;
|
||||
|
||||
if (fact_ops->read)
|
||||
mtd->_read_fact_prot_reg = spinand_mtd_fact_otp_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -322,6 +322,68 @@ struct spinand_ecc_info {
|
||||
#define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3)
|
||||
#define SPINAND_NO_RAW_ACCESS BIT(4)
|
||||
|
||||
/**
|
||||
* struct spinand_otp_layout - structure to describe the SPI NAND OTP area
|
||||
* @npages: number of pages in the OTP
|
||||
* @start_page: start page of the user/factory OTP area.
|
||||
*/
|
||||
struct spinand_otp_layout {
|
||||
unsigned int npages;
|
||||
unsigned int start_page;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spinand_fact_otp_ops - SPI NAND OTP methods for factory area
|
||||
* @info: get the OTP area information
|
||||
* @read: read from the SPI NAND OTP area
|
||||
*/
|
||||
struct spinand_fact_otp_ops {
|
||||
int (*info)(struct spinand_device *spinand, size_t len,
|
||||
struct otp_info *buf, size_t *retlen);
|
||||
int (*read)(struct spinand_device *spinand, loff_t from, size_t len,
|
||||
size_t *retlen, u8 *buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spinand_user_otp_ops - SPI NAND OTP methods for user area
|
||||
* @info: get the OTP area information
|
||||
* @lock: lock an OTP region
|
||||
* @erase: erase an OTP region
|
||||
* @read: read from the SPI NAND OTP area
|
||||
* @write: write to the SPI NAND OTP area
|
||||
*/
|
||||
struct spinand_user_otp_ops {
|
||||
int (*info)(struct spinand_device *spinand, size_t len,
|
||||
struct otp_info *buf, size_t *retlen);
|
||||
int (*lock)(struct spinand_device *spinand, loff_t from, size_t len);
|
||||
int (*erase)(struct spinand_device *spinand, loff_t from, size_t len);
|
||||
int (*read)(struct spinand_device *spinand, loff_t from, size_t len,
|
||||
size_t *retlen, u8 *buf);
|
||||
int (*write)(struct spinand_device *spinand, loff_t from, size_t len,
|
||||
size_t *retlen, const u8 *buf);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spinand_fact_otp - SPI NAND OTP grouping structure for factory area
|
||||
* @layout: OTP region layout
|
||||
* @ops: OTP access ops
|
||||
*/
|
||||
struct spinand_fact_otp {
|
||||
const struct spinand_otp_layout layout;
|
||||
const struct spinand_fact_otp_ops *ops;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spinand_user_otp - SPI NAND OTP grouping structure for user area
|
||||
* @layout: OTP region layout
|
||||
* @ops: OTP access ops
|
||||
*/
|
||||
struct spinand_user_otp {
|
||||
const struct spinand_otp_layout layout;
|
||||
const struct spinand_user_otp_ops *ops;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* struct spinand_info - Structure used to describe SPI NAND chips
|
||||
* @model: model name
|
||||
@ -337,6 +399,8 @@ struct spinand_ecc_info {
|
||||
* @select_target: function used to select a target/die. Required only for
|
||||
* multi-die chips
|
||||
* @set_cont_read: enable/disable continuous cached reads
|
||||
* @fact_otp: SPI NAND factory OTP info.
|
||||
* @user_otp: SPI NAND user OTP info.
|
||||
* @read_retries: the number of read retry modes supported
|
||||
* @set_read_retry: enable/disable read retry for data recovery
|
||||
*
|
||||
@ -359,6 +423,8 @@ struct spinand_info {
|
||||
unsigned int target);
|
||||
int (*set_cont_read)(struct spinand_device *spinand,
|
||||
bool enable);
|
||||
struct spinand_fact_otp fact_otp;
|
||||
struct spinand_user_otp user_otp;
|
||||
unsigned int read_retries;
|
||||
int (*set_read_retry)(struct spinand_device *spinand,
|
||||
unsigned int read_retry);
|
||||
@ -390,6 +456,24 @@ struct spinand_info {
|
||||
#define SPINAND_CONT_READ(__set_cont_read) \
|
||||
.set_cont_read = __set_cont_read,
|
||||
|
||||
#define SPINAND_FACT_OTP_INFO(__npages, __start_page, __ops) \
|
||||
.fact_otp = { \
|
||||
.layout = { \
|
||||
.npages = __npages, \
|
||||
.start_page = __start_page, \
|
||||
}, \
|
||||
.ops = __ops, \
|
||||
}
|
||||
|
||||
#define SPINAND_USER_OTP_INFO(__npages, __start_page, __ops) \
|
||||
.user_otp = { \
|
||||
.layout = { \
|
||||
.npages = __npages, \
|
||||
.start_page = __start_page, \
|
||||
}, \
|
||||
.ops = __ops, \
|
||||
}
|
||||
|
||||
#define SPINAND_READ_RETRY(__read_retries, __set_read_retry) \
|
||||
.read_retries = __read_retries, \
|
||||
.set_read_retry = __set_read_retry
|
||||
@ -444,6 +528,8 @@ struct spinand_dirmap {
|
||||
* actually relevant to enable this feature.
|
||||
* @set_cont_read: Enable/disable the continuous read feature
|
||||
* @priv: manufacturer private data
|
||||
* @fact_otp: SPI NAND factory OTP info.
|
||||
* @user_otp: SPI NAND user OTP info.
|
||||
* @read_retries: the number of read retry modes supported
|
||||
* @set_read_retry: Enable/disable the read retry feature
|
||||
* @last_wait_status: status of the last wait operation that will be used in case
|
||||
@ -487,6 +573,9 @@ struct spinand_device {
|
||||
int (*set_cont_read)(struct spinand_device *spinand,
|
||||
bool enable);
|
||||
|
||||
const struct spinand_fact_otp *fact_otp;
|
||||
const struct spinand_user_otp *user_otp;
|
||||
|
||||
unsigned int read_retries;
|
||||
int (*set_read_retry)(struct spinand_device *spinand,
|
||||
unsigned int retry_mode);
|
||||
@ -571,6 +660,29 @@ int spinand_match_and_init(struct spinand_device *spinand,
|
||||
enum spinand_readid_method rdid_method);
|
||||
|
||||
int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
|
||||
int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val);
|
||||
int spinand_select_target(struct spinand_device *spinand, unsigned int target);
|
||||
|
||||
int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,
|
||||
unsigned long poll_delay_us, u8 *s);
|
||||
|
||||
int spinand_read_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req);
|
||||
|
||||
int spinand_write_page(struct spinand_device *spinand,
|
||||
const struct nand_page_io_req *req);
|
||||
|
||||
size_t spinand_otp_page_size(struct spinand_device *spinand);
|
||||
size_t spinand_fact_otp_size(struct spinand_device *spinand);
|
||||
size_t spinand_user_otp_size(struct spinand_device *spinand);
|
||||
|
||||
int spinand_fact_otp_read(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf);
|
||||
int spinand_user_otp_read(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, u8 *buf);
|
||||
int spinand_user_otp_write(struct spinand_device *spinand, loff_t ofs,
|
||||
size_t len, size_t *retlen, const u8 *buf);
|
||||
|
||||
int spinand_set_mtd_otp_ops(struct spinand_device *spinand);
|
||||
|
||||
#endif /* __LINUX_MTD_SPINAND_H */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user