Use the new OTP ops to implement OTP access on Winbond flashes. Most
Winbond flashes provides up to four different OTP regions ("Security
Registers").
Winbond devices use a special opcode to read and write to the OTP
regions, just like the RDSFDP opcode. In fact, it seems that the
(undocumented) first OTP area of the newer flashes is the actual SFDP
table.
On a side note, Winbond devices also allow erasing the OTP regions as
long as the area isn't locked down.
Signed-off-by: Michael Walle <[email protected]>
---
drivers/mtd/spi-nor/core.c | 2 +-
drivers/mtd/spi-nor/core.h | 6 ++
drivers/mtd/spi-nor/otp.c | 164 ++++++++++++++++++++++++++++++++++++
include/linux/mtd/spi-nor.h | 9 ++
4 files changed, 180 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 0c5c757fa95b..ef7df26896f1 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1034,7 +1034,7 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
*
* Return: 0 on success, -errno otherwise.
*/
-static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
+int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
{
int ret;
u8 *sr_cr = nor->bouncebuf;
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index ec8da1243846..dfbf6ba42b57 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -496,6 +496,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
+int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr);
int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr);
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
@@ -503,6 +504,11 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
const u8 *buf);
+int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf);
+int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf);
+int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region);
+int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region);
+
int spi_nor_hwcaps_read2cmd(u32 hwcaps);
u8 spi_nor_convert_3to4_read(u8 opcode);
void spi_nor_set_read_settings(struct spi_nor_read_command *read,
diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
index 4e301fd5156b..4e8da9108c77 100644
--- a/drivers/mtd/spi-nor/otp.c
+++ b/drivers/mtd/spi-nor/otp.c
@@ -15,6 +15,170 @@
#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
+/**
+ * spi_nor_otp_read_secr() - read OTP data
+ * @nor: pointer to 'struct spi_nor'
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @buf: pointer to dst buffer
+ *
+ * Read OTP data from one region by using the SPINOR_OP_RSECR commands. This
+ * method is used on GigaDevice and Winbond flashes.
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
+{
+ u8 addr_width, read_opcode, read_dummy;
+ struct spi_mem_dirmap_desc *rdesc;
+ enum spi_nor_protocol read_proto;
+ int ret;
+
+ read_opcode = nor->read_opcode;
+ addr_width = nor->addr_width;
+ read_dummy = nor->read_dummy;
+ read_proto = nor->read_proto;
+ rdesc = nor->dirmap.rdesc;
+
+ nor->read_opcode = SPINOR_OP_RSECR;
+ nor->addr_width = 3;
+ nor->read_dummy = 8;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->dirmap.rdesc = NULL;
+
+ ret = spi_nor_read_data(nor, addr, len, buf);
+
+ nor->read_opcode = read_opcode;
+ nor->addr_width = addr_width;
+ nor->read_dummy = read_dummy;
+ nor->read_proto = read_proto;
+ nor->dirmap.rdesc = rdesc;
+
+ return ret;
+}
+
+/**
+ * spi_nor_otp_write_secr() - write OTP data
+ * @nor: pointer to 'struct spi_nor'
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @buf: pointer to src buffer
+ *
+ * Write OTP data to one region by using the SPINOR_OP_PSECR commands. This
+ * method is used on GigaDevice and Winbond flashes.
+ *
+ * Please note, the write must not span multiple OTP regions.
+ *
+ * Return: number of bytes written successfully, -errno otherwise
+ */
+int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
+{
+ enum spi_nor_protocol write_proto;
+ struct spi_mem_dirmap_desc *wdesc;
+ u8 addr_width, program_opcode;
+ int ret, written;
+
+ program_opcode = nor->program_opcode;
+ addr_width = nor->addr_width;
+ write_proto = nor->write_proto;
+ wdesc = nor->dirmap.wdesc;
+
+ nor->program_opcode = SPINOR_OP_PSECR;
+ nor->addr_width = 3;
+ nor->write_proto = SNOR_PROTO_1_1_1;
+ nor->dirmap.wdesc = NULL;
+
+ /*
+ * We only support a write to one single page. For now all winbond
+ * flashes only have one page per OTP region.
+ */
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ goto out;
+
+ written = spi_nor_write_data(nor, addr, len, buf);
+ if (written < 0)
+ goto out;
+
+ ret = spi_nor_wait_till_ready(nor);
+
+out:
+ nor->program_opcode = program_opcode;
+ nor->addr_width = addr_width;
+ nor->write_proto = write_proto;
+ nor->dirmap.wdesc = wdesc;
+
+ return ret ?: written;
+}
+
+static int spi_nor_otp_lock_bit_cr(unsigned int region)
+{
+ static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 };
+
+ if (region >= ARRAY_SIZE(lock_bits))
+ return -EINVAL;
+
+ return lock_bits[region];
+}
+
+/**
+ * spi_nor_otp_lock_sr2() - lock the OTP region
+ * @nor: pointer to 'struct spi_nor'
+ * @region: OTP region
+ *
+ * Lock the OTP region by writing the status register-2. This method is used on
+ * GigaDevice and Winbond flashes.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region)
+{
+ u8 *cr = nor->bouncebuf;
+ int ret, lock_bit;
+
+ lock_bit = spi_nor_otp_lock_bit_cr(region);
+ if (lock_bit < 0)
+ return lock_bit;
+
+ ret = spi_nor_read_cr(nor, cr);
+ if (ret)
+ return ret;
+
+ /* no need to write the register if region is already locked */
+ if (cr[0] & lock_bit)
+ return 0;
+
+ cr[0] |= lock_bit;
+
+ return spi_nor_write_16bit_cr_and_check(nor, cr[0]);
+}
+
+/**
+ * spi_nor_otp_is_locked_sr2() - get the OTP region lock status
+ * @nor: pointer to 'struct spi_nor'
+ * @region: OTP region
+ *
+ * Retrieve the OTP region lock bit by reading the status register-2. This
+ * method is used on GigaDevice and Winbond flashes.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region)
+{
+ u8 *cr = nor->bouncebuf;
+ int ret, lock_bit;
+
+ lock_bit = spi_nor_otp_lock_bit_cr(region);
+ if (lock_bit < 0)
+ return lock_bit;
+
+ ret = spi_nor_read_cr(nor, cr);
+ if (ret)
+ return ret;
+
+ return cr[0] & lock_bit;
+}
+
static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, int region)
{
const struct spi_nor_otp_organization *org = nor->params->otp.org;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index a0d572855444..6d1956049e90 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -107,6 +107,11 @@
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
+/* Used for GigaDevices and Winbond flashes. */
+#define SPINOR_OP_ESECR 0x44 /* Erase Security registers */
+#define SPINOR_OP_PSECR 0x42 /* Program Security registers */
+#define SPINOR_OP_RSECR 0x48 /* Read Security registers */
+
/* Status Register bits. */
#define SR_WIP BIT(0) /* Write in progress */
#define SR_WEL BIT(1) /* Write enable latch */
@@ -138,8 +143,12 @@
/* Status Register 2 bits. */
#define SR2_QUAD_EN_BIT1 BIT(1)
+#define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */
+#define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */
+#define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */
#define SR2_QUAD_EN_BIT7 BIT(7)
+
/* Supported SPI protocols */
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
#define SNOR_PROTO_INST_SHIFT 16
--
2.20.1
On 3/6/21 2:05 AM, Michael Walle wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> Use the new OTP ops to implement OTP access on Winbond flashes. Most
> Winbond flashes provides up to four different OTP regions ("Security
> Registers").
>
> Winbond devices use a special opcode to read and write to the OTP
> regions, just like the RDSFDP opcode. In fact, it seems that the
> (undocumented) first OTP area of the newer flashes is the actual SFDP
> table.
>
> On a side note, Winbond devices also allow erasing the OTP regions as
> long as the area isn't locked down.
>
> Signed-off-by: Michael Walle <[email protected]>
> ---
> drivers/mtd/spi-nor/core.c | 2 +-
> drivers/mtd/spi-nor/core.h | 6 ++
> drivers/mtd/spi-nor/otp.c | 164 ++++++++++++++++++++++++++++++++++++
> include/linux/mtd/spi-nor.h | 9 ++
> 4 files changed, 180 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index 0c5c757fa95b..ef7df26896f1 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -1034,7 +1034,7 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
> *
> * Return: 0 on success, -errno otherwise.
> */
> -static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
> +int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
> {
> int ret;
> u8 *sr_cr = nor->bouncebuf;
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index ec8da1243846..dfbf6ba42b57 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -496,6 +496,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
> int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
> int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
> int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
> +int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr);
>
> int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr);
> ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
> @@ -503,6 +504,11 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
> ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
> const u8 *buf);
>
> +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf);
> +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf);
> +int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region);
> +int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region);
> +
> int spi_nor_hwcaps_read2cmd(u32 hwcaps);
> u8 spi_nor_convert_3to4_read(u8 opcode);
> void spi_nor_set_read_settings(struct spi_nor_read_command *read,
> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
> index 4e301fd5156b..4e8da9108c77 100644
> --- a/drivers/mtd/spi-nor/otp.c
> +++ b/drivers/mtd/spi-nor/otp.c
> @@ -15,6 +15,170 @@
> #define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
> #define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions)
>
> +/**
> + * spi_nor_otp_read_secr() - read OTP data
> + * @nor: pointer to 'struct spi_nor'
> + * @from: offset to read from
> + * @len: number of bytes to read
> + * @buf: pointer to dst buffer
is buf DMA-able?
> + *
> + * Read OTP data from one region by using the SPINOR_OP_RSECR commands. This
> + * method is used on GigaDevice and Winbond flashes.
> + *
> + * Return: number of bytes read successfully, -errno otherwise
> + */
> +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
> +{
> + u8 addr_width, read_opcode, read_dummy;
> + struct spi_mem_dirmap_desc *rdesc;
> + enum spi_nor_protocol read_proto;
> + int ret;
> +
> + read_opcode = nor->read_opcode;
> + addr_width = nor->addr_width;
> + read_dummy = nor->read_dummy;
> + read_proto = nor->read_proto;
> + rdesc = nor->dirmap.rdesc;
> +
> + nor->read_opcode = SPINOR_OP_RSECR;
> + nor->addr_width = 3;
> + nor->read_dummy = 8;
> + nor->read_proto = SNOR_PROTO_1_1_1;
any winbond/gigadevice flashes with octal dtr support? Do they
provide SEC Register opcodes for octal dtr?
> + nor->dirmap.rdesc = NULL;
> +
> + ret = spi_nor_read_data(nor, addr, len, buf);
> +
> + nor->read_opcode = read_opcode;
> + nor->addr_width = addr_width;
> + nor->read_dummy = read_dummy;
> + nor->read_proto = read_proto;
> + nor->dirmap.rdesc = rdesc;
> +
> + return ret;
> +}
> +
> +/**
> + * spi_nor_otp_write_secr() - write OTP data
> + * @nor: pointer to 'struct spi_nor'
> + * @to: offset to write to
> + * @len: number of bytes to write
> + * @buf: pointer to src buffer
> + *
> + * Write OTP data to one region by using the SPINOR_OP_PSECR commands. This
> + * method is used on GigaDevice and Winbond flashes.
> + *
> + * Please note, the write must not span multiple OTP regions.
> + *
> + * Return: number of bytes written successfully, -errno otherwise
> + */
> +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
> +{
> + enum spi_nor_protocol write_proto;
> + struct spi_mem_dirmap_desc *wdesc;
> + u8 addr_width, program_opcode;
> + int ret, written;
> +
> + program_opcode = nor->program_opcode;
> + addr_width = nor->addr_width;
> + write_proto = nor->write_proto;
> + wdesc = nor->dirmap.wdesc;
> +
> + nor->program_opcode = SPINOR_OP_PSECR;
> + nor->addr_width = 3;
> + nor->write_proto = SNOR_PROTO_1_1_1;
> + nor->dirmap.wdesc = NULL;
> +
> + /*
> + * We only support a write to one single page. For now all winbond
> + * flashes only have one page per OTP region.
> + */
> + ret = spi_nor_write_enable(nor);
> + if (ret)
> + goto out;
> +
> + written = spi_nor_write_data(nor, addr, len, buf);
> + if (written < 0)
> + goto out;
> +
> + ret = spi_nor_wait_till_ready(nor);
> +
> +out:
> + nor->program_opcode = program_opcode;
> + nor->addr_width = addr_width;
> + nor->write_proto = write_proto;
> + nor->dirmap.wdesc = wdesc;
> +
> + return ret ?: written;
> +}
> +
> +static int spi_nor_otp_lock_bit_cr(unsigned int region)
> +{
> + static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 };
> +
> + if (region >= ARRAY_SIZE(lock_bits))
> + return -EINVAL;
> +
> + return lock_bits[region];
> +}
> +
> +/**
> + * spi_nor_otp_lock_sr2() - lock the OTP region
> + * @nor: pointer to 'struct spi_nor'
> + * @region: OTP region
> + *
> + * Lock the OTP region by writing the status register-2. This method is used on
> + * GigaDevice and Winbond flashes.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region)
> +{
> + u8 *cr = nor->bouncebuf;
> + int ret, lock_bit;
> +
> + lock_bit = spi_nor_otp_lock_bit_cr(region);
> + if (lock_bit < 0)
> + return lock_bit;
> +
> + ret = spi_nor_read_cr(nor, cr);
> + if (ret)
> + return ret;
> +
> + /* no need to write the register if region is already locked */
> + if (cr[0] & lock_bit)
> + return 0;
> +
> + cr[0] |= lock_bit;
> +
> + return spi_nor_write_16bit_cr_and_check(nor, cr[0]);
> +}
> +
> +/**
> + * spi_nor_otp_is_locked_sr2() - get the OTP region lock status
> + * @nor: pointer to 'struct spi_nor'
> + * @region: OTP region
> + *
> + * Retrieve the OTP region lock bit by reading the status register-2. This
> + * method is used on GigaDevice and Winbond flashes.
> + *
> + * Return: 0 on success, -errno otherwise.
> + */
> +int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region)
> +{
> + u8 *cr = nor->bouncebuf;
> + int ret, lock_bit;
> +
> + lock_bit = spi_nor_otp_lock_bit_cr(region);
> + if (lock_bit < 0)
> + return lock_bit;
> +
> + ret = spi_nor_read_cr(nor, cr);
> + if (ret)
> + return ret;
> +
> + return cr[0] & lock_bit;
> +}
> +
> static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, int region)
> {
> const struct spi_nor_otp_organization *org = nor->params->otp.org;
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index a0d572855444..6d1956049e90 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -107,6 +107,11 @@
> #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
> #define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
>
> +/* Used for GigaDevices and Winbond flashes. */
> +#define SPINOR_OP_ESECR 0x44 /* Erase Security registers */
> +#define SPINOR_OP_PSECR 0x42 /* Program Security registers */
> +#define SPINOR_OP_RSECR 0x48 /* Read Security registers */
> +
> /* Status Register bits. */
> #define SR_WIP BIT(0) /* Write in progress */
> #define SR_WEL BIT(1) /* Write enable latch */
> @@ -138,8 +143,12 @@
>
> /* Status Register 2 bits. */
> #define SR2_QUAD_EN_BIT1 BIT(1)
> +#define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */
> +#define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */
> +#define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */
> #define SR2_QUAD_EN_BIT7 BIT(7)
>
> +
not needed. You can catch this when running ./scripts/checkpatch --strict patch-name
> /* Supported SPI protocols */
> #define SNOR_PROTO_INST_MASK GENMASK(23, 16)
> #define SNOR_PROTO_INST_SHIFT 16
> --
> 2.20.1
>
On 3/6/21 2:05 AM, Michael Walle wrote:
> + nor->dirmap.rdesc = NULL;
why can't we use dirmap?
> +
> + ret = spi_nor_read_data(nor, addr, len, buf);
Am 2021-03-15 09:20, schrieb [email protected]:
> On 3/6/21 2:05 AM, Michael Walle wrote:
>> EXTERNAL EMAIL: Do not click links or open attachments unless you know
>> the content is safe
>>
>> Use the new OTP ops to implement OTP access on Winbond flashes. Most
>> Winbond flashes provides up to four different OTP regions ("Security
>> Registers").
>>
>> Winbond devices use a special opcode to read and write to the OTP
>> regions, just like the RDSFDP opcode. In fact, it seems that the
>> (undocumented) first OTP area of the newer flashes is the actual SFDP
>> table.
>>
>> On a side note, Winbond devices also allow erasing the OTP regions as
>> long as the area isn't locked down.
>>
>> Signed-off-by: Michael Walle <[email protected]>
>> ---
>> drivers/mtd/spi-nor/core.c | 2 +-
>> drivers/mtd/spi-nor/core.h | 6 ++
>> drivers/mtd/spi-nor/otp.c | 164
>> ++++++++++++++++++++++++++++++++++++
>> include/linux/mtd/spi-nor.h | 9 ++
>> 4 files changed, 180 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
>> index 0c5c757fa95b..ef7df26896f1 100644
>> --- a/drivers/mtd/spi-nor/core.c
>> +++ b/drivers/mtd/spi-nor/core.c
>> @@ -1034,7 +1034,7 @@ static int
>> spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
>> *
>> * Return: 0 on success, -errno otherwise.
>> */
>> -static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8
>> cr)
>> +int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
>> {
>> int ret;
>> u8 *sr_cr = nor->bouncebuf;
>> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
>> index ec8da1243846..dfbf6ba42b57 100644
>> --- a/drivers/mtd/spi-nor/core.h
>> +++ b/drivers/mtd/spi-nor/core.h
>> @@ -496,6 +496,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
>> int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
>> int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
>> int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
>> +int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr);
>>
>> int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr);
>> ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t
>> len,
>> @@ -503,6 +504,11 @@ ssize_t spi_nor_read_data(struct spi_nor *nor,
>> loff_t from, size_t len,
>> ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t
>> len,
>> const u8 *buf);
>>
>> +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t
>> len, u8 *buf);
>> +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t
>> len, u8 *buf);
>> +int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region);
>> +int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int
>> region);
>> +
>> int spi_nor_hwcaps_read2cmd(u32 hwcaps);
>> u8 spi_nor_convert_3to4_read(u8 opcode);
>> void spi_nor_set_read_settings(struct spi_nor_read_command *read,
>> diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c
>> index 4e301fd5156b..4e8da9108c77 100644
>> --- a/drivers/mtd/spi-nor/otp.c
>> +++ b/drivers/mtd/spi-nor/otp.c
>> @@ -15,6 +15,170 @@
>> #define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len)
>> #define spi_nor_otp_n_regions(nor)
>> ((nor)->params->otp.org->n_regions)
>>
>> +/**
>> + * spi_nor_otp_read_secr() - read OTP data
>> + * @nor: pointer to 'struct spi_nor'
>> + * @from: offset to read from
>> + * @len: number of bytes to read
>> + * @buf: pointer to dst buffer
>
> is buf DMA-able?
That's actually the same description as spi_nor_read_data().
Looks like the spimem will provide a DMA-able buffer on
the fly if necessary. I'm not sure about the the
spi_nor_controller_ops.
>> + *
>> + * Read OTP data from one region by using the SPINOR_OP_RSECR
>> commands. This
>> + * method is used on GigaDevice and Winbond flashes.
>> + *
>> + * Return: number of bytes read successfully, -errno otherwise
>> + */
>> +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t
>> len, u8 *buf)
>> +{
>> + u8 addr_width, read_opcode, read_dummy;
>> + struct spi_mem_dirmap_desc *rdesc;
>> + enum spi_nor_protocol read_proto;
>> + int ret;
>> +
>> + read_opcode = nor->read_opcode;
>> + addr_width = nor->addr_width;
>> + read_dummy = nor->read_dummy;
>> + read_proto = nor->read_proto;
>> + rdesc = nor->dirmap.rdesc;
>> +
>> + nor->read_opcode = SPINOR_OP_RSECR;
>> + nor->addr_width = 3;
>> + nor->read_dummy = 8;
>> + nor->read_proto = SNOR_PROTO_1_1_1;
>
> any winbond/gigadevice flashes with octal dtr support? Do they
> provide SEC Register opcodes for octal dtr?
AFAIK there are no winbond flashes with 8 bit I/O. There are
4bit/DTR modes, but not for the security registers.
I don't know what you had in mind. But I don't think it is
worth to read the 3x 256byte data faster than single bit I/O.
-michael
Am 2021-03-15 09:31, schrieb [email protected]:
> On 3/6/21 2:05 AM, Michael Walle wrote:
>> + nor->dirmap.rdesc = NULL;
>
> why can't we use dirmap?
Dirmap is used if the controller supports (transparent)
memory mapped access, right?
As you see I'm not familiar with that, nor does my
controller has support for it, well at least the driver,
the controller supports it actually.
But it also seems like how the flash is accessed is
set up in
spi_nor_probe()
spi_nor_create_read_dirmap()
And because the read opcode has to be changed, that isn't
possible.
Plese correct me if I'm wrong.
-michael