Commit e55bbdd4 authored by Binbin Zhou's avatar Binbin Zhou Committed by Miquel Raynal
Browse files

mtd: rawnand: loongson: Add Loongson-2K0500 NAND controller support



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: default avatarBinbin Zhou <zhoubinbin@loongson.cn>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
parent 4a2bab7c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -438,7 +438,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.
+52 −1
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 * NAND Controller Driver for Loongson family chips
 *
 * Copyright (C) 2015-2025 Keguang Zhang <keguang.zhang@gmail.com>
 * Copyright (C) 2025 Binbin Zhou <zhoubinbin@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)
@@ -83,6 +102,7 @@ struct loongson_nand_data {
	unsigned int hold_cycle;
	unsigned int wait_cycle;
	unsigned int nand_cs;
	unsigned int dma_bits;
	void (*set_addr)(struct loongson_nand_host *host, struct loongson_nand_op *op);
};

@@ -744,7 +764,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))
@@ -754,6 +774,19 @@ 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));

	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");

	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);

	regmap_write(host->regmap, LOONGSON_NAND_CS_RDY_MAP, val);

	chan = dma_request_chan(dev, "rxtx");
	if (IS_ERR(chan))
		return dev_err_probe(dev, PTR_ERR(chan), "failed to request DMA channel\n");
@@ -882,6 +915,7 @@ static const struct loongson_nand_data ls1b_nand_data = {
	.status_field = GENMASK(15, 8),
	.hold_cycle = 0x2,
	.wait_cycle = 0xc,
	.dma_bits = 32,
	.set_addr = ls1b_nand_set_addr,
};

@@ -892,6 +926,18 @@ static const struct loongson_nand_data ls1c_nand_data = {
	.op_scope_field = GENMASK(29, 16),
	.hold_cycle = 0x2,
	.wait_cycle = 0xc,
	.dma_bits = 32,
	.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,
	.dma_bits = 64,
	.set_addr = ls1c_nand_set_addr,
};

@@ -904,6 +950,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);
@@ -920,5 +970,6 @@ static struct platform_driver loongson_nand_driver = {
module_platform_driver(loongson_nand_driver);

MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
MODULE_DESCRIPTION("Loongson NAND Controller Driver");
MODULE_LICENSE("GPL");