* [PATCH v3 0/7] mtd: spinand: add OTP support
@ 2024-12-26 13:55 Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 1/7] mtd: spinand: make spinand_{read,write}_page global Martin Kurbanov
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
This patchset implements the SPI-NAND OTP functions to allow access to
the SPI-NAND OTP data.
Specific support is added for Micron MT29F2G01ABAGD and ESMT F50L1G41LB/
F50D1G41LB flash chips.
Changelog:
v3 since v2 at [1]:
- Make cosmetic changes (Miquel Raynal)
- Separate patch to introduce helper functions
- Use dev_warn() instead of pr_warn()/WARN()
- New patch for introducing the start_page field when defining OTP
- Use mem_is_zero()
v2 since v1 at [2]:
- Make cosmetic changes (Miquel Raynal)
Links:
[1] https://lore.kernel.org/all/20240827174920.316756-1-mmkurbanov@salutedevices.com/
[2] https://lore.kernel.org/all/20240617133504.179705-1-mmkurbanov@salutedevices.com/
Martin Kurbanov (7):
mtd: spinand: make spinand_{read,write}_page global
mtd: spinand: add OTP support
mtd: spinand: make spinand_{wait,otp_page_size} global
mtd: spinand: add start_page to otp layout
mtd: spinand: otp: add helpers functions
mtd: spinand: micron: OTP access for MT29F2G01ABAGD
mtd: spinand: esmt: OTP access for F50{L,D}1G41LB
drivers/mtd/nand/spi/Makefile | 3 +-
drivers/mtd/nand/spi/core.c | 49 ++++++-
drivers/mtd/nand/spi/esmt.c | 70 ++++++++-
drivers/mtd/nand/spi/micron.c | 113 +++++++++++++-
drivers/mtd/nand/spi/otp.c | 267 ++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 70 +++++++++
6 files changed, 560 insertions(+), 12 deletions(-)
create mode 100644 drivers/mtd/nand/spi/otp.c
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 1/7] mtd: spinand: make spinand_{read,write}_page global
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 2/7] mtd: spinand: add OTP support Martin Kurbanov
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
Change these functions from static to global so that to use them later
in OTP operations. Since reading OTP pages is no different from reading
pages from the main area.
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
drivers/mtd/nand/spi/core.c | 24 ++++++++++++++++++++----
include/linux/mtd/spinand.h | 6 ++++++
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index e0b6715e5dfed..4b394eace2d09 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -566,8 +566,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;
@@ -597,8 +605,16 @@ static int spinand_read_page(struct spinand_device *spinand,
return nand_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;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 5c19ead604996..555846517faf6 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -519,4 +519,10 @@ int spinand_match_and_init(struct spinand_device *spinand,
int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
int spinand_select_target(struct spinand_device *spinand, unsigned int target);
+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);
+
#endif /* __LINUX_MTD_SPINAND_H */
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 2/7] mtd: spinand: add OTP support
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 1/7] mtd: spinand: make spinand_{read,write}_page global Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 3/7] mtd: spinand: make spinand_{wait,otp_page_size} global Martin Kurbanov
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
The MTD subsystem already supports accessing two OTP areas: user and
factory. User areas can be written by the user. This patch only adds
support for the user areas.
In this patch the OTP_INFO macro is provided to add parameters to
spinand_info.
To implement OTP operations, the client (flash driver) is provided with
5 callbacks: .read(), .write(), .info(), .lock(), .erase().
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
drivers/mtd/nand/spi/Makefile | 3 +-
drivers/mtd/nand/spi/core.c | 7 ++
drivers/mtd/nand/spi/otp.c | 169 ++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 52 +++++++++++
4 files changed, 230 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/nand/spi/otp.c
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index 19cc77288ebbc..60d2e830ffc6b 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,4 +1,5 @@
# 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
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 4b394eace2d09..d12f09b28e371 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1111,6 +1111,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->flags = table[i].flags;
spinand->id.len = 1 + table[i].devid.len;
spinand->select_target = table[i].select_target;
+ spinand->otp = &table[i].otp;
op = spinand_select_op_variant(spinand,
info->op_variants.read_cache);
@@ -1292,6 +1293,12 @@ static int spinand_init(struct spinand_device *spinand)
mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks;
mtd->_resume = spinand_mtd_resume;
+ if (spinand_otp_size(spinand)) {
+ ret = spinand_set_mtd_otp_ops(spinand);
+ if (ret)
+ goto err_cleanup_ecc_engine;
+ }
+
if (nand->ecc.engine) {
ret = mtd_ooblayout_count_freebytes(mtd);
if (ret < 0)
diff --git a/drivers/mtd/nand/spi/otp.c b/drivers/mtd/nand/spi/otp.c
new file mode 100644
index 0000000000000..3650ff336db14
--- /dev/null
+++ b/drivers/mtd/nand/spi/otp.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Martin Kurbanov <mmkurbanov@salutedevices.com>
+ */
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/spinand.h>
+
+/**
+ * spinand_otp_size() - Get SPI-NAND OTP area size
+ * @spinand: the spinand device
+ *
+ * Return: the OTP size.
+ */
+size_t spinand_otp_size(struct spinand_device *spinand)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ size_t otp_pagesize = nanddev_page_size(nand) +
+ nanddev_per_page_oobsize(nand);
+
+ return spinand->otp->layout.npages * otp_pagesize;
+}
+
+static int spinand_otp_check_bounds(struct spinand_device *spinand, loff_t ofs,
+ size_t len)
+{
+ if (ofs < 0 || ofs + len > spinand_otp_size(spinand))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int spinand_mtd_otp_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen, struct otp_info *buf)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ const struct spinand_otp_ops *ops = spinand->otp->ops;
+ int ret;
+
+ *retlen = 0;
+
+ mutex_lock(&spinand->lock);
+ ret = ops->info(spinand, len, buf, retlen);
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+static int spinand_mtd_otp_read(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u8 *buf)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ const struct spinand_otp_ops *ops = spinand->otp->ops;
+ int ret;
+
+ *retlen = 0;
+
+ if (!len)
+ return 0;
+
+ ret = spinand_otp_check_bounds(spinand, ofs, len);
+ if (ret)
+ return ret;
+
+ mutex_lock(&spinand->lock);
+ ret = ops->read(spinand, ofs, len, retlen, buf);
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+static int spinand_mtd_otp_write(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, const u8 *buf)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ const struct spinand_otp_ops *ops = spinand->otp->ops;
+ int ret;
+
+ *retlen = 0;
+
+ if (!len)
+ return 0;
+
+ ret = spinand_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;
+}
+
+static int spinand_mtd_otp_erase(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ const struct spinand_otp_ops *ops = spinand->otp->ops;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ ret = spinand_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;
+}
+
+static int spinand_mtd_otp_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ const struct spinand_otp_ops *ops = spinand->otp->ops;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ ret = spinand_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_otp_ops *ops = spinand->otp->ops;
+
+ if (!ops)
+ return -EINVAL;
+
+ if (ops->info)
+ mtd->_get_user_prot_info = spinand_mtd_otp_info;
+
+ if (ops->read)
+ mtd->_read_user_prot_reg = spinand_mtd_otp_read;
+
+ if (ops->write)
+ mtd->_write_user_prot_reg = spinand_mtd_otp_write;
+
+ if (ops->lock)
+ mtd->_lock_user_prot_reg = spinand_mtd_otp_lock;
+
+ if (ops->erase)
+ mtd->_erase_user_prot_reg = spinand_mtd_otp_erase;
+
+ return 0;
+}
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 555846517faf6..a9ad286de2902 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -322,6 +322,43 @@ struct spinand_ondie_ecc_conf {
u8 status;
};
+/**
+ * struct spinand_otp_layout - structure to describe the SPI NAND OTP area
+ * @npages: number of pages in the OTP
+ */
+struct spinand_otp_layout {
+ unsigned int npages;
+};
+
+/**
+ * struct spinand_otp_ops - SPI NAND OTP methods
+ * @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_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_otp - SPI NAND OTP grouping structure
+ * @layout: OTP region layout
+ * @ops: OTP access ops
+ */
+struct spinand_otp {
+ const struct spinand_otp_layout layout;
+ const struct spinand_otp_ops *ops;
+};
+
/**
* struct spinand_info - Structure used to describe SPI NAND chips
* @model: model name
@@ -354,6 +391,7 @@ struct spinand_info {
} op_variants;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
+ struct spinand_otp otp;
};
#define SPINAND_ID(__method, ...) \
@@ -379,6 +417,14 @@ struct spinand_info {
#define SPINAND_SELECT_TARGET(__func) \
.select_target = __func,
+#define SPINAND_OTP_INFO(__npages, __ops) \
+ .otp = { \
+ .layout = { \
+ .npages = __npages, \
+ }, \
+ .ops = __ops, \
+ }
+
#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
__flags, ...) \
{ \
@@ -422,6 +468,7 @@ struct spinand_dirmap {
* passed in spi_mem_op be DMA-able, so we can't based the bufs on
* the stack
* @manufacturer: SPI NAND manufacturer information
+ * @otp: SPI NAND OTP info.
* @priv: manufacturer private data
*/
struct spinand_device {
@@ -450,6 +497,7 @@ struct spinand_device {
u8 *oobbuf;
u8 *scratchbuf;
const struct spinand_manufacturer *manufacturer;
+ const struct spinand_otp *otp;
void *priv;
};
@@ -525,4 +573,8 @@ int spinand_read_page(struct spinand_device *spinand,
int spinand_write_page(struct spinand_device *spinand,
const struct nand_page_io_req *req);
+size_t spinand_otp_size(struct spinand_device *spinand);
+
+int spinand_set_mtd_otp_ops(struct spinand_device *spinand);
+
#endif /* __LINUX_MTD_SPINAND_H */
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 3/7] mtd: spinand: make spinand_{wait,otp_page_size} global
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 1/7] mtd: spinand: make spinand_{read,write}_page global Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 2/7] mtd: spinand: add OTP support Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 4/7] mtd: spinand: add start_page to otp layout Martin Kurbanov
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
Change the functions spinand_wait() and spinand_otp_page_size() from
static to global so that SPI NAND flash drivers don't duplicate it.
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
drivers/mtd/nand/spi/core.c | 18 ++++++++++++++----
drivers/mtd/nand/spi/otp.c | 19 ++++++++++++++-----
include/linux/mtd/spinand.h | 4 ++++
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index d12f09b28e371..018d6b6c61d10 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -496,10 +496,20 @@ static int spinand_erase_op(struct spinand_device *spinand,
return spi_mem_exec_op(spinand->spimem, &op);
}
-static int spinand_wait(struct spinand_device *spinand,
- unsigned long initial_delay_us,
- unsigned long poll_delay_us,
- u8 *s)
+/**
+ * spinand_wait() - Poll memory device status
+ * @spinand: the spinand device
+ * @initial_delay_us: delay in us before starting to poll
+ * @poll_delay_us: time to sleep between reads in us
+ * @s: the pointer to variable to store the value of REG_STATUS
+ *
+ * This function polls a status register (REG_STATUS) and returns when
+ * the STATUS_READY bit is 0 or when the timeout has expired.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,
+ unsigned long poll_delay_us, u8 *s)
{
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(REG_STATUS,
spinand->scratchbuf);
diff --git a/drivers/mtd/nand/spi/otp.c b/drivers/mtd/nand/spi/otp.c
index 3650ff336db14..a50d6af818613 100644
--- a/drivers/mtd/nand/spi/otp.c
+++ b/drivers/mtd/nand/spi/otp.c
@@ -8,6 +8,19 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/spinand.h>
+/**
+ * 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);
+}
+
/**
* spinand_otp_size() - Get SPI-NAND OTP area size
* @spinand: the spinand device
@@ -16,11 +29,7 @@
*/
size_t spinand_otp_size(struct spinand_device *spinand)
{
- struct nand_device *nand = spinand_to_nand(spinand);
- size_t otp_pagesize = nanddev_page_size(nand) +
- nanddev_per_page_oobsize(nand);
-
- return spinand->otp->layout.npages * otp_pagesize;
+ return spinand->otp->layout.npages * spinand_otp_page_size(spinand);
}
static int spinand_otp_check_bounds(struct spinand_device *spinand, loff_t ofs,
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index a9ad286de2902..19d76057c0444 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -567,12 +567,16 @@ int spinand_match_and_init(struct spinand_device *spinand,
int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, 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_otp_size(struct spinand_device *spinand);
int spinand_set_mtd_otp_ops(struct spinand_device *spinand);
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 4/7] mtd: spinand: add start_page to otp layout
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
` (2 preceding siblings ...)
2024-12-26 13:55 ` [PATCH v3 3/7] mtd: spinand: make spinand_{wait,otp_page_size} global Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
2024-12-30 9:16 ` Miquel Raynal
2024-12-26 13:55 ` [PATCH v3 5/7] mtd: spinand: otp: add helpers functions Martin Kurbanov
` (2 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
The OTP area is divided into two parts: the factory and the user.
In SPI-NAND, it is usually this one OTP region: the first few pages are
allocated to the factory area. Therefore, enter the start_page field,
which indicates from which page the custom OTP region starts.
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
include/linux/mtd/spinand.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 19d76057c0444..d6dbb85094283 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -325,9 +325,11 @@ struct spinand_ondie_ecc_conf {
/**
* 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 OTP area.
*/
struct spinand_otp_layout {
unsigned int npages;
+ unsigned int start_page;
};
/**
@@ -417,10 +419,11 @@ struct spinand_info {
#define SPINAND_SELECT_TARGET(__func) \
.select_target = __func,
-#define SPINAND_OTP_INFO(__npages, __ops) \
+#define SPINAND_OTP_INFO(__npages, __start_page, __ops) \
.otp = { \
.layout = { \
.npages = __npages, \
+ .start_page = __start_page, \
}, \
.ops = __ops, \
}
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 5/7] mtd: spinand: otp: add helpers functions
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
` (3 preceding siblings ...)
2024-12-26 13:55 ` [PATCH v3 4/7] mtd: spinand: add start_page to otp layout Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 6/7] mtd: spinand: micron: OTP access for MT29F2G01ABAGD Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 7/7] mtd: spinand: esmt: OTP access for F50{L,D}1G41LB Martin Kurbanov
6 siblings, 0 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
The global functions spinand_otp_read() and spinand_otp_write() have
been introduced. Since most SPI-NAND flashes read/write OTP in the same
way, let's define global functions to avoid code duplication.
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
drivers/mtd/nand/spi/otp.c | 89 +++++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 5 +++
2 files changed, 94 insertions(+)
diff --git a/drivers/mtd/nand/spi/otp.c b/drivers/mtd/nand/spi/otp.c
index a50d6af818613..b8204bb6d848e 100644
--- a/drivers/mtd/nand/spi/otp.c
+++ b/drivers/mtd/nand/spi/otp.c
@@ -41,6 +41,95 @@ static int spinand_otp_check_bounds(struct spinand_device *spinand, loff_t ofs,
return 0;
}
+static int spinand_otp_rw(struct spinand_device *spinand, loff_t ofs,
+ size_t len, size_t *retlen, u8 *buf, bool is_write)
+{
+ 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);
+ 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 + spinand->otp->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_to_mtd(spinand)->dev,
+ "Can not disable OTP mode\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/**
+ * spinand_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_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_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_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);
+}
+
static int spinand_mtd_otp_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index d6dbb85094283..dcb1811e55d25 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -582,6 +582,11 @@ int spinand_write_page(struct spinand_device *spinand,
size_t spinand_otp_page_size(struct spinand_device *spinand);
size_t spinand_otp_size(struct spinand_device *spinand);
+int spinand_otp_read(struct spinand_device *spinand, loff_t ofs, size_t len,
+ size_t *retlen, u8 *buf);
+int spinand_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 */
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 6/7] mtd: spinand: micron: OTP access for MT29F2G01ABAGD
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
` (4 preceding siblings ...)
2024-12-26 13:55 ` [PATCH v3 5/7] mtd: spinand: otp: add helpers functions Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 7/7] mtd: spinand: esmt: OTP access for F50{L,D}1G41LB Martin Kurbanov
6 siblings, 0 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
Support for OTP area access on Micron MT29F2G01ABAGD chip.
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
drivers/mtd/nand/spi/micron.c | 113 +++++++++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
index 8d741be6d5f3e..beb4ca8df0653 100644
--- a/drivers/mtd/nand/spi/micron.c
+++ b/drivers/mtd/nand/spi/micron.c
@@ -9,6 +9,8 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/string.h>
#define SPINAND_MFR_MICRON 0x2c
@@ -28,6 +30,10 @@
#define MICRON_SELECT_DIE(x) ((x) << 6)
+#define MICRON_MT29F2G01ABAGD_CFG_OTP_STATE BIT(7)
+#define MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK \
+ (CFG_OTP_ENABLE | MICRON_MT29F2G01ABAGD_CFG_OTP_STATE)
+
static SPINAND_OP_VARIANTS(quadio_read_cache_variants,
//SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
@@ -182,6 +188,110 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
+static int mt29f2g01abagd_otp_is_locked(struct spinand_device *spinand)
+{
+ size_t bufsize = spinand_otp_page_size(spinand);
+ size_t retlen;
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = spinand_upd_cfg(spinand,
+ MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK,
+ MICRON_MT29F2G01ABAGD_CFG_OTP_STATE);
+ if (ret)
+ goto free_buf;
+
+ ret = spinand_otp_read(spinand, 0, bufsize, &retlen, buf);
+
+ if (spinand_upd_cfg(spinand, MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK,
+ 0)) {
+ dev_warn(&spinand_to_mtd(spinand)->dev,
+ "Can not disable OTP mode\n");
+ ret = -EIO;
+ }
+
+ if (ret)
+ goto free_buf;
+
+ /* If all zeros, then the OTP area is locked. */
+ if (mem_is_zero(buf, bufsize))
+ ret = 1;
+
+free_buf:
+ kfree(buf);
+ return ret;
+}
+
+static int mt29f2g01abagd_otp_info(struct spinand_device *spinand, size_t len,
+ struct otp_info *buf, size_t *retlen)
+{
+ int locked;
+
+ if (len < sizeof(*buf))
+ return -EINVAL;
+
+ locked = mt29f2g01abagd_otp_is_locked(spinand);
+ if (locked < 0)
+ return locked;
+
+ buf->locked = locked;
+ buf->start = 0;
+ buf->length = spinand_otp_size(spinand);
+
+ *retlen = sizeof(*buf);
+ return 0;
+}
+
+static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from,
+ size_t len)
+{
+ struct spi_mem_op write_op = SPINAND_WR_EN_DIS_OP(true);
+ struct spi_mem_op exec_op = SPINAND_PROG_EXEC_OP(0);
+ u8 status;
+ int ret;
+
+ ret = spinand_upd_cfg(spinand,
+ MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK,
+ MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK);
+ if (!ret)
+ return ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &write_op);
+ if (!ret)
+ goto out;
+
+ ret = spi_mem_exec_op(spinand->spimem, &exec_op);
+ if (!ret)
+ goto out;
+
+ ret = spinand_wait(spinand,
+ SPINAND_WRITE_INITIAL_DELAY_US,
+ SPINAND_WRITE_POLL_DELAY_US,
+ &status);
+ if (!ret && (status & STATUS_PROG_FAILED))
+ ret = -EIO;
+
+out:
+ if (spinand_upd_cfg(spinand, MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK, 0)) {
+ dev_warn(&spinand_to_mtd(spinand)->dev,
+ "Can not disable OTP mode\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static const struct spinand_otp_ops mt29f2g01abagd_otp_ops = {
+ .info = mt29f2g01abagd_otp_info,
+ .lock = mt29f2g01abagd_otp_lock,
+ .read = spinand_otp_read,
+ .write = spinand_otp_write,
+};
+
static const struct spinand_info micron_spinand_table[] = {
/* M79A 2Gb 3.3V */
SPINAND_INFO("MT29F2G01ABAGD",
@@ -193,7 +303,8 @@ static const struct spinand_info micron_spinand_table[] = {
&x4_update_cache_variants),
0,
SPINAND_ECCINFO(µn_8_ooblayout,
- micron_8_ecc_get_status)),
+ micron_8_ecc_get_status),
+ SPINAND_OTP_INFO(12, 2, &mt29f2g01abagd_otp_ops)),
/* M79A 2Gb 1.8V */
SPINAND_INFO("MT29F2G01ABBGD",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 7/7] mtd: spinand: esmt: OTP access for F50{L,D}1G41LB
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
` (5 preceding siblings ...)
2024-12-26 13:55 ` [PATCH v3 6/7] mtd: spinand: micron: OTP access for MT29F2G01ABAGD Martin Kurbanov
@ 2024-12-26 13:55 ` Martin Kurbanov
6 siblings, 0 replies; 9+ messages in thread
From: Martin Kurbanov @ 2024-12-26 13:55 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Ezra Buehler, Alexey Romanov, Frieder Schrempf
Cc: linux-kernel, linux-mtd, kernel, Martin Kurbanov
Support for OTP area access on ESMT F50L1G41LB and F50D1G41LB chips.
Signed-off-by: Martin Kurbanov <mmkurbanov@salutedevices.com>
---
drivers/mtd/nand/spi/esmt.c | 70 +++++++++++++++++++++++++++++++++++--
1 file changed, 68 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c
index 4597a82de23a4..481005c518f15 100644
--- a/drivers/mtd/nand/spi/esmt.c
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -8,10 +8,15 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
+#include <linux/spi/spi-mem.h>
/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
#define SPINAND_MFR_ESMT_C8 0xc8
+#define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7)
+#define ESMT_F50L1G41LB_CFG_OTP_LOCK \
+ (CFG_OTP_ENABLE | ESMT_F50L1G41LB_CFG_OTP_PROTECT)
+
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
@@ -102,6 +107,65 @@ static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
.free = f50l1g41lb_ooblayout_free,
};
+static int f50l1g41lb_otp_info(struct spinand_device *spinand, size_t len,
+ struct otp_info *buf, size_t *retlen)
+{
+ if (len < sizeof(*buf))
+ return -EINVAL;
+
+ buf->locked = 0;
+ buf->start = 0;
+ buf->length = spinand_otp_size(spinand);
+
+ *retlen = sizeof(*buf);
+ return 0;
+}
+
+static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from,
+ size_t len)
+{
+ struct spi_mem_op write_op = SPINAND_WR_EN_DIS_OP(true);
+ struct spi_mem_op exec_op = SPINAND_PROG_EXEC_OP(0);
+ u8 status;
+ int ret;
+
+ ret = spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK,
+ ESMT_F50L1G41LB_CFG_OTP_LOCK);
+ if (!ret)
+ return ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &write_op);
+ if (!ret)
+ goto out;
+
+ ret = spi_mem_exec_op(spinand->spimem, &exec_op);
+ if (!ret)
+ goto out;
+
+ ret = spinand_wait(spinand,
+ SPINAND_WRITE_INITIAL_DELAY_US,
+ SPINAND_WRITE_POLL_DELAY_US,
+ &status);
+ if (!ret && (status & STATUS_PROG_FAILED))
+ ret = -EIO;
+
+out:
+ if (spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK, 0)) {
+ dev_warn(&spinand_to_mtd(spinand)->dev,
+ "Can not disable OTP mode\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static const struct spinand_otp_ops f50l1g41lb_otp_ops = {
+ .info = f50l1g41lb_otp_info,
+ .lock = f50l1g41lb_otp_lock,
+ .read = spinand_otp_read,
+ .write = spinand_otp_write,
+};
+
static const struct spinand_info esmt_c8_spinand_table[] = {
SPINAND_INFO("F50L1G41LB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
@@ -112,7 +176,8 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
0,
- SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
+ SPINAND_OTP_INFO(28, 2, &f50l1g41lb_otp_ops)),
SPINAND_INFO("F50D1G41LB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
0x7f, 0x7f),
@@ -122,7 +187,8 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
0,
- SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
+ SPINAND_OTP_INFO(28, 2, &f50l1g41lb_otp_ops)),
SPINAND_INFO("F50D2G41KA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f,
0x7f, 0x7f),
--
2.43.0
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3 4/7] mtd: spinand: add start_page to otp layout
2024-12-26 13:55 ` [PATCH v3 4/7] mtd: spinand: add start_page to otp layout Martin Kurbanov
@ 2024-12-30 9:16 ` Miquel Raynal
0 siblings, 0 replies; 9+ messages in thread
From: Miquel Raynal @ 2024-12-30 9:16 UTC (permalink / raw)
To: Martin Kurbanov
Cc: Richard Weinberger, Vignesh Raghavendra, Ezra Buehler,
Alexey Romanov, Frieder Schrempf, linux-kernel, linux-mtd, kernel
On 26/12/2024 at 16:55:49 +03, Martin Kurbanov <mmkurbanov@salutedevices.com> wrote:
> The OTP area is divided into two parts: the factory and the user.
> In SPI-NAND, it is usually this one OTP region: the first few pages
> are
(this sentence might be reworded, it is not very clear)
> allocated to the factory area. Therefore, enter the start_page field,
> which indicates from which page the custom OTP region starts.
TBH I don't like this shortcut and I would prefer having:
- two otp structures defining each parts (user/factory)
- both the user and factory otp hooks provided
- perhaps a top level macro which does both initializations based on
the (user_otp_)start_page parameter.
But I don't like much hiding the layout subtlety behind a
"start_page" member (although it should probably be named
"user_area_start_page" or something like that, otherwise it's
misleading).
This would also be more future proof, manufacturers are creative.
Thanks,
Miquèl
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2024-12-30 10:41 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-26 13:55 [PATCH v3 0/7] mtd: spinand: add OTP support Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 1/7] mtd: spinand: make spinand_{read,write}_page global Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 2/7] mtd: spinand: add OTP support Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 3/7] mtd: spinand: make spinand_{wait,otp_page_size} global Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 4/7] mtd: spinand: add start_page to otp layout Martin Kurbanov
2024-12-30 9:16 ` Miquel Raynal
2024-12-26 13:55 ` [PATCH v3 5/7] mtd: spinand: otp: add helpers functions Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 6/7] mtd: spinand: micron: OTP access for MT29F2G01ABAGD Martin Kurbanov
2024-12-26 13:55 ` [PATCH v3 7/7] mtd: spinand: esmt: OTP access for F50{L,D}1G41LB Martin Kurbanov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).