--- /dev/null
+From 71e5da46e78c1cd24e2feed251a2845327447ad8 Mon Sep 17 00:00:00 2001
+From: Kees Cook <kees@kernel.org>
+Date: Wed, 21 May 2025 23:27:04 +0200
+Subject: wireguard: global: add __nonstring annotations for unterminated
+ strings
+
+When a character array without a terminating NUL character has a static
+initializer, GCC 15's -Wunterminated-string-initialization will only
+warn if the array lacks the "nonstring" attribute[1]. Mark the arrays
+with __nonstring to correctly identify the char array as "not a C string"
+and thereby eliminate the warning:
+
+../drivers/net/wireguard/cookie.c:29:56: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Wunterminated-string-initialization]
+ 29 | static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----";
+ | ^~~~~~~~~~
+../drivers/net/wireguard/cookie.c:30:58: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Wunterminated-string-initialization]
+ 30 | static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--";
+ | ^~~~~~~~~~
+../drivers/net/wireguard/noise.c:28:38: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (38 chars into 37 available) [-Wunterminated-string-initialization]
+ 28 | static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+../drivers/net/wireguard/noise.c:29:39: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (35 chars into 34 available) [-Wunterminated-string-initialization]
+ 29 | static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The arrays are always used with their fixed size, so use __nonstring.
+
+Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1]
+Signed-off-by: Kees Cook <kees@kernel.org>
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Link: https://patch.msgid.link/20250521212707.1767879-3-Jason@zx2c4.com
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/wireguard/cookie.c | 4 ++--
+ drivers/net/wireguard/noise.c | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/wireguard/cookie.c
++++ b/drivers/net/wireguard/cookie.c
+@@ -26,8 +26,8 @@ void wg_cookie_checker_init(struct cooki
+ }
+
+ enum { COOKIE_KEY_LABEL_LEN = 8 };
+-static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----";
+-static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--";
++static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "mac1----";
++static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "cookie--";
+
+ static void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN],
+ const u8 pubkey[NOISE_PUBLIC_KEY_LEN],
+--- a/drivers/net/wireguard/noise.c
++++ b/drivers/net/wireguard/noise.c
+@@ -25,8 +25,8 @@
+ * <- e, ee, se, psk, {}
+ */
+
+-static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
+-static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
++static const u8 handshake_name[37] __nonstring = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
++static const u8 identifier_name[34] __nonstring = "WireGuard v1 zx2c4 Jason@zx2c4.com";
+ static u8 handshake_init_hash[NOISE_HASH_LEN] __ro_after_init;
+ static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __ro_after_init;
+ static atomic64_t keypair_counter = ATOMIC64_INIT(0);
--- /dev/null
+From 65cb56d49f6edea409600a3c61effc70ee5d43d8 Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <j4g8y7@gmail.com>
+Date: Thu, 1 May 2025 18:19:16 +0200
+Subject: spi: spi-qpic-snand: validate user/chip specific ECC properties
+
+The driver only supports 512 bytes ECC step size and 4 bit ECC strength
+at the moment, however it does not reject unsupported step/strength
+configurations. Due to this, whenever the driver is used with a flash
+chip which needs stronger ECC protection, the following warning is shown
+in the kernel log:
+
+ [ 0.574648] spi-nand spi0.0: GigaDevice SPI NAND was found.
+ [ 0.635748] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
+ [ 0.649079] nand: WARNING: (null): the ECC used on your system is too weak compared to the one required by the NAND chip
+
+Although the message indicates that something is wrong, but it often gets
+unnoticed, which can cause serious problems. For example when the user
+writes something into the flash chip despite the warning, the written data
+may won't be readable by the bootloader or by the boot ROM. In the worst
+case, when the attached SPI NAND chip is the boot device, the board may not
+be able to boot anymore.
+
+Also, it is not even possible to create a backup of the flash, because
+reading its content results in bogus data. For example, dumping the first
+page of the flash gives this:
+
+ # hexdump -C -n 2048 /dev/mtd0
+ 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000040 0f 0f 0f 0f 0f 0f 0f 0d 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ 00000050 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 000001c0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ 000001d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000200 0f 0f 0f 0f f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.....[..........|
+ 00000210 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 000002f0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 1f 0f 0f |................|
+ 00000300 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 000003c0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ff 0f 0f 0f |................|
+ 000003d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000400 0f 0f 0f 0f 0f 0f 0f 0f e9 74 c9 06 f5 5b ff ff |.........t...[..|
+ 00000410 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 000005d0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ 000005e0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000600 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f c6 be 0f c3 |................|
+ 00000610 e9 74 c9 06 f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.t...[..........|
+ 00000620 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000770 0f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ 00000780 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000800
+ #
+
+Doing the same by using the downstream kernel results in different output:
+
+ # hexdump -C -n 2048 /dev/mtd0
+ 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
+ *
+ 00000800
+ #
+
+This patch adds some sanity checks to the code to prevent using the driver
+with unsupported ECC step/strength configurations. After the change, probing
+of the driver fails in such cases:
+
+ [ 0.655038] spi-nand spi0.0: GigaDevice SPI NAND was found.
+ [ 0.659159] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
+ [ 0.669138] qcom_snand 79b0000.spi: only 4 bits ECC strength is supported
+ [ 0.677476] nand: No suitable ECC configuration
+ [ 0.689909] spi-nand spi0.0: probe with driver spi-nand failed with error -95
+
+This helps to avoid the aforementioned hassles until support for 8 bit ECC
+strength gets implemented.
+
+Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface")
+Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
+Link: https://patch.msgid.link/20250501-qpic-snand-validate-ecc-v1-1-532776581a66@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-qpic-snand.c | 42 +++++++++++++++++++++++++++++++-----
+ 1 file changed, 37 insertions(+), 5 deletions(-)
+
+--- a/drivers/spi/spi-qpic-snand.c
++++ b/drivers/spi/spi-qpic-snand.c
+@@ -249,9 +249,11 @@ static const struct mtd_ooblayout_ops qc
+ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
+ {
+ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
++ struct nand_ecc_props *reqs = &nand->ecc.requirements;
++ struct nand_ecc_props *user = &nand->ecc.user_conf;
+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+- int cwperpage, bad_block_byte;
++ int cwperpage, bad_block_byte, ret;
+ struct qpic_ecc *ecc_cfg;
+
+ cwperpage = mtd->writesize / NANDC_STEP_SIZE;
+@@ -260,11 +262,39 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL);
+ if (!ecc_cfg)
+ return -ENOMEM;
++
++ if (user->step_size && user->strength) {
++ ecc_cfg->step_size = user->step_size;
++ ecc_cfg->strength = user->strength;
++ } else if (reqs->step_size && reqs->strength) {
++ ecc_cfg->step_size = reqs->step_size;
++ ecc_cfg->strength = reqs->strength;
++ } else {
++ /* use defaults */
++ ecc_cfg->step_size = NANDC_STEP_SIZE;
++ ecc_cfg->strength = 4;
++ }
++
++ if (ecc_cfg->step_size != NANDC_STEP_SIZE) {
++ dev_err(snandc->dev,
++ "only %u bytes ECC step size is supported\n",
++ NANDC_STEP_SIZE);
++ ret = -EOPNOTSUPP;
++ goto err_free_ecc_cfg;
++ }
++
++ if (ecc_cfg->strength != 4) {
++ dev_err(snandc->dev,
++ "only 4 bits ECC strength is supported\n");
++ ret = -EOPNOTSUPP;
++ goto err_free_ecc_cfg;
++ }
++
+ snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize,
+ GFP_KERNEL);
+ if (!snandc->qspi->oob_buf) {
+- kfree(ecc_cfg);
+- return -ENOMEM;
++ ret = -ENOMEM;
++ goto err_free_ecc_cfg;
+ }
+
+ memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize);
+@@ -279,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
+
+ ecc_cfg->steps = 4;
+- ecc_cfg->strength = 4;
+- ecc_cfg->step_size = 512;
+ ecc_cfg->cw_data = 516;
+ ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
+ bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1;
+@@ -338,6 +366,10 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ ecc_cfg->strength, ecc_cfg->step_size);
+
+ return 0;
++
++err_free_ecc_cfg:
++ kfree(ecc_cfg);
++ return ret;
+ }
+
+ static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
+++ /dev/null
-From b98994cb9bc24f5c7575c86650f96c384576fdfa Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Mon, 17 Nov 2025 02:54:19 +0000
-Subject: [PATCH] mtd: spinand: esmt: add support for F50L1G41LC
-
-This adds support for ESMT F50L1G41LC, which appears to be an updated
-version of the already supported F50L1G41LB.
-Add esmt_8c SPI_NAND manufacturer to account for the newly used vendor
-ID with support for the ESMT F50L1G41LC chip.
-
-Link: https://github.com/openwrt/openwrt/pull/15214#issuecomment-3514824435
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- drivers/mtd/nand/spi/core.c | 1 +
- drivers/mtd/nand/spi/esmt.c | 24 ++++++++++++++++++++++++
- include/linux/mtd/spinand.h | 1 +
- 3 files changed, 26 insertions(+)
-
---- a/drivers/mtd/nand/spi/core.c
-+++ b/drivers/mtd/nand/spi/core.c
-@@ -1114,6 +1114,7 @@ static const struct nand_ops spinand_ops
- static const struct spinand_manufacturer *spinand_manufacturers[] = {
- &alliancememory_spinand_manufacturer,
- &ato_spinand_manufacturer,
-+ &esmt_8c_spinand_manufacturer,
- &esmt_c8_spinand_manufacturer,
- &fmsh_spinand_manufacturer,
- &foresee_spinand_manufacturer,
---- a/drivers/mtd/nand/spi/esmt.c
-+++ b/drivers/mtd/nand/spi/esmt.c
-@@ -11,6 +11,7 @@
-
- /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
- #define SPINAND_MFR_ESMT_C8 0xc8
-+#define SPINAND_MFR_ESMT_8C 0x8c
-
- static SPINAND_OP_VARIANTS(read_cache_variants,
- SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
-@@ -102,6 +103,19 @@ static const struct mtd_ooblayout_ops f5
- .free = f50l1g41lb_ooblayout_free,
- };
-
-+
-+static const struct spinand_info esmt_8c_spinand_table[] = {
-+ SPINAND_INFO("F50L1G41LC",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C),
-+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
-+ NAND_ECCREQ(1, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ 0,
-+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
-+};
-+
- static const struct spinand_info esmt_c8_spinand_table[] = {
- SPINAND_INFO("F50L1G41LB",
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
-@@ -138,6 +152,14 @@ static const struct spinand_info esmt_c8
- static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
- };
-
-+const struct spinand_manufacturer esmt_8c_spinand_manufacturer = {
-+ .id = SPINAND_MFR_ESMT_8C,
-+ .name = "ESMT",
-+ .chips = esmt_8c_spinand_table,
-+ .nchips = ARRAY_SIZE(esmt_8c_spinand_table),
-+ .ops = &esmt_spinand_manuf_ops,
-+};
-+
- const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
- .id = SPINAND_MFR_ESMT_C8,
- .name = "ESMT",
---- a/include/linux/mtd/spinand.h
-+++ b/include/linux/mtd/spinand.h
-@@ -262,6 +262,7 @@ struct spinand_manufacturer {
- /* SPI NAND manufacturers */
- extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
- extern const struct spinand_manufacturer ato_spinand_manufacturer;
-+extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer;
- extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
- extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
- extern const struct spinand_manufacturer foresee_spinand_manufacturer;
--- /dev/null
+From 0dc7e656ddd54c3267b7cc18c1ac8ec1297ed02f Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <j4g8y7@gmail.com>
+Date: Wed, 2 Jul 2025 14:35:23 +0200
+Subject: mtd: nand: qpic-common: add defines for ECC_MODE values
+
+Add defines for the values of the ECC_MODE field of the NAND_DEV0_ECC_CFG
+register and change both the 'qcom-nandc' and 'spi-qpic-snand' drivers to
+use those instead of magic numbers.
+
+No functional changes. This is in preparation for adding 8 bit ECC strength
+support for the 'spi-qpic-snand' driver.
+
+Reviewed-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
+Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-1-ae2c17a30bb7@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/mtd/nand/raw/qcom_nandc.c | 6 +++---
+ drivers/spi/spi-qpic-snand.c | 2 +-
+ include/linux/mtd/nand-qpic-common.h | 2 ++
+ 3 files changed, 6 insertions(+), 4 deletions(-)
+
+--- a/drivers/mtd/nand/raw/qcom_nandc.c
++++ b/drivers/mtd/nand/raw/qcom_nandc.c
+@@ -1379,7 +1379,7 @@ static int qcom_nand_attach_chip(struct
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ int cwperpage, bad_block_byte, ret;
+ bool wide_bus;
+- int ecc_mode = 1;
++ int ecc_mode = ECC_MODE_8BIT;
+
+ /* controller only supports 512 bytes data steps */
+ ecc->size = NANDC_STEP_SIZE;
+@@ -1400,7 +1400,7 @@ static int qcom_nand_attach_chip(struct
+ if (ecc->strength >= 8) {
+ /* 8 bit ECC defaults to BCH ECC on all platforms */
+ host->bch_enabled = true;
+- ecc_mode = 1;
++ ecc_mode = ECC_MODE_8BIT;
+
+ if (wide_bus) {
+ host->ecc_bytes_hw = 14;
+@@ -1420,7 +1420,7 @@ static int qcom_nand_attach_chip(struct
+ if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
+ /* BCH */
+ host->bch_enabled = true;
+- ecc_mode = 0;
++ ecc_mode = ECC_MODE_4BIT;
+
+ if (wide_bus) {
+ host->ecc_bytes_hw = 8;
+--- a/drivers/spi/spi-qpic-snand.c
++++ b/drivers/spi/spi-qpic-snand.c
+@@ -349,7 +349,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ FIELD_PREP(ECC_SW_RESET, 0) |
+ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
+ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
+- FIELD_PREP(ECC_MODE_MASK, 0) |
++ FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) |
+ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
+
+ ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;
+--- a/include/linux/mtd/nand-qpic-common.h
++++ b/include/linux/mtd/nand-qpic-common.h
+@@ -101,6 +101,8 @@
+ #define ECC_SW_RESET BIT(1)
+ #define ECC_MODE 4
+ #define ECC_MODE_MASK GENMASK(5, 4)
++#define ECC_MODE_4BIT 0
++#define ECC_MODE_8BIT 1
+ #define ECC_PARITY_SIZE_BYTES_BCH 8
+ #define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8)
+ #define ECC_NUM_DATA_BYTES 16
--- /dev/null
+From 913bf8d50cbd144c87e9660b591781179182ff59 Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <j4g8y7@gmail.com>
+Date: Wed, 2 Jul 2025 14:35:24 +0200
+Subject: spi: spi-qpic-snand: add support for 8 bits ECC strength
+
+Even though the hardware supports 8 bits ECC strength, but that is not
+handled in the driver yet. This change adds the missing bits in order
+to allow using the driver with chips which require 8 bits ECC strength.
+
+No functional changes intended with regard to the existing 4 bits ECC
+strength support.
+
+Tested on an IPQ9574 platform using a GigaDevice GD5F2GM7REYIG chip.
+
+Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
+Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-2-ae2c17a30bb7@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-qpic-snand.c | 21 ++++++++++++++++-----
+ 1 file changed, 16 insertions(+), 5 deletions(-)
+
+--- a/drivers/spi/spi-qpic-snand.c
++++ b/drivers/spi/spi-qpic-snand.c
+@@ -283,9 +283,22 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ goto err_free_ecc_cfg;
+ }
+
+- if (ecc_cfg->strength != 4) {
++ switch (ecc_cfg->strength) {
++ case 4:
++ ecc_cfg->ecc_mode = ECC_MODE_4BIT;
++ ecc_cfg->ecc_bytes_hw = 7;
++ ecc_cfg->spare_bytes = 4;
++ break;
++
++ case 8:
++ ecc_cfg->ecc_mode = ECC_MODE_8BIT;
++ ecc_cfg->ecc_bytes_hw = 13;
++ ecc_cfg->spare_bytes = 2;
++ break;
++
++ default:
+ dev_err(snandc->dev,
+- "only 4 bits ECC strength is supported\n");
++ "only 4 or 8 bits ECC strength is supported\n");
+ ret = -EOPNOTSUPP;
+ goto err_free_ecc_cfg;
+ }
+@@ -302,8 +315,6 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ nand->ecc.ctx.priv = ecc_cfg;
+ snandc->qspi->mtd = mtd;
+
+- ecc_cfg->ecc_bytes_hw = 7;
+- ecc_cfg->spare_bytes = 4;
+ ecc_cfg->bbm_size = 1;
+ ecc_cfg->bch_enabled = true;
+ ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
+@@ -349,7 +360,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
+ FIELD_PREP(ECC_SW_RESET, 0) |
+ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
+ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
+- FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) |
++ FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) |
+ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
+
+ ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;
--- /dev/null
+From e4a0cf9f1d90e6888e5373da3314f761024f6c97 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Thu, 18 Sep 2025 00:53:59 +0300
+Subject: mtd: spinand: fix direct mapping creation sizes
+
+Continuous mode is only supported for data reads, thus writing
+requires only single flash page mapping.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/nand/spi/core.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1028,18 +1028,13 @@ static int spinand_create_dirmap(struct
+ unsigned int plane)
+ {
+ struct nand_device *nand = spinand_to_nand(spinand);
+- struct spi_mem_dirmap_info info = {
+- .length = nanddev_page_size(nand) +
+- nanddev_per_page_oobsize(nand),
+- };
++ struct spi_mem_dirmap_info info = { 0 };
+ struct spi_mem_dirmap_desc *desc;
+
+- if (spinand->cont_read_possible)
+- info.length = nanddev_eraseblock_size(nand);
+-
+ /* The plane number is passed in MSB just above the column address */
+ info.offset = plane << fls(nand->memorg.pagesize);
+
++ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
+ info.op_tmpl = *spinand->op_templates.update_cache;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, &info);
+@@ -1048,6 +1043,8 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc = desc;
+
++ if (spinand->cont_read_possible)
++ info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, &info);
+@@ -1063,6 +1060,7 @@ static int spinand_create_dirmap(struct
+ return 0;
+ }
+
++ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
+ info.op_tmpl = *spinand->op_templates.update_cache;
+ info.op_tmpl.data.ecc = true;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+@@ -1072,6 +1070,8 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc_ecc = desc;
+
++ if (spinand->cont_read_possible)
++ info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl.data.ecc = true;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
--- /dev/null
+From 004f8ea0d9917398aabff7388b3bf62a84a4088b Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Thu, 18 Sep 2025 00:54:00 +0300
+Subject: mtd: spinand: try a regular dirmap if creating a dirmap for
+ continuous reading fails
+
+Continuous reading may result in multiple flash pages reading in one
+operation. Typically only one flash page has read/written (a little bit
+more than 2-4 Kb), but continuous reading requires the spi controller
+to read up to 512 Kb in one operation without toggling CS in beetween.
+
+Roughly speaking spi controllers can be divided on 2 categories:
+ * spi controllers without dirmap acceleration support
+ * spi controllers with dirmap acceleration support
+
+Firt of them will have issues with continuous reading if restriction on
+the transfer length is implemented in the adjust_op_size() handler.
+Second group often supports acceleration of single page only reading.
+Thus enabling of continuous reading can break flash reading.
+
+This patch tries to create dirmap for continuous reading first and
+fallback to regular reading if spi controller refuses to create it.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/nand/spi/core.c | 43 ++++++++++++++++++++++++++++++-------
+ 1 file changed, 35 insertions(+), 8 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1024,6 +1024,39 @@ static int spinand_mtd_block_isreserved(
+ return ret;
+ }
+
++static struct spi_mem_dirmap_desc *spinand_create_rdesc(
++ struct spinand_device *spinand,
++ struct spi_mem_dirmap_info *info)
++{
++ struct nand_device *nand = spinand_to_nand(spinand);
++ struct spi_mem_dirmap_desc *desc = NULL;
++
++ if (spinand->cont_read_possible) {
++ /*
++ * spi controller may return an error if info->length is
++ * too large
++ */
++ info->length = nanddev_eraseblock_size(nand);
++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
++ spinand->spimem, info);
++ }
++
++ if (IS_ERR_OR_NULL(desc)) {
++ /*
++ * continuous reading is not supported by flash or
++ * its spi controller, use regular reading
++ */
++ spinand->cont_read_possible = false;
++
++ info->length = nanddev_page_size(nand) +
++ nanddev_per_page_oobsize(nand);
++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
++ spinand->spimem, info);
++ }
++
++ return desc;
++}
++
+ static int spinand_create_dirmap(struct spinand_device *spinand,
+ unsigned int plane)
+ {
+@@ -1043,11 +1076,8 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc = desc;
+
+- if (spinand->cont_read_possible)
+- info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+- spinand->spimem, &info);
++ desc = spinand_create_rdesc(spinand, &info);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+@@ -1070,12 +1100,9 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc_ecc = desc;
+
+- if (spinand->cont_read_possible)
+- info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl.data.ecc = true;
+- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+- spinand->spimem, &info);
++ desc = spinand_create_rdesc(spinand, &info);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
--- /dev/null
+From 010dc7f2dd6a0078ade3f88f627ed5fbf45ceb94 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Thu, 18 Sep 2025 00:54:01 +0300
+Subject: mtd: spinand: repeat reading in regular mode if continuous reading
+ fails
+
+Continuous reading may result in multiple flash pages reading in one
+operation. Unfortunately, not all spinand controllers support such
+large reading. They will read less data. Unfortunately, the operation
+can't be continued.
+
+In this case:
+ * disable continuous reading on this (not good enough) spi controller
+ * repeat reading in regular mode.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/nand/spi/core.c | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -427,8 +427,16 @@ static int spinand_read_from_cache_op(st
+ * Dirmap accesses are allowed to toggle the CS.
+ * Toggling the CS during a continuous read is forbidden.
+ */
+- if (nbytes && req->continuous)
+- return -EIO;
++ if (nbytes && req->continuous) {
++ /*
++ * Spi controller with broken support of continuous
++ * reading was detected. Disable future use of
++ * continuous reading and return -EAGAIN to retry
++ * reading within regular mode.
++ */
++ spinand->cont_read_possible = false;
++ return -EAGAIN;
++ }
+ }
+
+ if (req->datalen)
+@@ -841,10 +849,19 @@ static int spinand_mtd_read(struct mtd_i
+
+ old_stats = mtd->ecc_stats;
+
+- if (spinand_use_cont_read(mtd, from, ops))
++ if (spinand_use_cont_read(mtd, from, ops)) {
+ ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips);
+- else
++ if (ret == -EAGAIN && !spinand->cont_read_possible) {
++ /*
++ * Spi controller with broken support of continuous
++ * reading was detected (see spinand_read_from_cache_op()),
++ * repeat reading in regular mode.
++ */
++ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
++ }
++ } else {
+ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
++ }
+
+ if (ops->stats) {
+ ops->stats->uncorrectable_errors +=
--- /dev/null
+From b98994cb9bc24f5c7575c86650f96c384576fdfa Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Mon, 17 Nov 2025 02:54:19 +0000
+Subject: [PATCH] mtd: spinand: esmt: add support for F50L1G41LC
+
+This adds support for ESMT F50L1G41LC, which appears to be an updated
+version of the already supported F50L1G41LB.
+Add esmt_8c SPI_NAND manufacturer to account for the newly used vendor
+ID with support for the ESMT F50L1G41LC chip.
+
+Link: https://github.com/openwrt/openwrt/pull/15214#issuecomment-3514824435
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/nand/spi/core.c | 1 +
+ drivers/mtd/nand/spi/esmt.c | 24 ++++++++++++++++++++++++
+ include/linux/mtd/spinand.h | 1 +
+ 3 files changed, 26 insertions(+)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1158,6 +1158,7 @@ static const struct nand_ops spinand_ops
+ static const struct spinand_manufacturer *spinand_manufacturers[] = {
+ &alliancememory_spinand_manufacturer,
+ &ato_spinand_manufacturer,
++ &esmt_8c_spinand_manufacturer,
+ &esmt_c8_spinand_manufacturer,
+ &fmsh_spinand_manufacturer,
+ &foresee_spinand_manufacturer,
+--- a/drivers/mtd/nand/spi/esmt.c
++++ b/drivers/mtd/nand/spi/esmt.c
+@@ -11,6 +11,7 @@
+
+ /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
+ #define SPINAND_MFR_ESMT_C8 0xc8
++#define SPINAND_MFR_ESMT_8C 0x8c
+
+ static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+@@ -102,6 +103,19 @@ static const struct mtd_ooblayout_ops f5
+ .free = f50l1g41lb_ooblayout_free,
+ };
+
++
++static const struct spinand_info esmt_8c_spinand_table[] = {
++ SPINAND_INFO("F50L1G41LC",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C),
++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(1, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ 0,
++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
++};
++
+ static const struct spinand_info esmt_c8_spinand_table[] = {
+ SPINAND_INFO("F50L1G41LB",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
+@@ -138,6 +152,14 @@ static const struct spinand_info esmt_c8
+ static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
+ };
+
++const struct spinand_manufacturer esmt_8c_spinand_manufacturer = {
++ .id = SPINAND_MFR_ESMT_8C,
++ .name = "ESMT",
++ .chips = esmt_8c_spinand_table,
++ .nchips = ARRAY_SIZE(esmt_8c_spinand_table),
++ .ops = &esmt_spinand_manuf_ops,
++};
++
+ const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
+ .id = SPINAND_MFR_ESMT_C8,
+ .name = "ESMT",
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -262,6 +262,7 @@ struct spinand_manufacturer {
+ /* SPI NAND manufacturers */
+ extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
+ extern const struct spinand_manufacturer ato_spinand_manufacturer;
++extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer;
+ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
+ extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
+ extern const struct spinand_manufacturer foresee_spinand_manufacturer;
--- /dev/null
+From f6dffe2a9ed1bdcee1879e2728310fb1e08602cf Mon Sep 17 00:00:00 2001
+From: Mikhail Zhilkin <csharper2005@gmail.com>
+Date: Thu, 27 Nov 2025 22:59:00 +0300
+Subject: mtd: spinand: add support for FudanMicro FM25S01BI3
+
+Add support for FudanMicro FM25S01BI3 SPI NAND.
+
+Link: https://www.fmsh.com/nvm/FM25S01BI3_ds_eng.pdf
+
+Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+---
+ drivers/mtd/nand/spi/fmsh.c | 72 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 72 insertions(+)
+
+--- a/drivers/mtd/nand/spi/fmsh.c
++++ b/drivers/mtd/nand/spi/fmsh.c
+@@ -9,6 +9,13 @@
+ #include <linux/kernel.h>
+ #include <linux/mtd/spinand.h>
+
++#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
++ #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
++ #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
++ #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4)
++ #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4)
++ #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4)
++
+ #define SPINAND_MFR_FMSH 0xA1
+
+ static SPINAND_OP_VARIANTS(read_cache_variants,
+@@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struc
+ return 0;
+ }
+
++static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
++ u8 status)
++{
++ switch (status & FM25S01BI3_STATUS_ECC_MASK) {
++ case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
++ return 0;
++
++ case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
++ return -EBADMSG;
++
++ case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
++ return 3;
++
++ case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
++ return 6;
++
++ case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
++ return 8;
++
++ default:
++ break;
++ }
++
++ return -EINVAL;
++}
++
++static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section)
++ return -ERANGE;
++
++ region->offset = 64;
++ region->length = 64;
++
++ return 0;
++}
++
++static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
++ struct mtd_oob_region *region)
++{
++ if (section > 3)
++ return -ERANGE;
++
++ region->offset = (16 * section) + 4;
++ region->length = 12;
++
++ return 0;
++}
++
+ static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
+ .ecc = fm25s01a_ooblayout_ecc,
+ .free = fm25s01a_ooblayout_free,
+ };
+
++static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
++ .ecc = fm25s01bi3_ooblayout_ecc,
++ .free = fm25s01bi3_ooblayout_free,
++};
++
+ static const struct spinand_info fmsh_spinand_table[] = {
+ SPINAND_INFO("FM25S01A",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
+@@ -60,6 +122,16 @@ static const struct spinand_info fmsh_sp
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
++ SPINAND_INFO("FM25S01BI3",
++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
++ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
++ NAND_ECCREQ(8, 512),
++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++ &write_cache_variants,
++ &update_cache_variants),
++ SPINAND_HAS_QE_BIT,
++ SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
++ fm25s01bi3_ecc_get_status)),
+ };
+
+ static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
--- /dev/null
+From 599b92fd0efa8b7c43e7f58c9dd0f7951f7cbf09 Mon Sep 17 00:00:00 2001
+From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
+Date: Wed, 18 Dec 2024 18:33:58 +0000
+Subject: dt-bindings: leds: Add LED1202 LED Controller
+
+The LED1202 is a 12-channel low quiescent current LED driver with:
+ * Supply range from 2.6 V to 5 V
+ * 20 mA current capability per channel
+ * 1.8 V compatible I2C control interface
+ * 8-bit analog dimming individual control
+ * 12-bit local PWM resolution
+ * 8 programmable patterns
+
+If the led node is present in the controller then the channel is
+set to active.
+
+Signed-off-by: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/r/20241218183401.41687-3-vicentiu.galanopulo@remote-tech.co.uk
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ .../devicetree/bindings/leds/st,led1202.yaml | 132 ++++++++++++++++++
+ 1 file changed, 132 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/leds/st,led1202.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/leds/st,led1202.yaml
+@@ -0,0 +1,132 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/leds/st,led1202.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: ST LED1202 LED controllers
++
++maintainers:
++ - Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
++
++description: |
++ The LED1202 is a 12-channel low quiescent current LED controller
++ programmable via I2C; The output current can be adjusted separately
++ for each channel by 8-bit analog and 12-bit digital dimming control.
++ Datasheet available at
++ https://www.st.com/en/power-management/led1202.html
++
++properties:
++ compatible:
++ const: st,led1202
++
++ reg:
++ maxItems: 1
++
++ "#address-cells":
++ const: 1
++
++ "#size-cells":
++ const: 0
++
++patternProperties:
++ "^led@[0-9a-f]$":
++ type: object
++ $ref: common.yaml#
++ unevaluatedProperties: false
++
++ properties:
++ reg:
++ minimum: 0
++ maximum: 11
++
++ required:
++ - reg
++
++required:
++ - compatible
++ - reg
++ - "#address-cells"
++ - "#size-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/leds/common.h>
++
++ i2c {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ led-controller@58 {
++ compatible = "st,led1202";
++ reg = <0x58>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ led@0 {
++ reg = <0x0>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_RED>;
++ function-enumerator = <1>;
++ };
++
++ led@1 {
++ reg = <0x1>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_GREEN>;
++ function-enumerator = <2>;
++ };
++
++ led@2 {
++ reg = <0x2>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_BLUE>;
++ function-enumerator = <3>;
++ };
++
++ led@3 {
++ reg = <0x3>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_RED>;
++ function-enumerator = <4>;
++ };
++
++ led@4 {
++ reg = <0x4>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_GREEN>;
++ function-enumerator = <5>;
++ };
++
++ led@5 {
++ reg = <0x5>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_BLUE>;
++ function-enumerator = <6>;
++ };
++
++ led@6 {
++ reg = <0x6>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_RED>;
++ function-enumerator = <7>;
++ };
++
++ led@7 {
++ reg = <0x7>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_GREEN>;
++ function-enumerator = <8>;
++ };
++
++ led@8 {
++ reg = <0x8>;
++ function = LED_FUNCTION_STATUS;
++ color = <LED_COLOR_ID_BLUE>;
++ function-enumerator = <9>;
++ };
++ };
++ };
++...
--- /dev/null
+From 939757aafeb9c266dda37657ee5f7a73ffd35ae2 Mon Sep 17 00:00:00 2001
+From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
+Date: Wed, 18 Dec 2024 18:33:59 +0000
+Subject: leds: Add LED1202 I2C driver
+
+The output current can be adjusted separately for each channel by 8-bit
+analog (current sink input) and 12-bit digital (PWM) dimming control. The
+LED1202 implements 12 low-side current generators with independent dimming
+control.
+Internal volatile memory allows the user to store up to 8 different patterns,
+each pattern is a particular output configuration in terms of PWM
+duty-cycle (on 4096 steps). Analog dimming (on 256 steps) is per channel but
+common to all patterns. Each device tree LED node will have a corresponding
+entry in /sys/class/leds with the label name. The brightness property
+corresponds to the per channel analog dimming, while the patterns[1-8] to the
+PWM dimming control.
+
+Signed-off-by: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
+Link: https://lore.kernel.org/r/20241218183401.41687-4-vicentiu.galanopulo@remote-tech.co.uk
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/Kconfig | 10 +
+ drivers/leds/Makefile | 1 +
+ drivers/leds/leds-st1202.c | 416 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 427 insertions(+)
+ create mode 100644 drivers/leds/leds-st1202.c
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -931,6 +931,16 @@ config LEDS_LM36274
+ Say Y to enable the LM36274 LED driver for TI LMU devices.
+ This supports the LED device LM36274.
+
++config LEDS_ST1202
++ tristate "LED Support for STMicroelectronics LED1202 I2C chips"
++ depends on LEDS_CLASS
++ depends on I2C
++ depends on OF
++ select LEDS_TRIGGERS
++ help
++ Say Y to enable support for LEDs connected to LED1202
++ LED driver chips accessed via the I2C bus.
++
+ config LEDS_TPS6105X
+ tristate "LED support for TI TPS6105X"
+ depends on LEDS_CLASS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -81,6 +81,7 @@ obj-$(CONFIG_LEDS_POWERNV) += leds-powe
+ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
+ obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
+ obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
++obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o
+ obj-$(CONFIG_LEDS_SUN50I_A100) += leds-sun50i-a100.o
+ obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
+ obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
+--- /dev/null
++++ b/drivers/leds/leds-st1202.c
+@@ -0,0 +1,416 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * LED driver for STMicroelectronics LED1202 chip
++ *
++ * Copyright (C) 2024 Remote-Tech Ltd. UK
++ */
++
++#include <linux/cleanup.h>
++#include <linux/ctype.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/leds.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++
++#define ST1202_CHAN_DISABLE_ALL 0x00
++#define ST1202_CHAN_ENABLE_HIGH 0x03
++#define ST1202_CHAN_ENABLE_LOW 0x02
++#define ST1202_CONFIG_REG 0x04
++/* PATS: Pattern sequence feature enable */
++#define ST1202_CONFIG_REG_PATS BIT(7)
++/* PATSR: Pattern sequence runs (self-clear when sequence is finished) */
++#define ST1202_CONFIG_REG_PATSR BIT(6)
++#define ST1202_CONFIG_REG_SHFT BIT(3)
++#define ST1202_DEV_ENABLE 0x01
++#define ST1202_DEV_ENABLE_ON BIT(0)
++#define ST1202_DEV_ENABLE_RESET BIT(7)
++#define ST1202_DEVICE_ID 0x00
++#define ST1202_ILED_REG0 0x09
++#define ST1202_MAX_LEDS 12
++#define ST1202_MAX_PATTERNS 8
++#define ST1202_MILLIS_PATTERN_DUR_MAX 5660
++#define ST1202_MILLIS_PATTERN_DUR_MIN 22
++#define ST1202_PATTERN_DUR 0x16
++#define ST1202_PATTERN_PWM 0x1E
++#define ST1202_PATTERN_REP 0x15
++
++struct st1202_led {
++ struct fwnode_handle *fwnode;
++ struct led_classdev led_cdev;
++ struct st1202_chip *chip;
++ bool is_active;
++ int led_num;
++};
++
++struct st1202_chip {
++ struct i2c_client *client;
++ struct mutex lock;
++ struct st1202_led leds[ST1202_MAX_LEDS];
++};
++
++static struct st1202_led *cdev_to_st1202_led(struct led_classdev *cdev)
++{
++ return container_of(cdev, struct st1202_led, led_cdev);
++}
++
++static int st1202_read_reg(struct st1202_chip *chip, int reg, uint8_t *val)
++{
++ struct device *dev = &chip->client->dev;
++ int ret;
++
++ ret = i2c_smbus_read_byte_data(chip->client, reg);
++ if (ret < 0) {
++ dev_err(dev, "Failed to read register [0x%x]: %d\n", reg, ret);
++ return ret;
++ }
++
++ *val = (uint8_t)ret;
++ return 0;
++}
++
++static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
++{
++ struct device *dev = &chip->client->dev;
++ int ret;
++
++ ret = i2c_smbus_write_byte_data(chip->client, reg, val);
++ if (ret != 0)
++ dev_err(dev, "Failed to write %d to register [0x%x]: %d\n", val, reg, ret);
++
++ return ret;
++}
++
++static uint8_t st1202_prescalar_to_miliseconds(unsigned int value)
++{
++ return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1;
++}
++
++static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
++ int pattern, unsigned int value)
++{
++ u8 value_l, value_h;
++ int ret;
++
++ value_l = (u8)value;
++ value_h = (u8)(value >> 8);
++
++ /*
++ * Datasheet: Register address low = 1Eh + 2*(xh) + 18h*(yh),
++ * where x is the channel number (led number) in hexadecimal (x = 00h .. 0Bh)
++ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
++ */
++ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + (led_num * 2) + 0x18 * pattern),
++ value_l);
++ if (ret != 0)
++ return ret;
++
++ /*
++ * Datasheet: Register address high = 1Eh + 01h + 2(xh) +18h*(yh),
++ * where x is the channel number in hexadecimal (x = 00h .. 0Bh)
++ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
++ */
++ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + 0x1 + (led_num * 2) + 0x18 * pattern),
++ value_h);
++ if (ret != 0)
++ return ret;
++
++ return 0;
++}
++
++static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern,
++ unsigned int value)
++{
++ return st1202_write_reg(chip, (ST1202_PATTERN_DUR + pattern),
++ st1202_prescalar_to_miliseconds(value));
++}
++
++static void st1202_brightness_set(struct led_classdev *led_cdev,
++ enum led_brightness value)
++{
++ struct st1202_led *led = cdev_to_st1202_led(led_cdev);
++ struct st1202_chip *chip = led->chip;
++
++ guard(mutex)(&chip->lock);
++
++ st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
++}
++
++static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
++{
++ struct st1202_led *led = cdev_to_st1202_led(led_cdev);
++ struct st1202_chip *chip = led->chip;
++ u8 value = 0;
++
++ guard(mutex)(&chip->lock);
++
++ st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
++
++ return value;
++}
++
++static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
++{
++ u8 chan_low, chan_high;
++ int ret;
++
++ guard(mutex)(&chip->lock);
++
++ if (led_num <= 7) {
++ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low);
++ if (ret < 0)
++ return ret;
++
++ chan_low = active ? chan_low | BIT(led_num) : chan_low & ~BIT(led_num);
++
++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, chan_low);
++ if (ret < 0)
++ return ret;
++
++ } else {
++ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_HIGH, &chan_high);
++ if (ret < 0)
++ return ret;
++
++ chan_high = active ? chan_high | (BIT(led_num) >> 8) :
++ chan_high & ~(BIT(led_num) >> 8);
++
++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, chan_high);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
++{
++ struct st1202_led *led = cdev_to_st1202_led(ldev);
++ struct st1202_chip *chip = led->chip;
++
++ return st1202_channel_set(chip, led->led_num, value == LED_OFF ? false : true);
++}
++
++static int st1202_led_pattern_clear(struct led_classdev *ldev)
++{
++ struct st1202_led *led = cdev_to_st1202_led(ldev);
++ struct st1202_chip *chip = led->chip;
++ int ret;
++
++ guard(mutex)(&chip->lock);
++
++ for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
++ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
++ if (ret != 0)
++ return ret;
++
++ ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN);
++ if (ret != 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int st1202_led_pattern_set(struct led_classdev *ldev,
++ struct led_pattern *pattern,
++ u32 len, int repeat)
++{
++ struct st1202_led *led = cdev_to_st1202_led(ldev);
++ struct st1202_chip *chip = led->chip;
++ int ret;
++
++ if (len > ST1202_MAX_PATTERNS)
++ return -EINVAL;
++
++ guard(mutex)(&chip->lock);
++
++ for (int patt = 0; patt < len; patt++) {
++ if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
++ pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
++ return -EINVAL;
++
++ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
++ if (ret != 0)
++ return ret;
++
++ ret = st1202_duration_pattern_write(chip, patt, pattern[patt].delta_t);
++ if (ret != 0)
++ return ret;
++ }
++
++ ret = st1202_write_reg(chip, ST1202_PATTERN_REP, repeat);
++ if (ret != 0)
++ return ret;
++
++ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR |
++ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT));
++ if (ret != 0)
++ return ret;
++
++ return 0;
++}
++
++static int st1202_dt_init(struct st1202_chip *chip)
++{
++ struct device *dev = &chip->client->dev;
++ struct st1202_led *led;
++ int err, reg;
++
++ for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
++ struct led_init_data init_data = {};
++
++ err = of_property_read_u32(child, "reg", ®);
++ if (err)
++ return dev_err_probe(dev, err, "Invalid register\n");
++
++ led = &chip->leds[reg];
++ led->is_active = true;
++ led->fwnode = of_fwnode_handle(child);
++
++ led->led_cdev.max_brightness = U8_MAX;
++ led->led_cdev.brightness_set_blocking = st1202_led_set;
++ led->led_cdev.pattern_set = st1202_led_pattern_set;
++ led->led_cdev.pattern_clear = st1202_led_pattern_clear;
++ led->led_cdev.default_trigger = "pattern";
++
++ init_data.fwnode = led->fwnode;
++ init_data.devicename = "st1202";
++ init_data.default_label = ":";
++
++ err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
++ if (err < 0)
++ return dev_err_probe(dev, err, "Failed to register LED class device\n");
++
++ led->led_cdev.brightness_set = st1202_brightness_set;
++ led->led_cdev.brightness_get = st1202_brightness_get;
++ }
++
++ return 0;
++}
++
++static int st1202_setup(struct st1202_chip *chip)
++{
++ int ret;
++
++ guard(mutex)(&chip->lock);
++
++ /*
++ * Once the supply voltage is applied, the LED1202 executes some internal checks,
++ * afterwords it stops the oscillator and puts the internal LDO in quiescent mode.
++ * To start the device, EN bit must be set inside the “Device Enable” register at
++ * address 01h. As soon as EN is set, the LED1202 loads the adjustment parameters
++ * from the internal non-volatile memory and performs an auto-calibration procedure
++ * in order to increase the output current precision.
++ * Such initialization lasts about 6.5 ms.
++ */
++
++ /* Reset the chip during setup */
++ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_RESET);
++ if (ret < 0)
++ return ret;
++
++ /* Enable phase-shift delay feature */
++ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
++ if (ret < 0)
++ return ret;
++
++ /* Enable the device */
++ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_ON);
++ if (ret < 0)
++ return ret;
++
++ /* Duration of initialization */
++ usleep_range(6500, 10000);
++
++ /* Deactivate all LEDS (channels) and activate only the ones found in Device Tree */
++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, ST1202_CHAN_DISABLE_ALL);
++ if (ret < 0)
++ return ret;
++
++ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, ST1202_CHAN_DISABLE_ALL);
++ if (ret < 0)
++ return ret;
++
++ ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
++ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int st1202_probe(struct i2c_client *client)
++{
++ struct st1202_chip *chip;
++ struct st1202_led *led;
++ int ret;
++
++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
++ return dev_err_probe(&client->dev, -EIO, "SMBUS Byte Data not Supported\n");
++
++ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
++ if (!chip)
++ return -ENOMEM;
++
++ devm_mutex_init(&client->dev, &chip->lock);
++ chip->client = client;
++
++ ret = st1202_dt_init(chip);
++ if (ret < 0)
++ return ret;
++
++ ret = st1202_setup(chip);
++ if (ret < 0)
++ return ret;
++
++ for (int i = 0; i < ST1202_MAX_LEDS; i++) {
++ led = &chip->leds[i];
++ led->chip = chip;
++ led->led_num = i;
++
++ if (!led->is_active)
++ continue;
++
++ ret = st1202_channel_set(led->chip, led->led_num, true);
++ if (ret < 0)
++ return dev_err_probe(&client->dev, ret,
++ "Failed to activate LED channel\n");
++
++ ret = st1202_led_pattern_clear(&led->led_cdev);
++ if (ret < 0)
++ return dev_err_probe(&client->dev, ret,
++ "Failed to clear LED pattern\n");
++ }
++
++ return 0;
++}
++
++static const struct i2c_device_id st1202_id[] = {
++ { "st1202-i2c" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(i2c, st1202_id);
++
++static const struct of_device_id st1202_dt_ids[] = {
++ { .compatible = "st,led1202" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, st1202_dt_ids);
++
++static struct i2c_driver st1202_driver = {
++ .driver = {
++ .name = "leds-st1202",
++ .of_match_table = of_match_ptr(st1202_dt_ids),
++ },
++ .probe = st1202_probe,
++ .id_table = st1202_id,
++};
++module_i2c_driver(st1202_driver);
++
++MODULE_AUTHOR("Remote Tech LTD");
++MODULE_DESCRIPTION("STMicroelectronics LED1202 : 12-channel constant current LED driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From c72e455b89f216b43cd0dbb518036ec4c98f5c46 Mon Sep 17 00:00:00 2001
+From: Manuel Fombuena <fombuena@outlook.com>
+Date: Tue, 25 Feb 2025 22:01:02 +0000
+Subject: leds: leds-st1202: Fix NULL pointer access on race condition
+
+st1202_dt_init() calls devm_led_classdev_register_ext() before the
+internal data structures are properly set up, so the LEDs become visible
+to user space while being partially initialized, leading to a window
+where trying to access them causes a NULL pointer access.
+
+Move devm_led_classdev_register_ext() from DT initialization
+to the end of the probe function when DT and hardware are fully
+initialized and ready to interact with user space.
+
+Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
+Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
+Link: https://lore.kernel.org/r/CWLP123MB54732771AC0CE5491B3C84DCC5C32@CWLP123MB5473.GBRP123.PROD.OUTLOOK.COM
+Signed-off-by: Lee Jones <lee@kernel.org>
+---
+ drivers/leds/leds-st1202.c | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+
+--- a/drivers/leds/leds-st1202.c
++++ b/drivers/leds/leds-st1202.c
+@@ -261,8 +261,6 @@ static int st1202_dt_init(struct st1202_
+ int err, reg;
+
+ for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
+- struct led_init_data init_data = {};
+-
+ err = of_property_read_u32(child, "reg", ®);
+ if (err)
+ return dev_err_probe(dev, err, "Invalid register\n");
+@@ -276,15 +274,6 @@ static int st1202_dt_init(struct st1202_
+ led->led_cdev.pattern_set = st1202_led_pattern_set;
+ led->led_cdev.pattern_clear = st1202_led_pattern_clear;
+ led->led_cdev.default_trigger = "pattern";
+-
+- init_data.fwnode = led->fwnode;
+- init_data.devicename = "st1202";
+- init_data.default_label = ":";
+-
+- err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
+- if (err < 0)
+- return dev_err_probe(dev, err, "Failed to register LED class device\n");
+-
+ led->led_cdev.brightness_set = st1202_brightness_set;
+ led->led_cdev.brightness_get = st1202_brightness_get;
+ }
+@@ -368,6 +357,7 @@ static int st1202_probe(struct i2c_clien
+ return ret;
+
+ for (int i = 0; i < ST1202_MAX_LEDS; i++) {
++ struct led_init_data init_data = {};
+ led = &chip->leds[i];
+ led->chip = chip;
+ led->led_num = i;
+@@ -384,6 +374,15 @@ static int st1202_probe(struct i2c_clien
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to clear LED pattern\n");
++
++ init_data.fwnode = led->fwnode;
++ init_data.devicename = "st1202";
++ init_data.default_label = ":";
++
++ ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
++ if (ret < 0)
++ return dev_err_probe(&client->dev, ret,
++ "Failed to register LED class device\n");
+ }
+
+ return 0;
+++ /dev/null
-From 71e5da46e78c1cd24e2feed251a2845327447ad8 Mon Sep 17 00:00:00 2001
-From: Kees Cook <kees@kernel.org>
-Date: Wed, 21 May 2025 23:27:04 +0200
-Subject: wireguard: global: add __nonstring annotations for unterminated
- strings
-
-When a character array without a terminating NUL character has a static
-initializer, GCC 15's -Wunterminated-string-initialization will only
-warn if the array lacks the "nonstring" attribute[1]. Mark the arrays
-with __nonstring to correctly identify the char array as "not a C string"
-and thereby eliminate the warning:
-
-../drivers/net/wireguard/cookie.c:29:56: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Wunterminated-string-initialization]
- 29 | static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----";
- | ^~~~~~~~~~
-../drivers/net/wireguard/cookie.c:30:58: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (9 chars into 8 available) [-Wunterminated-string-initialization]
- 30 | static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--";
- | ^~~~~~~~~~
-../drivers/net/wireguard/noise.c:28:38: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (38 chars into 37 available) [-Wunterminated-string-initialization]
- 28 | static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
- | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-../drivers/net/wireguard/noise.c:29:39: warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (35 chars into 34 available) [-Wunterminated-string-initialization]
- 29 | static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
- | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The arrays are always used with their fixed size, so use __nonstring.
-
-Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117178 [1]
-Signed-off-by: Kees Cook <kees@kernel.org>
-Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-Link: https://patch.msgid.link/20250521212707.1767879-3-Jason@zx2c4.com
-Signed-off-by: Paolo Abeni <pabeni@redhat.com>
----
- drivers/net/wireguard/cookie.c | 4 ++--
- drivers/net/wireguard/noise.c | 4 ++--
- 2 files changed, 4 insertions(+), 4 deletions(-)
-
---- a/drivers/net/wireguard/cookie.c
-+++ b/drivers/net/wireguard/cookie.c
-@@ -26,8 +26,8 @@ void wg_cookie_checker_init(struct cooki
- }
-
- enum { COOKIE_KEY_LABEL_LEN = 8 };
--static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----";
--static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--";
-+static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "mac1----";
-+static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "cookie--";
-
- static void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN],
- const u8 pubkey[NOISE_PUBLIC_KEY_LEN],
---- a/drivers/net/wireguard/noise.c
-+++ b/drivers/net/wireguard/noise.c
-@@ -25,8 +25,8 @@
- * <- e, ee, se, psk, {}
- */
-
--static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
--static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
-+static const u8 handshake_name[37] __nonstring = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
-+static const u8 identifier_name[34] __nonstring = "WireGuard v1 zx2c4 Jason@zx2c4.com";
- static u8 handshake_init_hash[NOISE_HASH_LEN] __ro_after_init;
- static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __ro_after_init;
- static atomic64_t keypair_counter = ATOMIC64_INIT(0);
+++ /dev/null
-From f6dffe2a9ed1bdcee1879e2728310fb1e08602cf Mon Sep 17 00:00:00 2001
-From: Mikhail Zhilkin <csharper2005@gmail.com>
-Date: Thu, 27 Nov 2025 22:59:00 +0300
-Subject: mtd: spinand: add support for FudanMicro FM25S01BI3
-
-Add support for FudanMicro FM25S01BI3 SPI NAND.
-
-Link: https://www.fmsh.com/nvm/FM25S01BI3_ds_eng.pdf
-
-Signed-off-by: Mikhail Zhilkin <csharper2005@gmail.com>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- drivers/mtd/nand/spi/fmsh.c | 72 +++++++++++++++++++++++++++++++++++++
- 1 file changed, 72 insertions(+)
-
---- a/drivers/mtd/nand/spi/fmsh.c
-+++ b/drivers/mtd/nand/spi/fmsh.c
-@@ -9,6 +9,13 @@
- #include <linux/kernel.h>
- #include <linux/mtd/spinand.h>
-
-+#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
-+ #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
-+ #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
-+ #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4)
-+ #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4)
-+ #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4)
-+
- #define SPINAND_MFR_FMSH 0xA1
-
- static SPINAND_OP_VARIANTS(read_cache_variants,
-@@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struc
- return 0;
- }
-
-+static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
-+ u8 status)
-+{
-+ switch (status & FM25S01BI3_STATUS_ECC_MASK) {
-+ case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
-+ return 0;
-+
-+ case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
-+ return -EBADMSG;
-+
-+ case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
-+ return 3;
-+
-+ case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
-+ return 6;
-+
-+ case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
-+ return 8;
-+
-+ default:
-+ break;
-+ }
-+
-+ return -EINVAL;
-+}
-+
-+static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ if (section)
-+ return -ERANGE;
-+
-+ region->offset = 64;
-+ region->length = 64;
-+
-+ return 0;
-+}
-+
-+static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *region)
-+{
-+ if (section > 3)
-+ return -ERANGE;
-+
-+ region->offset = (16 * section) + 4;
-+ region->length = 12;
-+
-+ return 0;
-+}
-+
- static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
- .ecc = fm25s01a_ooblayout_ecc,
- .free = fm25s01a_ooblayout_free,
- };
-
-+static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
-+ .ecc = fm25s01bi3_ooblayout_ecc,
-+ .free = fm25s01bi3_ooblayout_free,
-+};
-+
- static const struct spinand_info fmsh_spinand_table[] = {
- SPINAND_INFO("FM25S01A",
- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
-@@ -60,6 +122,16 @@ static const struct spinand_info fmsh_sp
- &update_cache_variants),
- 0,
- SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
-+ SPINAND_INFO("FM25S01BI3",
-+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
-+ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
-+ NAND_ECCREQ(8, 512),
-+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-+ &write_cache_variants,
-+ &update_cache_variants),
-+ SPINAND_HAS_QE_BIT,
-+ SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
-+ fm25s01bi3_ecc_get_status)),
- };
-
- static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
+++ /dev/null
-From e4a0cf9f1d90e6888e5373da3314f761024f6c97 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 18 Sep 2025 00:53:59 +0300
-Subject: mtd: spinand: fix direct mapping creation sizes
-
-Continuous mode is only supported for data reads, thus writing
-requires only single flash page mapping.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- drivers/mtd/nand/spi/core.c | 14 +++++++-------
- 1 file changed, 7 insertions(+), 7 deletions(-)
-
---- a/drivers/mtd/nand/spi/core.c
-+++ b/drivers/mtd/nand/spi/core.c
-@@ -1028,18 +1028,13 @@ static int spinand_create_dirmap(struct
- unsigned int plane)
- {
- struct nand_device *nand = spinand_to_nand(spinand);
-- struct spi_mem_dirmap_info info = {
-- .length = nanddev_page_size(nand) +
-- nanddev_per_page_oobsize(nand),
-- };
-+ struct spi_mem_dirmap_info info = { 0 };
- struct spi_mem_dirmap_desc *desc;
-
-- if (spinand->cont_read_possible)
-- info.length = nanddev_eraseblock_size(nand);
--
- /* The plane number is passed in MSB just above the column address */
- info.offset = plane << fls(nand->memorg.pagesize);
-
-+ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates.update_cache;
- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
- spinand->spimem, &info);
-@@ -1048,6 +1043,8 @@ static int spinand_create_dirmap(struct
-
- spinand->dirmaps[plane].wdesc = desc;
-
-+ if (spinand->cont_read_possible)
-+ info.length = nanddev_eraseblock_size(nand);
- info.op_tmpl = *spinand->op_templates.read_cache;
- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
- spinand->spimem, &info);
-@@ -1063,6 +1060,7 @@ static int spinand_create_dirmap(struct
- return 0;
- }
-
-+ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates.update_cache;
- info.op_tmpl.data.ecc = true;
- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
-@@ -1072,6 +1070,8 @@ static int spinand_create_dirmap(struct
-
- spinand->dirmaps[plane].wdesc_ecc = desc;
-
-+ if (spinand->cont_read_possible)
-+ info.length = nanddev_eraseblock_size(nand);
- info.op_tmpl = *spinand->op_templates.read_cache;
- info.op_tmpl.data.ecc = true;
- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+++ /dev/null
-From 004f8ea0d9917398aabff7388b3bf62a84a4088b Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 18 Sep 2025 00:54:00 +0300
-Subject: mtd: spinand: try a regular dirmap if creating a dirmap for
- continuous reading fails
-
-Continuous reading may result in multiple flash pages reading in one
-operation. Typically only one flash page has read/written (a little bit
-more than 2-4 Kb), but continuous reading requires the spi controller
-to read up to 512 Kb in one operation without toggling CS in beetween.
-
-Roughly speaking spi controllers can be divided on 2 categories:
- * spi controllers without dirmap acceleration support
- * spi controllers with dirmap acceleration support
-
-Firt of them will have issues with continuous reading if restriction on
-the transfer length is implemented in the adjust_op_size() handler.
-Second group often supports acceleration of single page only reading.
-Thus enabling of continuous reading can break flash reading.
-
-This patch tries to create dirmap for continuous reading first and
-fallback to regular reading if spi controller refuses to create it.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- drivers/mtd/nand/spi/core.c | 43 ++++++++++++++++++++++++++++++-------
- 1 file changed, 35 insertions(+), 8 deletions(-)
-
---- a/drivers/mtd/nand/spi/core.c
-+++ b/drivers/mtd/nand/spi/core.c
-@@ -1024,6 +1024,39 @@ static int spinand_mtd_block_isreserved(
- return ret;
- }
-
-+static struct spi_mem_dirmap_desc *spinand_create_rdesc(
-+ struct spinand_device *spinand,
-+ struct spi_mem_dirmap_info *info)
-+{
-+ struct nand_device *nand = spinand_to_nand(spinand);
-+ struct spi_mem_dirmap_desc *desc = NULL;
-+
-+ if (spinand->cont_read_possible) {
-+ /*
-+ * spi controller may return an error if info->length is
-+ * too large
-+ */
-+ info->length = nanddev_eraseblock_size(nand);
-+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
-+ spinand->spimem, info);
-+ }
-+
-+ if (IS_ERR_OR_NULL(desc)) {
-+ /*
-+ * continuous reading is not supported by flash or
-+ * its spi controller, use regular reading
-+ */
-+ spinand->cont_read_possible = false;
-+
-+ info->length = nanddev_page_size(nand) +
-+ nanddev_per_page_oobsize(nand);
-+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
-+ spinand->spimem, info);
-+ }
-+
-+ return desc;
-+}
-+
- static int spinand_create_dirmap(struct spinand_device *spinand,
- unsigned int plane)
- {
-@@ -1043,11 +1076,8 @@ static int spinand_create_dirmap(struct
-
- spinand->dirmaps[plane].wdesc = desc;
-
-- if (spinand->cont_read_possible)
-- info.length = nanddev_eraseblock_size(nand);
- info.op_tmpl = *spinand->op_templates.read_cache;
-- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
-- spinand->spimem, &info);
-+ desc = spinand_create_rdesc(spinand, &info);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
-
-@@ -1070,12 +1100,9 @@ static int spinand_create_dirmap(struct
-
- spinand->dirmaps[plane].wdesc_ecc = desc;
-
-- if (spinand->cont_read_possible)
-- info.length = nanddev_eraseblock_size(nand);
- info.op_tmpl = *spinand->op_templates.read_cache;
- info.op_tmpl.data.ecc = true;
-- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
-- spinand->spimem, &info);
-+ desc = spinand_create_rdesc(spinand, &info);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
-
+++ /dev/null
-From 010dc7f2dd6a0078ade3f88f627ed5fbf45ceb94 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 18 Sep 2025 00:54:01 +0300
-Subject: mtd: spinand: repeat reading in regular mode if continuous reading
- fails
-
-Continuous reading may result in multiple flash pages reading in one
-operation. Unfortunately, not all spinand controllers support such
-large reading. They will read less data. Unfortunately, the operation
-can't be continued.
-
-In this case:
- * disable continuous reading on this (not good enough) spi controller
- * repeat reading in regular mode.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- drivers/mtd/nand/spi/core.c | 25 +++++++++++++++++++++----
- 1 file changed, 21 insertions(+), 4 deletions(-)
-
---- a/drivers/mtd/nand/spi/core.c
-+++ b/drivers/mtd/nand/spi/core.c
-@@ -427,8 +427,16 @@ static int spinand_read_from_cache_op(st
- * Dirmap accesses are allowed to toggle the CS.
- * Toggling the CS during a continuous read is forbidden.
- */
-- if (nbytes && req->continuous)
-- return -EIO;
-+ if (nbytes && req->continuous) {
-+ /*
-+ * Spi controller with broken support of continuous
-+ * reading was detected. Disable future use of
-+ * continuous reading and return -EAGAIN to retry
-+ * reading within regular mode.
-+ */
-+ spinand->cont_read_possible = false;
-+ return -EAGAIN;
-+ }
- }
-
- if (req->datalen)
-@@ -841,10 +849,19 @@ static int spinand_mtd_read(struct mtd_i
-
- old_stats = mtd->ecc_stats;
-
-- if (spinand_use_cont_read(mtd, from, ops))
-+ if (spinand_use_cont_read(mtd, from, ops)) {
- ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips);
-- else
-+ if (ret == -EAGAIN && !spinand->cont_read_possible) {
-+ /*
-+ * Spi controller with broken support of continuous
-+ * reading was detected (see spinand_read_from_cache_op()),
-+ * repeat reading in regular mode.
-+ */
-+ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
-+ }
-+ } else {
- ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
-+ }
-
- if (ops->stats) {
- ops->stats->uncorrectable_errors +=
+++ /dev/null
-From 65cb56d49f6edea409600a3c61effc70ee5d43d8 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Thu, 1 May 2025 18:19:16 +0200
-Subject: spi: spi-qpic-snand: validate user/chip specific ECC properties
-
-The driver only supports 512 bytes ECC step size and 4 bit ECC strength
-at the moment, however it does not reject unsupported step/strength
-configurations. Due to this, whenever the driver is used with a flash
-chip which needs stronger ECC protection, the following warning is shown
-in the kernel log:
-
- [ 0.574648] spi-nand spi0.0: GigaDevice SPI NAND was found.
- [ 0.635748] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
- [ 0.649079] nand: WARNING: (null): the ECC used on your system is too weak compared to the one required by the NAND chip
-
-Although the message indicates that something is wrong, but it often gets
-unnoticed, which can cause serious problems. For example when the user
-writes something into the flash chip despite the warning, the written data
-may won't be readable by the bootloader or by the boot ROM. In the worst
-case, when the attached SPI NAND chip is the boot device, the board may not
-be able to boot anymore.
-
-Also, it is not even possible to create a backup of the flash, because
-reading its content results in bogus data. For example, dumping the first
-page of the flash gives this:
-
- # hexdump -C -n 2048 /dev/mtd0
- 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000040 0f 0f 0f 0f 0f 0f 0f 0d 0f 0f 0f 0f 0f 0f 0f 0f |................|
- 00000050 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 000001c0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- 000001d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000200 0f 0f 0f 0f f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.....[..........|
- 00000210 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 000002f0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 1f 0f 0f |................|
- 00000300 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 000003c0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ff 0f 0f 0f |................|
- 000003d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000400 0f 0f 0f 0f 0f 0f 0f 0f e9 74 c9 06 f5 5b ff ff |.........t...[..|
- 00000410 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 000005d0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- 000005e0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000600 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f c6 be 0f c3 |................|
- 00000610 e9 74 c9 06 f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.t...[..........|
- 00000620 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000770 0f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- 00000780 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000800
- #
-
-Doing the same by using the downstream kernel results in different output:
-
- # hexdump -C -n 2048 /dev/mtd0
- 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................|
- *
- 00000800
- #
-
-This patch adds some sanity checks to the code to prevent using the driver
-with unsupported ECC step/strength configurations. After the change, probing
-of the driver fails in such cases:
-
- [ 0.655038] spi-nand spi0.0: GigaDevice SPI NAND was found.
- [ 0.659159] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
- [ 0.669138] qcom_snand 79b0000.spi: only 4 bits ECC strength is supported
- [ 0.677476] nand: No suitable ECC configuration
- [ 0.689909] spi-nand spi0.0: probe with driver spi-nand failed with error -95
-
-This helps to avoid the aforementioned hassles until support for 8 bit ECC
-strength gets implemented.
-
-Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface")
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250501-qpic-snand-validate-ecc-v1-1-532776581a66@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- drivers/spi/spi-qpic-snand.c | 42 +++++++++++++++++++++++++++++++-----
- 1 file changed, 37 insertions(+), 5 deletions(-)
-
---- a/drivers/spi/spi-qpic-snand.c
-+++ b/drivers/spi/spi-qpic-snand.c
-@@ -249,9 +249,11 @@ static const struct mtd_ooblayout_ops qc
- static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
- {
- struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
-+ struct nand_ecc_props *reqs = &nand->ecc.requirements;
-+ struct nand_ecc_props *user = &nand->ecc.user_conf;
- struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
- struct mtd_info *mtd = nanddev_to_mtd(nand);
-- int cwperpage, bad_block_byte;
-+ int cwperpage, bad_block_byte, ret;
- struct qpic_ecc *ecc_cfg;
-
- cwperpage = mtd->writesize / NANDC_STEP_SIZE;
-@@ -260,11 +262,39 @@ static int qcom_spi_ecc_init_ctx_pipelin
- ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL);
- if (!ecc_cfg)
- return -ENOMEM;
-+
-+ if (user->step_size && user->strength) {
-+ ecc_cfg->step_size = user->step_size;
-+ ecc_cfg->strength = user->strength;
-+ } else if (reqs->step_size && reqs->strength) {
-+ ecc_cfg->step_size = reqs->step_size;
-+ ecc_cfg->strength = reqs->strength;
-+ } else {
-+ /* use defaults */
-+ ecc_cfg->step_size = NANDC_STEP_SIZE;
-+ ecc_cfg->strength = 4;
-+ }
-+
-+ if (ecc_cfg->step_size != NANDC_STEP_SIZE) {
-+ dev_err(snandc->dev,
-+ "only %u bytes ECC step size is supported\n",
-+ NANDC_STEP_SIZE);
-+ ret = -EOPNOTSUPP;
-+ goto err_free_ecc_cfg;
-+ }
-+
-+ if (ecc_cfg->strength != 4) {
-+ dev_err(snandc->dev,
-+ "only 4 bits ECC strength is supported\n");
-+ ret = -EOPNOTSUPP;
-+ goto err_free_ecc_cfg;
-+ }
-+
- snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize,
- GFP_KERNEL);
- if (!snandc->qspi->oob_buf) {
-- kfree(ecc_cfg);
-- return -ENOMEM;
-+ ret = -ENOMEM;
-+ goto err_free_ecc_cfg;
- }
-
- memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize);
-@@ -279,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelin
- ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
-
- ecc_cfg->steps = 4;
-- ecc_cfg->strength = 4;
-- ecc_cfg->step_size = 512;
- ecc_cfg->cw_data = 516;
- ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
- bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1;
-@@ -338,6 +366,10 @@ static int qcom_spi_ecc_init_ctx_pipelin
- ecc_cfg->strength, ecc_cfg->step_size);
-
- return 0;
-+
-+err_free_ecc_cfg:
-+ kfree(ecc_cfg);
-+ return ret;
- }
-
- static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
+++ /dev/null
-From 0dc7e656ddd54c3267b7cc18c1ac8ec1297ed02f Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Wed, 2 Jul 2025 14:35:23 +0200
-Subject: mtd: nand: qpic-common: add defines for ECC_MODE values
-
-Add defines for the values of the ECC_MODE field of the NAND_DEV0_ECC_CFG
-register and change both the 'qcom-nandc' and 'spi-qpic-snand' drivers to
-use those instead of magic numbers.
-
-No functional changes. This is in preparation for adding 8 bit ECC strength
-support for the 'spi-qpic-snand' driver.
-
-Reviewed-by: Md Sadre Alam <quic_mdalam@quicinc.com>
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
-Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-1-ae2c17a30bb7@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- drivers/mtd/nand/raw/qcom_nandc.c | 6 +++---
- drivers/spi/spi-qpic-snand.c | 2 +-
- include/linux/mtd/nand-qpic-common.h | 2 ++
- 3 files changed, 6 insertions(+), 4 deletions(-)
-
---- a/drivers/mtd/nand/raw/qcom_nandc.c
-+++ b/drivers/mtd/nand/raw/qcom_nandc.c
-@@ -1379,7 +1379,7 @@ static int qcom_nand_attach_chip(struct
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- int cwperpage, bad_block_byte, ret;
- bool wide_bus;
-- int ecc_mode = 1;
-+ int ecc_mode = ECC_MODE_8BIT;
-
- /* controller only supports 512 bytes data steps */
- ecc->size = NANDC_STEP_SIZE;
-@@ -1400,7 +1400,7 @@ static int qcom_nand_attach_chip(struct
- if (ecc->strength >= 8) {
- /* 8 bit ECC defaults to BCH ECC on all platforms */
- host->bch_enabled = true;
-- ecc_mode = 1;
-+ ecc_mode = ECC_MODE_8BIT;
-
- if (wide_bus) {
- host->ecc_bytes_hw = 14;
-@@ -1420,7 +1420,7 @@ static int qcom_nand_attach_chip(struct
- if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
- /* BCH */
- host->bch_enabled = true;
-- ecc_mode = 0;
-+ ecc_mode = ECC_MODE_4BIT;
-
- if (wide_bus) {
- host->ecc_bytes_hw = 8;
---- a/drivers/spi/spi-qpic-snand.c
-+++ b/drivers/spi/spi-qpic-snand.c
-@@ -349,7 +349,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
- FIELD_PREP(ECC_SW_RESET, 0) |
- FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
- FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
-- FIELD_PREP(ECC_MODE_MASK, 0) |
-+ FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) |
- FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
-
- ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;
---- a/include/linux/mtd/nand-qpic-common.h
-+++ b/include/linux/mtd/nand-qpic-common.h
-@@ -101,6 +101,8 @@
- #define ECC_SW_RESET BIT(1)
- #define ECC_MODE 4
- #define ECC_MODE_MASK GENMASK(5, 4)
-+#define ECC_MODE_4BIT 0
-+#define ECC_MODE_8BIT 1
- #define ECC_PARITY_SIZE_BYTES_BCH 8
- #define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8)
- #define ECC_NUM_DATA_BYTES 16
+++ /dev/null
-From 913bf8d50cbd144c87e9660b591781179182ff59 Mon Sep 17 00:00:00 2001
-From: Gabor Juhos <j4g8y7@gmail.com>
-Date: Wed, 2 Jul 2025 14:35:24 +0200
-Subject: spi: spi-qpic-snand: add support for 8 bits ECC strength
-
-Even though the hardware supports 8 bits ECC strength, but that is not
-handled in the driver yet. This change adds the missing bits in order
-to allow using the driver with chips which require 8 bits ECC strength.
-
-No functional changes intended with regard to the existing 4 bits ECC
-strength support.
-
-Tested on an IPQ9574 platform using a GigaDevice GD5F2GM7REYIG chip.
-
-Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
-Link: https://patch.msgid.link/20250702-qpic-snand-8bit-ecc-v2-2-ae2c17a30bb7@gmail.com
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- drivers/spi/spi-qpic-snand.c | 21 ++++++++++++++++-----
- 1 file changed, 16 insertions(+), 5 deletions(-)
-
---- a/drivers/spi/spi-qpic-snand.c
-+++ b/drivers/spi/spi-qpic-snand.c
-@@ -283,9 +283,22 @@ static int qcom_spi_ecc_init_ctx_pipelin
- goto err_free_ecc_cfg;
- }
-
-- if (ecc_cfg->strength != 4) {
-+ switch (ecc_cfg->strength) {
-+ case 4:
-+ ecc_cfg->ecc_mode = ECC_MODE_4BIT;
-+ ecc_cfg->ecc_bytes_hw = 7;
-+ ecc_cfg->spare_bytes = 4;
-+ break;
-+
-+ case 8:
-+ ecc_cfg->ecc_mode = ECC_MODE_8BIT;
-+ ecc_cfg->ecc_bytes_hw = 13;
-+ ecc_cfg->spare_bytes = 2;
-+ break;
-+
-+ default:
- dev_err(snandc->dev,
-- "only 4 bits ECC strength is supported\n");
-+ "only 4 or 8 bits ECC strength is supported\n");
- ret = -EOPNOTSUPP;
- goto err_free_ecc_cfg;
- }
-@@ -302,8 +315,6 @@ static int qcom_spi_ecc_init_ctx_pipelin
- nand->ecc.ctx.priv = ecc_cfg;
- snandc->qspi->mtd = mtd;
-
-- ecc_cfg->ecc_bytes_hw = 7;
-- ecc_cfg->spare_bytes = 4;
- ecc_cfg->bbm_size = 1;
- ecc_cfg->bch_enabled = true;
- ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
-@@ -349,7 +360,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
- FIELD_PREP(ECC_SW_RESET, 0) |
- FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
- FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
-- FIELD_PREP(ECC_MODE_MASK, ECC_MODE_4BIT) |
-+ FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) |
- FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
-
- ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;
+++ /dev/null
-From 599b92fd0efa8b7c43e7f58c9dd0f7951f7cbf09 Mon Sep 17 00:00:00 2001
-From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-Date: Wed, 18 Dec 2024 18:33:58 +0000
-Subject: dt-bindings: leds: Add LED1202 LED Controller
-
-The LED1202 is a 12-channel low quiescent current LED driver with:
- * Supply range from 2.6 V to 5 V
- * 20 mA current capability per channel
- * 1.8 V compatible I2C control interface
- * 8-bit analog dimming individual control
- * 12-bit local PWM resolution
- * 8 programmable patterns
-
-If the led node is present in the controller then the channel is
-set to active.
-
-Signed-off-by: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Link: https://lore.kernel.org/r/20241218183401.41687-3-vicentiu.galanopulo@remote-tech.co.uk
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- .../devicetree/bindings/leds/st,led1202.yaml | 132 ++++++++++++++++++
- 1 file changed, 132 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/leds/st,led1202.yaml
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/leds/st,led1202.yaml
-@@ -0,0 +1,132 @@
-+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/leds/st,led1202.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: ST LED1202 LED controllers
-+
-+maintainers:
-+ - Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-+
-+description: |
-+ The LED1202 is a 12-channel low quiescent current LED controller
-+ programmable via I2C; The output current can be adjusted separately
-+ for each channel by 8-bit analog and 12-bit digital dimming control.
-+ Datasheet available at
-+ https://www.st.com/en/power-management/led1202.html
-+
-+properties:
-+ compatible:
-+ const: st,led1202
-+
-+ reg:
-+ maxItems: 1
-+
-+ "#address-cells":
-+ const: 1
-+
-+ "#size-cells":
-+ const: 0
-+
-+patternProperties:
-+ "^led@[0-9a-f]$":
-+ type: object
-+ $ref: common.yaml#
-+ unevaluatedProperties: false
-+
-+ properties:
-+ reg:
-+ minimum: 0
-+ maximum: 11
-+
-+ required:
-+ - reg
-+
-+required:
-+ - compatible
-+ - reg
-+ - "#address-cells"
-+ - "#size-cells"
-+
-+additionalProperties: false
-+
-+examples:
-+ - |
-+ #include <dt-bindings/leds/common.h>
-+
-+ i2c {
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ led-controller@58 {
-+ compatible = "st,led1202";
-+ reg = <0x58>;
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ led@0 {
-+ reg = <0x0>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_RED>;
-+ function-enumerator = <1>;
-+ };
-+
-+ led@1 {
-+ reg = <0x1>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_GREEN>;
-+ function-enumerator = <2>;
-+ };
-+
-+ led@2 {
-+ reg = <0x2>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_BLUE>;
-+ function-enumerator = <3>;
-+ };
-+
-+ led@3 {
-+ reg = <0x3>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_RED>;
-+ function-enumerator = <4>;
-+ };
-+
-+ led@4 {
-+ reg = <0x4>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_GREEN>;
-+ function-enumerator = <5>;
-+ };
-+
-+ led@5 {
-+ reg = <0x5>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_BLUE>;
-+ function-enumerator = <6>;
-+ };
-+
-+ led@6 {
-+ reg = <0x6>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_RED>;
-+ function-enumerator = <7>;
-+ };
-+
-+ led@7 {
-+ reg = <0x7>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_GREEN>;
-+ function-enumerator = <8>;
-+ };
-+
-+ led@8 {
-+ reg = <0x8>;
-+ function = LED_FUNCTION_STATUS;
-+ color = <LED_COLOR_ID_BLUE>;
-+ function-enumerator = <9>;
-+ };
-+ };
-+ };
-+...
+++ /dev/null
-From 939757aafeb9c266dda37657ee5f7a73ffd35ae2 Mon Sep 17 00:00:00 2001
-From: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-Date: Wed, 18 Dec 2024 18:33:59 +0000
-Subject: leds: Add LED1202 I2C driver
-
-The output current can be adjusted separately for each channel by 8-bit
-analog (current sink input) and 12-bit digital (PWM) dimming control. The
-LED1202 implements 12 low-side current generators with independent dimming
-control.
-Internal volatile memory allows the user to store up to 8 different patterns,
-each pattern is a particular output configuration in terms of PWM
-duty-cycle (on 4096 steps). Analog dimming (on 256 steps) is per channel but
-common to all patterns. Each device tree LED node will have a corresponding
-entry in /sys/class/leds with the label name. The brightness property
-corresponds to the per channel analog dimming, while the patterns[1-8] to the
-PWM dimming control.
-
-Signed-off-by: Vicentiu Galanopulo <vicentiu.galanopulo@remote-tech.co.uk>
-Link: https://lore.kernel.org/r/20241218183401.41687-4-vicentiu.galanopulo@remote-tech.co.uk
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/Kconfig | 10 +
- drivers/leds/Makefile | 1 +
- drivers/leds/leds-st1202.c | 416 +++++++++++++++++++++++++++++++++++++
- 3 files changed, 427 insertions(+)
- create mode 100644 drivers/leds/leds-st1202.c
-
---- a/drivers/leds/Kconfig
-+++ b/drivers/leds/Kconfig
-@@ -931,6 +931,16 @@ config LEDS_LM36274
- Say Y to enable the LM36274 LED driver for TI LMU devices.
- This supports the LED device LM36274.
-
-+config LEDS_ST1202
-+ tristate "LED Support for STMicroelectronics LED1202 I2C chips"
-+ depends on LEDS_CLASS
-+ depends on I2C
-+ depends on OF
-+ select LEDS_TRIGGERS
-+ help
-+ Say Y to enable support for LEDs connected to LED1202
-+ LED driver chips accessed via the I2C bus.
-+
- config LEDS_TPS6105X
- tristate "LED support for TI TPS6105X"
- depends on LEDS_CLASS
---- a/drivers/leds/Makefile
-+++ b/drivers/leds/Makefile
-@@ -81,6 +81,7 @@ obj-$(CONFIG_LEDS_POWERNV) += leds-powe
- obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
- obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
- obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
-+obj-$(CONFIG_LEDS_ST1202) += leds-st1202.o
- obj-$(CONFIG_LEDS_SUN50I_A100) += leds-sun50i-a100.o
- obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
- obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
---- /dev/null
-+++ b/drivers/leds/leds-st1202.c
-@@ -0,0 +1,416 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * LED driver for STMicroelectronics LED1202 chip
-+ *
-+ * Copyright (C) 2024 Remote-Tech Ltd. UK
-+ */
-+
-+#include <linux/cleanup.h>
-+#include <linux/ctype.h>
-+#include <linux/delay.h>
-+#include <linux/err.h>
-+#include <linux/gpio.h>
-+#include <linux/i2c.h>
-+#include <linux/leds.h>
-+#include <linux/module.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+
-+#define ST1202_CHAN_DISABLE_ALL 0x00
-+#define ST1202_CHAN_ENABLE_HIGH 0x03
-+#define ST1202_CHAN_ENABLE_LOW 0x02
-+#define ST1202_CONFIG_REG 0x04
-+/* PATS: Pattern sequence feature enable */
-+#define ST1202_CONFIG_REG_PATS BIT(7)
-+/* PATSR: Pattern sequence runs (self-clear when sequence is finished) */
-+#define ST1202_CONFIG_REG_PATSR BIT(6)
-+#define ST1202_CONFIG_REG_SHFT BIT(3)
-+#define ST1202_DEV_ENABLE 0x01
-+#define ST1202_DEV_ENABLE_ON BIT(0)
-+#define ST1202_DEV_ENABLE_RESET BIT(7)
-+#define ST1202_DEVICE_ID 0x00
-+#define ST1202_ILED_REG0 0x09
-+#define ST1202_MAX_LEDS 12
-+#define ST1202_MAX_PATTERNS 8
-+#define ST1202_MILLIS_PATTERN_DUR_MAX 5660
-+#define ST1202_MILLIS_PATTERN_DUR_MIN 22
-+#define ST1202_PATTERN_DUR 0x16
-+#define ST1202_PATTERN_PWM 0x1E
-+#define ST1202_PATTERN_REP 0x15
-+
-+struct st1202_led {
-+ struct fwnode_handle *fwnode;
-+ struct led_classdev led_cdev;
-+ struct st1202_chip *chip;
-+ bool is_active;
-+ int led_num;
-+};
-+
-+struct st1202_chip {
-+ struct i2c_client *client;
-+ struct mutex lock;
-+ struct st1202_led leds[ST1202_MAX_LEDS];
-+};
-+
-+static struct st1202_led *cdev_to_st1202_led(struct led_classdev *cdev)
-+{
-+ return container_of(cdev, struct st1202_led, led_cdev);
-+}
-+
-+static int st1202_read_reg(struct st1202_chip *chip, int reg, uint8_t *val)
-+{
-+ struct device *dev = &chip->client->dev;
-+ int ret;
-+
-+ ret = i2c_smbus_read_byte_data(chip->client, reg);
-+ if (ret < 0) {
-+ dev_err(dev, "Failed to read register [0x%x]: %d\n", reg, ret);
-+ return ret;
-+ }
-+
-+ *val = (uint8_t)ret;
-+ return 0;
-+}
-+
-+static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
-+{
-+ struct device *dev = &chip->client->dev;
-+ int ret;
-+
-+ ret = i2c_smbus_write_byte_data(chip->client, reg, val);
-+ if (ret != 0)
-+ dev_err(dev, "Failed to write %d to register [0x%x]: %d\n", val, reg, ret);
-+
-+ return ret;
-+}
-+
-+static uint8_t st1202_prescalar_to_miliseconds(unsigned int value)
-+{
-+ return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1;
-+}
-+
-+static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
-+ int pattern, unsigned int value)
-+{
-+ u8 value_l, value_h;
-+ int ret;
-+
-+ value_l = (u8)value;
-+ value_h = (u8)(value >> 8);
-+
-+ /*
-+ * Datasheet: Register address low = 1Eh + 2*(xh) + 18h*(yh),
-+ * where x is the channel number (led number) in hexadecimal (x = 00h .. 0Bh)
-+ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
-+ */
-+ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + (led_num * 2) + 0x18 * pattern),
-+ value_l);
-+ if (ret != 0)
-+ return ret;
-+
-+ /*
-+ * Datasheet: Register address high = 1Eh + 01h + 2(xh) +18h*(yh),
-+ * where x is the channel number in hexadecimal (x = 00h .. 0Bh)
-+ * and y is the pattern number in hexadecimal (y = 00h .. 07h)
-+ */
-+ ret = st1202_write_reg(chip, (ST1202_PATTERN_PWM + 0x1 + (led_num * 2) + 0x18 * pattern),
-+ value_h);
-+ if (ret != 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern,
-+ unsigned int value)
-+{
-+ return st1202_write_reg(chip, (ST1202_PATTERN_DUR + pattern),
-+ st1202_prescalar_to_miliseconds(value));
-+}
-+
-+static void st1202_brightness_set(struct led_classdev *led_cdev,
-+ enum led_brightness value)
-+{
-+ struct st1202_led *led = cdev_to_st1202_led(led_cdev);
-+ struct st1202_chip *chip = led->chip;
-+
-+ guard(mutex)(&chip->lock);
-+
-+ st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
-+}
-+
-+static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
-+{
-+ struct st1202_led *led = cdev_to_st1202_led(led_cdev);
-+ struct st1202_chip *chip = led->chip;
-+ u8 value = 0;
-+
-+ guard(mutex)(&chip->lock);
-+
-+ st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
-+
-+ return value;
-+}
-+
-+static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
-+{
-+ u8 chan_low, chan_high;
-+ int ret;
-+
-+ guard(mutex)(&chip->lock);
-+
-+ if (led_num <= 7) {
-+ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low);
-+ if (ret < 0)
-+ return ret;
-+
-+ chan_low = active ? chan_low | BIT(led_num) : chan_low & ~BIT(led_num);
-+
-+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, chan_low);
-+ if (ret < 0)
-+ return ret;
-+
-+ } else {
-+ ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_HIGH, &chan_high);
-+ if (ret < 0)
-+ return ret;
-+
-+ chan_high = active ? chan_high | (BIT(led_num) >> 8) :
-+ chan_high & ~(BIT(led_num) >> 8);
-+
-+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, chan_high);
-+ if (ret < 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
-+{
-+ struct st1202_led *led = cdev_to_st1202_led(ldev);
-+ struct st1202_chip *chip = led->chip;
-+
-+ return st1202_channel_set(chip, led->led_num, value == LED_OFF ? false : true);
-+}
-+
-+static int st1202_led_pattern_clear(struct led_classdev *ldev)
-+{
-+ struct st1202_led *led = cdev_to_st1202_led(ldev);
-+ struct st1202_chip *chip = led->chip;
-+ int ret;
-+
-+ guard(mutex)(&chip->lock);
-+
-+ for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
-+ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
-+ if (ret != 0)
-+ return ret;
-+
-+ ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN);
-+ if (ret != 0)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int st1202_led_pattern_set(struct led_classdev *ldev,
-+ struct led_pattern *pattern,
-+ u32 len, int repeat)
-+{
-+ struct st1202_led *led = cdev_to_st1202_led(ldev);
-+ struct st1202_chip *chip = led->chip;
-+ int ret;
-+
-+ if (len > ST1202_MAX_PATTERNS)
-+ return -EINVAL;
-+
-+ guard(mutex)(&chip->lock);
-+
-+ for (int patt = 0; patt < len; patt++) {
-+ if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
-+ pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
-+ return -EINVAL;
-+
-+ ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
-+ if (ret != 0)
-+ return ret;
-+
-+ ret = st1202_duration_pattern_write(chip, patt, pattern[patt].delta_t);
-+ if (ret != 0)
-+ return ret;
-+ }
-+
-+ ret = st1202_write_reg(chip, ST1202_PATTERN_REP, repeat);
-+ if (ret != 0)
-+ return ret;
-+
-+ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR |
-+ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT));
-+ if (ret != 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int st1202_dt_init(struct st1202_chip *chip)
-+{
-+ struct device *dev = &chip->client->dev;
-+ struct st1202_led *led;
-+ int err, reg;
-+
-+ for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
-+ struct led_init_data init_data = {};
-+
-+ err = of_property_read_u32(child, "reg", ®);
-+ if (err)
-+ return dev_err_probe(dev, err, "Invalid register\n");
-+
-+ led = &chip->leds[reg];
-+ led->is_active = true;
-+ led->fwnode = of_fwnode_handle(child);
-+
-+ led->led_cdev.max_brightness = U8_MAX;
-+ led->led_cdev.brightness_set_blocking = st1202_led_set;
-+ led->led_cdev.pattern_set = st1202_led_pattern_set;
-+ led->led_cdev.pattern_clear = st1202_led_pattern_clear;
-+ led->led_cdev.default_trigger = "pattern";
-+
-+ init_data.fwnode = led->fwnode;
-+ init_data.devicename = "st1202";
-+ init_data.default_label = ":";
-+
-+ err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
-+ if (err < 0)
-+ return dev_err_probe(dev, err, "Failed to register LED class device\n");
-+
-+ led->led_cdev.brightness_set = st1202_brightness_set;
-+ led->led_cdev.brightness_get = st1202_brightness_get;
-+ }
-+
-+ return 0;
-+}
-+
-+static int st1202_setup(struct st1202_chip *chip)
-+{
-+ int ret;
-+
-+ guard(mutex)(&chip->lock);
-+
-+ /*
-+ * Once the supply voltage is applied, the LED1202 executes some internal checks,
-+ * afterwords it stops the oscillator and puts the internal LDO in quiescent mode.
-+ * To start the device, EN bit must be set inside the “Device Enable” register at
-+ * address 01h. As soon as EN is set, the LED1202 loads the adjustment parameters
-+ * from the internal non-volatile memory and performs an auto-calibration procedure
-+ * in order to increase the output current precision.
-+ * Such initialization lasts about 6.5 ms.
-+ */
-+
-+ /* Reset the chip during setup */
-+ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_RESET);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Enable phase-shift delay feature */
-+ ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Enable the device */
-+ ret = st1202_write_reg(chip, ST1202_DEV_ENABLE, ST1202_DEV_ENABLE_ON);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* Duration of initialization */
-+ usleep_range(6500, 10000);
-+
-+ /* Deactivate all LEDS (channels) and activate only the ones found in Device Tree */
-+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_LOW, ST1202_CHAN_DISABLE_ALL);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = st1202_write_reg(chip, ST1202_CHAN_ENABLE_HIGH, ST1202_CHAN_DISABLE_ALL);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
-+ ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int st1202_probe(struct i2c_client *client)
-+{
-+ struct st1202_chip *chip;
-+ struct st1202_led *led;
-+ int ret;
-+
-+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-+ return dev_err_probe(&client->dev, -EIO, "SMBUS Byte Data not Supported\n");
-+
-+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
-+ if (!chip)
-+ return -ENOMEM;
-+
-+ devm_mutex_init(&client->dev, &chip->lock);
-+ chip->client = client;
-+
-+ ret = st1202_dt_init(chip);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = st1202_setup(chip);
-+ if (ret < 0)
-+ return ret;
-+
-+ for (int i = 0; i < ST1202_MAX_LEDS; i++) {
-+ led = &chip->leds[i];
-+ led->chip = chip;
-+ led->led_num = i;
-+
-+ if (!led->is_active)
-+ continue;
-+
-+ ret = st1202_channel_set(led->chip, led->led_num, true);
-+ if (ret < 0)
-+ return dev_err_probe(&client->dev, ret,
-+ "Failed to activate LED channel\n");
-+
-+ ret = st1202_led_pattern_clear(&led->led_cdev);
-+ if (ret < 0)
-+ return dev_err_probe(&client->dev, ret,
-+ "Failed to clear LED pattern\n");
-+ }
-+
-+ return 0;
-+}
-+
-+static const struct i2c_device_id st1202_id[] = {
-+ { "st1202-i2c" },
-+ { /* sentinel */ }
-+};
-+MODULE_DEVICE_TABLE(i2c, st1202_id);
-+
-+static const struct of_device_id st1202_dt_ids[] = {
-+ { .compatible = "st,led1202" },
-+ { /* sentinel */ }
-+};
-+MODULE_DEVICE_TABLE(of, st1202_dt_ids);
-+
-+static struct i2c_driver st1202_driver = {
-+ .driver = {
-+ .name = "leds-st1202",
-+ .of_match_table = of_match_ptr(st1202_dt_ids),
-+ },
-+ .probe = st1202_probe,
-+ .id_table = st1202_id,
-+};
-+module_i2c_driver(st1202_driver);
-+
-+MODULE_AUTHOR("Remote Tech LTD");
-+MODULE_DESCRIPTION("STMicroelectronics LED1202 : 12-channel constant current LED driver");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From c72e455b89f216b43cd0dbb518036ec4c98f5c46 Mon Sep 17 00:00:00 2001
-From: Manuel Fombuena <fombuena@outlook.com>
-Date: Tue, 25 Feb 2025 22:01:02 +0000
-Subject: leds: leds-st1202: Fix NULL pointer access on race condition
-
-st1202_dt_init() calls devm_led_classdev_register_ext() before the
-internal data structures are properly set up, so the LEDs become visible
-to user space while being partially initialized, leading to a window
-where trying to access them causes a NULL pointer access.
-
-Move devm_led_classdev_register_ext() from DT initialization
-to the end of the probe function when DT and hardware are fully
-initialized and ready to interact with user space.
-
-Fixes: 259230378c65 ("leds: Add LED1202 I2C driver")
-Signed-off-by: Manuel Fombuena <fombuena@outlook.com>
-Link: https://lore.kernel.org/r/CWLP123MB54732771AC0CE5491B3C84DCC5C32@CWLP123MB5473.GBRP123.PROD.OUTLOOK.COM
-Signed-off-by: Lee Jones <lee@kernel.org>
----
- drivers/leds/leds-st1202.c | 21 ++++++++++-----------
- 1 file changed, 10 insertions(+), 11 deletions(-)
-
---- a/drivers/leds/leds-st1202.c
-+++ b/drivers/leds/leds-st1202.c
-@@ -261,8 +261,6 @@ static int st1202_dt_init(struct st1202_
- int err, reg;
-
- for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
-- struct led_init_data init_data = {};
--
- err = of_property_read_u32(child, "reg", ®);
- if (err)
- return dev_err_probe(dev, err, "Invalid register\n");
-@@ -276,15 +274,6 @@ static int st1202_dt_init(struct st1202_
- led->led_cdev.pattern_set = st1202_led_pattern_set;
- led->led_cdev.pattern_clear = st1202_led_pattern_clear;
- led->led_cdev.default_trigger = "pattern";
--
-- init_data.fwnode = led->fwnode;
-- init_data.devicename = "st1202";
-- init_data.default_label = ":";
--
-- err = devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
-- if (err < 0)
-- return dev_err_probe(dev, err, "Failed to register LED class device\n");
--
- led->led_cdev.brightness_set = st1202_brightness_set;
- led->led_cdev.brightness_get = st1202_brightness_get;
- }
-@@ -368,6 +357,7 @@ static int st1202_probe(struct i2c_clien
- return ret;
-
- for (int i = 0; i < ST1202_MAX_LEDS; i++) {
-+ struct led_init_data init_data = {};
- led = &chip->leds[i];
- led->chip = chip;
- led->led_num = i;
-@@ -384,6 +374,15 @@ static int st1202_probe(struct i2c_clien
- if (ret < 0)
- return dev_err_probe(&client->dev, ret,
- "Failed to clear LED pattern\n");
-+
-+ init_data.fwnode = led->fwnode;
-+ init_data.devicename = "st1202";
-+ init_data.default_label = ":";
-+
-+ ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
-+ if (ret < 0)
-+ return dev_err_probe(&client->dev, ret,
-+ "Failed to register LED class device\n");
- }
-
- return 0;