* 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>
#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)
#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)
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);
};
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))
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");
.status_field = GENMASK(15, 8),
.hold_cycle = 0x2,
.wait_cycle = 0xc,
+ .dma_bits = 32,
.set_addr = ls1b_nand_set_addr,
};
.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,
};
.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);
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");