]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mtd: spinand: List vendor specific operations and make sure they are supported
authorMiquel Raynal <miquel.raynal@bootlin.com>
Fri, 9 Jan 2026 17:18:12 +0000 (18:18 +0100)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Thu, 29 Jan 2026 19:21:41 +0000 (20:21 +0100)
It is probably safe to expect that all SPI controller drivers will ever
support all the most basic SPI NAND operations, such as write enable,
register reads, page program, block erases, etc. However, what about
vendor specific operations? So far nobody complained about it, but as we
are about to introduce octal DTR support, and as none of the SPI NAND
instruction set is defined in any standard, we must remain careful about
these extra operations.

One way to make sure we do not blindly get ourselves in strange
situations with vendor commands failing silently is to make the check
once for all, while probing the chip. However at this stage we have no
such list, so let's add the necessary infrastructure to allow:
- registering vendor operations,
- checking they are actually supported when appropriate.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
drivers/mtd/nand/spi/core.c
include/linux/mtd/spinand.h

index 7146eec51afaae21f08a89cffb27f1226c567e3c..cdf45d054082efa5699a42cfadaa1805300db7bd 100644 (file)
@@ -1424,6 +1424,27 @@ static void spinand_init_ssdr_templates(struct spinand_device *spinand)
        spinand->op_templates = &spinand->ssdr_op_templates;
 }
 
+static int spinand_support_vendor_ops(struct spinand_device *spinand,
+                                     const struct spinand_info *info)
+{
+       int i;
+
+       /*
+        * The vendor ops array is only used in order to verify this chip and all its memory
+        * operations are supported. If we see patterns emerging, we could ideally name these
+        * operations and define them at the SPI NAND core level instead.
+        * For now, this only serves as a sanity check.
+        */
+       for (i = 0; i < info->vendor_ops->nops; i++) {
+               const struct spi_mem_op *op = &info->vendor_ops->ops[i];
+
+               if (!spi_mem_supports_op(spinand->spimem, op))
+                       return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
 static const struct spi_mem_op *
 spinand_select_op_variant(struct spinand_device *spinand,
                          const struct spinand_op_variants *variants)
@@ -1490,6 +1511,7 @@ int spinand_match_and_init(struct spinand_device *spinand,
        u8 *id = spinand->id.data;
        struct nand_device *nand = spinand_to_nand(spinand);
        unsigned int i;
+       int ret;
 
        for (i = 0; i < table_size; i++) {
                const struct spinand_info *info = &table[i];
@@ -1535,6 +1557,10 @@ int spinand_match_and_init(struct spinand_device *spinand,
 
                spinand->ssdr_op_templates.update_cache = op;
 
+               ret = spinand_support_vendor_ops(spinand, info);
+               if (ret)
+                       return ret;
+
                return 0;
        }
 
index 553c56a389d2eededfedae1413b73cac1aa2cd34..b020c119a15d47b16d3d1191c9aaa224acc49ebd 100644 (file)
@@ -494,6 +494,7 @@ struct spinand_user_otp {
  * @op_variants.read_cache: variants of the read-cache operation
  * @op_variants.write_cache: variants of the write-cache operation
  * @op_variants.update_cache: variants of the update-cache operation
+ * @vendor_ops: vendor specific operations
  * @select_target: function used to select a target/die. Required only for
  *                multi-die chips
  * @configure_chip: Align the chip configuration with the core settings
@@ -518,6 +519,7 @@ struct spinand_info {
                const struct spinand_op_variants *write_cache;
                const struct spinand_op_variants *update_cache;
        } op_variants;
+       const struct spinand_op_variants *vendor_ops;
        int (*select_target)(struct spinand_device *spinand,
                             unsigned int target);
        int (*configure_chip)(struct spinand_device *spinand);
@@ -544,6 +546,9 @@ struct spinand_info {
                .update_cache = __update,                               \
        }
 
+#define SPINAND_INFO_VENDOR_OPS(__ops)                                 \
+       .vendor_ops = __ops
+
 #define SPINAND_ECCINFO(__ooblayout, __get_status)                     \
        .eccinfo = {                                                    \
                .ooblayout = __ooblayout,                               \