[PATCH v2 6/8] mtd: rawnand: loongson: Add Loongson-2K0500 NAND controller support

Keguang Zhang keguang.zhang at gmail.com
Wed Aug 20 23:45:49 PDT 2025


On Mon, Aug 11, 2025 at 2:03 PM Binbin Zhou <zhoubinbin at loongson.cn> wrote:
>
> The Loongson-2K0500 NAND controller is similar to the Loongson-1C.
>
> It supports a maximum capacity of 16GB FLASH per chip with a maximum
> page size of 8KB, and it supports up to 4 chip selects and 4 RDY
> signals.
>
> Its DMA controller is defaulted to APBDMA0.
>
> Signed-off-by: Binbin Zhou <zhoubinbin at loongson.cn>
> ---
>  drivers/mtd/nand/raw/Kconfig                  |  2 +-
>  .../mtd/nand/raw/loongson-nand-controller.c   | 55 ++++++++++++++++++-
>  2 files changed, 54 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index d9e3f13666ac..7b0c5d06aa95 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -464,7 +464,7 @@ config MTD_NAND_NUVOTON_MA35
>
>  config MTD_NAND_LOONGSON
>         tristate "Loongson NAND controller"
> -       depends on LOONGSON1_APB_DMA || COMPILE_TEST
> +       depends on LOONGSON1_APB_DMA || LOONGSON2_APB_DMA || COMPILE_TEST
>         select REGMAP_MMIO
>         help
>           Enables support for NAND controller on Loongson family chips.
> diff --git a/drivers/mtd/nand/raw/loongson-nand-controller.c b/drivers/mtd/nand/raw/loongson-nand-controller.c
> index 5a51c7d299cc..7a15df3fcd31 100644
> --- a/drivers/mtd/nand/raw/loongson-nand-controller.c
> +++ b/drivers/mtd/nand/raw/loongson-nand-controller.c
> @@ -3,6 +3,7 @@
>   * NAND Controller Driver for Loongson family chips
>   *
>   * Copyright (C) 2015-2025 Keguang Zhang <keguang.zhang at gmail.com>
> + * Copyright (C) 2025 Binbin Zhou <zhoubinbin at loongson.cn>
>   */
>
>  #include <linux/kernel.h>
> @@ -26,6 +27,7 @@
>  #define LOONGSON_NAND_IDH_STATUS       0x14
>  #define LOONGSON_NAND_PARAM            0x18
>  #define LOONGSON_NAND_OP_NUM           0x1c
> +#define LOONGSON_NAND_CS_RDY_MAP       0x20
>
>  /* Bitfields of nand command register */
>  #define LOONGSON_NAND_CMD_OP_DONE      BIT(10)
> @@ -40,6 +42,23 @@
>  #define LOONGSON_NAND_CMD_READ         BIT(1)
>  #define LOONGSON_NAND_CMD_VALID                BIT(0)
>
> +/* Bitfields of nand cs/rdy map register */
> +#define LOONGSON_NAND_MAP_CS1_SEL      GENMASK(11, 8)
> +#define LOONGSON_NAND_MAP_RDY1_SEL     GENMASK(15, 12)
> +#define LOONGSON_NAND_MAP_CS2_SEL      GENMASK(19, 16)
> +#define LOONGSON_NAND_MAP_RDY2_SEL     GENMASK(23, 20)
> +#define LOONGSON_NAND_MAP_CS3_SEL      GENMASK(27, 24)
> +#define LOONGSON_NAND_MAP_RDY3_SEL     GENMASK(31, 28)
> +
> +#define LOONGSON_NAND_CS_SEL0  BIT(0)
> +#define LOONGSON_NAND_CS_SEL1  BIT(1)
> +#define LOONGSON_NAND_CS_SEL2  BIT(2)
> +#define LOONGSON_NAND_CS_SEL3  BIT(3)
> +#define LOONGSON_NAND_CS_RDY0  BIT(0)
> +#define LOONGSON_NAND_CS_RDY1  BIT(1)
> +#define LOONGSON_NAND_CS_RDY2  BIT(2)
> +#define LOONGSON_NAND_CS_RDY3  BIT(3)
> +
>  /* Bitfields of nand timing register */
>  #define LOONGSON_NAND_WAIT_CYCLE_MASK  GENMASK(7, 0)
>  #define LOONGSON_NAND_HOLD_CYCLE_MASK  GENMASK(15, 8)
> @@ -53,6 +72,8 @@
>  #define LOONGSON_NAND_READ_ID_SLEEP_US         1000
>  #define LOONGSON_NAND_READ_ID_TIMEOUT_US       5000
>
> +#define LOONGSON_NAND_64BIT_DMA                BIT(0)

It's strongly suggested to replace this flag with a dma_bits field in
loongson_nand_host.
Please see the comments below.

> +
>  #define BITS_PER_WORD                  (4 * BITS_PER_BYTE)
>
>  struct loongson_nand_host;
> @@ -83,6 +104,7 @@ struct loongson_nand_data {
>         unsigned int hold_cycle;
>         unsigned int wait_cycle;
>         unsigned int nand_cs;

Add unsigned int dma_bits;

> +       unsigned int flags;
>         void (*set_addr)(struct loongson_nand_host *host, struct loongson_nand_op *op);
>  };
>
> @@ -751,7 +773,7 @@ static int loongson_nand_controller_init(struct loongson_nand_host *host)
>         struct device *dev = host->dev;
>         struct dma_chan *chan;
>         struct dma_slave_config cfg = {};
> -       int ret;
> +       int ret, val;
>
>         host->regmap = devm_regmap_init_mmio(dev, host->reg_base, &loongson_nand_regmap_config);
>         if (IS_ERR(host->regmap))
> @@ -761,6 +783,9 @@ static int loongson_nand_controller_init(struct loongson_nand_host *host)
>                 regmap_update_bits(host->regmap, LOONGSON_NAND_PARAM, host->data->id_cycle_field,
>                                    host->data->max_id_cycle << __ffs(host->data->id_cycle_field));
>
> +       if (host->data->flags & LOONGSON_NAND_64BIT_DMA)
> +               dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +

Sorry, all platforms should call dma_set_mask_and_coherent(),
including Loongson-1, which I missed earlier.
       ret = dma_set_mask_and_coherent(dev,
DMA_BIT_MASK(host->data->dma_bits));
       if (ret)
               return dev_err_probe(dev, ret, "failed to set DMA mask\n");

>         chan = dma_request_chan(dev, "rxtx");
>         if (IS_ERR(chan))
>                 return dev_err_probe(dev, PTR_ERR(chan), "failed to request DMA channel\n");
> @@ -776,7 +801,14 @@ static int loongson_nand_controller_init(struct loongson_nand_host *host)
>
>         init_completion(&host->dma_complete);
>
> -       return 0;
> +       val = FIELD_PREP(LOONGSON_NAND_MAP_CS1_SEL, LOONGSON_NAND_CS_SEL1)
> +           | FIELD_PREP(LOONGSON_NAND_MAP_RDY1_SEL, LOONGSON_NAND_CS_RDY1)
> +           | FIELD_PREP(LOONGSON_NAND_MAP_CS2_SEL, LOONGSON_NAND_CS_SEL2)
> +           | FIELD_PREP(LOONGSON_NAND_MAP_RDY2_SEL, LOONGSON_NAND_CS_RDY2)
> +           | FIELD_PREP(LOONGSON_NAND_MAP_CS3_SEL, LOONGSON_NAND_CS_SEL3)
> +           | FIELD_PREP(LOONGSON_NAND_MAP_RDY3_SEL, LOONGSON_NAND_CS_RDY3);
> +
> +       return regmap_write(host->regmap, LOONGSON_NAND_CS_RDY_MAP, val);

Chip selects should be set before requesting the DMA channel.

>  }
>
>  static int loongson_nand_chip_init(struct loongson_nand_host *host)
> @@ -890,6 +922,7 @@ static const struct loongson_nand_data ls1b_nand_data = {
>         .hold_cycle = 0x2,
>         .wait_cycle = 0xc,
>         .nand_cs = 0x0,

Add .dma_bits = 32,

> +       .flags = 0,

The assignment `.flags = 0x0` is redundant and can be removed.

>         .set_addr = ls1b_nand_set_addr,
>  };
>
> @@ -901,6 +934,19 @@ static const struct loongson_nand_data ls1c_nand_data = {
>         .hold_cycle = 0x2,
>         .wait_cycle = 0xc,
>         .nand_cs = 0x0,

Add .dma_bits = 32,

> +       .flags = 0,

Ditto.

> +       .set_addr = ls1c_nand_set_addr,
> +};
> +
> +static const struct loongson_nand_data ls2k0500_nand_data = {
> +       .max_id_cycle = 6,
> +       .id_cycle_field = GENMASK(14, 12),
> +       .status_field = GENMASK(23, 16),
> +       .op_scope_field = GENMASK(29, 16),
> +       .hold_cycle = 0x4,
> +       .wait_cycle = 0x12,
> +       .nand_cs = 0x0,
> +       .flags = LOONGSON_NAND_64BIT_DMA,

Replace LOONGSON_NAND_64BIT_DMA with .dma_bits = 64,

>         .set_addr = ls1c_nand_set_addr,
>  };
>
> @@ -913,6 +959,10 @@ static const struct of_device_id loongson_nand_match[] = {
>                 .compatible = "loongson,ls1c-nand-controller",
>                 .data = &ls1c_nand_data,
>         },
> +       {
> +               .compatible = "loongson,ls2k0500-nand-controller",
> +               .data = &ls2k0500_nand_data,
> +       },
>         { /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, loongson_nand_match);
> @@ -929,5 +979,6 @@ static struct platform_driver loongson_nand_driver = {
>  module_platform_driver(loongson_nand_driver);
>
>  MODULE_AUTHOR("Keguang Zhang <keguang.zhang at gmail.com>");
> +MODULE_AUTHOR("Binbin Zhou <zhoubinbin at loongson.cn>");
>  MODULE_DESCRIPTION("Loongson NAND Controller Driver");
>  MODULE_LICENSE("GPL");
> --
> 2.47.3
>


--
Best regards,

Keguang Zhang



More information about the linux-mtd mailing list