Skip to content

Commit 853034c

Browse files
maquefelarndb
authored andcommitted
mtd: rawnand: add support for ts72xx
Technologic Systems has it's own nand controller implementation in CPLD. Signed-off-by: Nikita Shubin <[email protected]> Tested-by: Alexander Sverdlin <[email protected]> Acked-by: Miquel Raynal <[email protected]> Acked-by: Vinod Koul <[email protected]> Signed-off-by: Arnd Bergmann <[email protected]>
1 parent 1d4f2ff commit 853034c

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

drivers/mtd/nand/raw/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,12 @@ config MTD_NAND_RENESAS
448448
Enables support for the NAND controller found on Renesas R-Car
449449
Gen3 and RZ/N1 SoC families.
450450

451+
config MTD_NAND_TS72XX
452+
tristate "ts72xx NAND controller"
453+
depends on ARCH_EP93XX && HAS_IOMEM
454+
help
455+
Enables support for NAND controller on ts72xx SBCs.
456+
451457
comment "Misc"
452458

453459
config MTD_SM_COMMON

drivers/mtd/nand/raw/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o
3434
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
3535
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
3636
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
37+
obj-$(CONFIG_MTD_NAND_TS72XX) += technologic-nand-controller.o
3738
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
3839
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
3940
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Technologic Systems TS72xx NAND controller driver
4+
*
5+
* Copyright (C) 2023 Nikita Shubin <[email protected]>
6+
*
7+
* Derived from: plat_nand.c
8+
* Author: Vitaly Wool <[email protected]>
9+
*/
10+
11+
#include <linux/bits.h>
12+
#include <linux/err.h>
13+
#include <linux/io.h>
14+
#include <linux/iopoll.h>
15+
#include <linux/module.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/slab.h>
18+
19+
#include <linux/mtd/mtd.h>
20+
#include <linux/mtd/platnand.h>
21+
22+
#define TS72XX_NAND_CONTROL_ADDR_LINE BIT(22) /* 0xN0400000 */
23+
#define TS72XX_NAND_BUSY_ADDR_LINE BIT(23) /* 0xN0800000 */
24+
25+
#define TS72XX_NAND_ALE BIT(0)
26+
#define TS72XX_NAND_CLE BIT(1)
27+
#define TS72XX_NAND_NCE BIT(2)
28+
29+
#define TS72XX_NAND_CTRL_CLE (TS72XX_NAND_NCE | TS72XX_NAND_CLE)
30+
#define TS72XX_NAND_CTRL_ALE (TS72XX_NAND_NCE | TS72XX_NAND_ALE)
31+
32+
struct ts72xx_nand_data {
33+
struct nand_controller controller;
34+
struct nand_chip chip;
35+
void __iomem *base;
36+
void __iomem *ctrl;
37+
void __iomem *busy;
38+
};
39+
40+
static inline struct ts72xx_nand_data *chip_to_ts72xx(struct nand_chip *chip)
41+
{
42+
return container_of(chip, struct ts72xx_nand_data, chip);
43+
}
44+
45+
static int ts72xx_nand_attach_chip(struct nand_chip *chip)
46+
{
47+
switch (chip->ecc.engine_type) {
48+
case NAND_ECC_ENGINE_TYPE_ON_HOST:
49+
return -EINVAL;
50+
case NAND_ECC_ENGINE_TYPE_SOFT:
51+
if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN)
52+
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
53+
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
54+
fallthrough;
55+
default:
56+
return 0;
57+
}
58+
}
59+
60+
static void ts72xx_nand_ctrl(struct nand_chip *chip, u8 value)
61+
{
62+
struct ts72xx_nand_data *data = chip_to_ts72xx(chip);
63+
unsigned char bits = ioread8(data->ctrl) & ~GENMASK(2, 0);
64+
65+
iowrite8(bits | value, data->ctrl);
66+
}
67+
68+
static int ts72xx_nand_exec_instr(struct nand_chip *chip,
69+
const struct nand_op_instr *instr)
70+
{
71+
struct ts72xx_nand_data *data = chip_to_ts72xx(chip);
72+
unsigned int timeout_us;
73+
u32 status;
74+
int ret;
75+
76+
switch (instr->type) {
77+
case NAND_OP_CMD_INSTR:
78+
ts72xx_nand_ctrl(chip, TS72XX_NAND_CTRL_CLE);
79+
iowrite8(instr->ctx.cmd.opcode, data->base);
80+
ts72xx_nand_ctrl(chip, TS72XX_NAND_NCE);
81+
break;
82+
83+
case NAND_OP_ADDR_INSTR:
84+
ts72xx_nand_ctrl(chip, TS72XX_NAND_CTRL_ALE);
85+
iowrite8_rep(data->base, instr->ctx.addr.addrs, instr->ctx.addr.naddrs);
86+
ts72xx_nand_ctrl(chip, TS72XX_NAND_NCE);
87+
break;
88+
89+
case NAND_OP_DATA_IN_INSTR:
90+
ioread8_rep(data->base, instr->ctx.data.buf.in, instr->ctx.data.len);
91+
break;
92+
93+
case NAND_OP_DATA_OUT_INSTR:
94+
iowrite8_rep(data->base, instr->ctx.data.buf.in, instr->ctx.data.len);
95+
break;
96+
97+
case NAND_OP_WAITRDY_INSTR:
98+
timeout_us = instr->ctx.waitrdy.timeout_ms * 1000;
99+
ret = readb_poll_timeout(data->busy, status, status & BIT(5), 0, timeout_us);
100+
if (ret)
101+
return ret;
102+
103+
break;
104+
}
105+
106+
if (instr->delay_ns)
107+
ndelay(instr->delay_ns);
108+
109+
return 0;
110+
}
111+
112+
static int ts72xx_nand_exec_op(struct nand_chip *chip,
113+
const struct nand_operation *op, bool check_only)
114+
{
115+
unsigned int i;
116+
int ret;
117+
118+
if (check_only)
119+
return 0;
120+
121+
for (i = 0; i < op->ninstrs; i++) {
122+
ret = ts72xx_nand_exec_instr(chip, &op->instrs[i]);
123+
if (ret)
124+
return ret;
125+
}
126+
127+
return 0;
128+
}
129+
130+
static const struct nand_controller_ops ts72xx_nand_ops = {
131+
.attach_chip = ts72xx_nand_attach_chip,
132+
.exec_op = ts72xx_nand_exec_op,
133+
};
134+
135+
static int ts72xx_nand_probe(struct platform_device *pdev)
136+
{
137+
struct ts72xx_nand_data *data;
138+
struct fwnode_handle *child;
139+
struct mtd_info *mtd;
140+
int err;
141+
142+
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
143+
if (!data)
144+
return -ENOMEM;
145+
146+
nand_controller_init(&data->controller);
147+
data->controller.ops = &ts72xx_nand_ops;
148+
data->chip.controller = &data->controller;
149+
150+
data->base = devm_platform_ioremap_resource(pdev, 0);
151+
if (IS_ERR(data->base))
152+
return PTR_ERR(data->base);
153+
data->ctrl = data->base + TS72XX_NAND_CONTROL_ADDR_LINE;
154+
data->busy = data->base + TS72XX_NAND_BUSY_ADDR_LINE;
155+
156+
child = fwnode_get_next_child_node(dev_fwnode(&pdev->dev), NULL);
157+
if (!child)
158+
return dev_err_probe(&pdev->dev, -ENXIO,
159+
"ts72xx controller node should have exactly one child\n");
160+
161+
nand_set_flash_node(&data->chip, to_of_node(child));
162+
mtd = nand_to_mtd(&data->chip);
163+
mtd->dev.parent = &pdev->dev;
164+
platform_set_drvdata(pdev, data);
165+
166+
/*
167+
* This driver assumes that the default ECC engine should be TYPE_SOFT.
168+
* Set ->engine_type before registering the NAND devices in order to
169+
* provide a driver specific default value.
170+
*/
171+
data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
172+
173+
/* Scan to find existence of the device */
174+
err = nand_scan(&data->chip, 1);
175+
if (err)
176+
goto err_handle_put;
177+
178+
err = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
179+
if (err)
180+
goto err_clean_nand;
181+
182+
return 0;
183+
184+
err_clean_nand:
185+
nand_cleanup(&data->chip);
186+
err_handle_put:
187+
fwnode_handle_put(child);
188+
return err;
189+
}
190+
191+
static void ts72xx_nand_remove(struct platform_device *pdev)
192+
{
193+
struct ts72xx_nand_data *data = platform_get_drvdata(pdev);
194+
struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);
195+
struct nand_chip *chip = &data->chip;
196+
int ret;
197+
198+
ret = mtd_device_unregister(nand_to_mtd(chip));
199+
WARN_ON(ret);
200+
nand_cleanup(chip);
201+
fwnode_handle_put(fwnode);
202+
}
203+
204+
static const struct of_device_id ts72xx_id_table[] = {
205+
{ .compatible = "technologic,ts7200-nand" },
206+
{ /* sentinel */ }
207+
};
208+
MODULE_DEVICE_TABLE(of, ts72xx_id_table);
209+
210+
static struct platform_driver ts72xx_nand_driver = {
211+
.driver = {
212+
.name = "ts72xx-nand",
213+
.of_match_table = ts72xx_id_table,
214+
},
215+
.probe = ts72xx_nand_probe,
216+
.remove_new = ts72xx_nand_remove,
217+
};
218+
module_platform_driver(ts72xx_nand_driver);
219+
220+
MODULE_AUTHOR("Nikita Shubin <[email protected]>");
221+
MODULE_DESCRIPTION("Technologic Systems TS72xx NAND controller driver");
222+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)