]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mtd: rawnand: loongson: Add nand chip select support
authorBinbin Zhou <zhoubinbin@loongson.cn>
Thu, 4 Sep 2025 13:06:35 +0000 (21:06 +0800)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 10 Sep 2025 08:56:09 +0000 (10:56 +0200)
The page address register describes the page address of the starting
address for NAND read/write/erase operations.

According to the manual, it consists of two parts:
{chip select, page number}

The `chip select` is fixed at 2 bits, and the `page number` is
determined based on the actual capacity of the single-chip memory.
Therefore we need to determine the `chip select` bits base on the `page
number`.

For example, for a 1GB capacity chip (2K page size), it has 1M pages.
Thus, [19:0] is used to represent the page number, and [21:20]
represents the chip select.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
drivers/mtd/nand/raw/loongson-nand-controller.c

index 97cd566420a8935f60beabcf64d3369643507979..1427dd6943a7bd8eb30e3b54d71d9b271dcfbc37 100644 (file)
@@ -82,6 +82,7 @@ struct loongson_nand_data {
        unsigned int op_scope_field;
        unsigned int hold_cycle;
        unsigned int wait_cycle;
+       unsigned int nand_cs;
        void (*set_addr)(struct loongson_nand_host *host, struct loongson_nand_op *op);
 };
 
@@ -90,6 +91,7 @@ struct loongson_nand_host {
        struct nand_chip chip;
        struct nand_controller controller;
        const struct loongson_nand_data *data;
+       unsigned int addr_cs_field;
        void __iomem *reg_base;
        struct regmap *regmap;
        /* DMA Engine stuff */
@@ -215,6 +217,26 @@ static int loongson_nand_parse_instructions(struct nand_chip *chip, const struct
        return 0;
 }
 
+static void loongson_nand_set_addr_cs(struct loongson_nand_host *host)
+{
+       struct nand_chip *chip = &host->chip;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       if (!host->data->nand_cs)
+               return;
+
+       /*
+        * The Manufacturer/Chip ID read operation precedes attach_chip, at which point
+        * information such as NAND chip selection and capacity is unknown. As a
+        * workaround, we use 128MB cellsize (2KB pagesize) as a fallback.
+        */
+       if (!mtd->writesize)
+               host->addr_cs_field = GENMASK(17, 16);
+
+       regmap_update_bits(host->regmap, LOONGSON_NAND_ADDR2, host->addr_cs_field,
+                          host->data->nand_cs << __ffs(host->addr_cs_field));
+}
+
 static void ls1b_nand_set_addr(struct loongson_nand_host *host, struct loongson_nand_op *op)
 {
        struct nand_chip *chip = &host->chip;
@@ -263,6 +285,8 @@ static void ls1c_nand_set_addr(struct loongson_nand_host *host, struct loongson_
                        regmap_update_bits(host->regmap, LOONGSON_NAND_ADDR2, mask, val);
                }
        }
+
+       loongson_nand_set_addr_cs(host);
 }
 
 static void loongson_nand_trigger_op(struct loongson_nand_host *host, struct loongson_nand_op *op)
@@ -603,42 +627,82 @@ static int loongson_nand_exec_op(struct nand_chip *chip, const struct nand_opera
        return nand_op_parser_exec_op(chip, &loongson_nand_op_parser, op, check_only);
 }
 
-static int loongson_nand_attach_chip(struct nand_chip *chip)
+static int loongson_nand_get_chip_capacity(struct nand_chip *chip)
 {
        struct loongson_nand_host *host = nand_get_controller_data(chip);
        u64 chipsize = nanddev_target_size(&chip->base);
-       int cell_size = 0;
+       struct mtd_info *mtd = nand_to_mtd(chip);
 
-       switch (chipsize) {
-       case SZ_128M:
-               cell_size = 0x0;
-               break;
-       case SZ_256M:
-               cell_size = 0x1;
-               break;
-       case SZ_512M:
-               cell_size = 0x2;
-               break;
-       case SZ_1G:
-               cell_size = 0x3;
-               break;
-       case SZ_2G:
-               cell_size = 0x4;
+       switch (mtd->writesize) {
+       case SZ_512:
+               switch (chipsize) {
+               case SZ_8M:
+                       host->addr_cs_field = GENMASK(15, 14);
+                       return 0x9;
+               case SZ_16M:
+                       host->addr_cs_field = GENMASK(16, 15);
+                       return 0xa;
+               case SZ_32M:
+                       host->addr_cs_field = GENMASK(17, 16);
+                       return 0xb;
+               case SZ_64M:
+                       host->addr_cs_field = GENMASK(18, 17);
+                       return 0xc;
+               case SZ_128M:
+                       host->addr_cs_field = GENMASK(19, 18);
+                       return 0xd;
+               }
                break;
-       case SZ_4G:
-               cell_size = 0x5;
+       case SZ_2K:
+               switch (chipsize) {
+               case SZ_128M:
+                       host->addr_cs_field = GENMASK(17, 16);
+                       return 0x0;
+               case SZ_256M:
+                       host->addr_cs_field = GENMASK(18, 17);
+                       return 0x1;
+               case SZ_512M:
+                       host->addr_cs_field = GENMASK(19, 18);
+                       return 0x2;
+               case SZ_1G:
+                       host->addr_cs_field = GENMASK(20, 19);
+                       return 0x3;
+               }
                break;
-       case SZ_8G:
-               cell_size = 0x6;
+       case SZ_4K:
+               if (chipsize == SZ_2G) {
+                       host->addr_cs_field = GENMASK(20, 19);
+                       return 0x4;
+               }
                break;
-       case SZ_16G:
-               cell_size = 0x7;
+       case SZ_8K:
+               switch (chipsize) {
+               case SZ_4G:
+                       host->addr_cs_field = GENMASK(20, 19);
+                       return 0x5;
+               case SZ_8G:
+                       host->addr_cs_field = GENMASK(21, 20);
+                       return 0x6;
+               case SZ_16G:
+                       host->addr_cs_field = GENMASK(22, 21);
+                       return 0x7;
+               }
                break;
-       default:
-               dev_err(host->dev, "unsupported chip size: %llu MB\n", chipsize);
-               return -EINVAL;
        }
 
+       dev_err(host->dev, "Unsupported chip size: %llu MB with page size %u B\n",
+               chipsize, mtd->writesize);
+       return -EINVAL;
+}
+
+static int loongson_nand_attach_chip(struct nand_chip *chip)
+{
+       struct loongson_nand_host *host = nand_get_controller_data(chip);
+       int cell_size = loongson_nand_get_chip_capacity(chip);
+
+       if (cell_size < 0)
+               return cell_size;
+
        switch (chip->ecc.engine_type) {
        case NAND_ECC_ENGINE_TYPE_NONE:
                break;