Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Technologic Systems TS72xx NAND controller driver
  4 *
  5 * Copyright (C) 2023 Nikita Shubin <nikita.shubin@maquefel.me>
  6 *
  7 * Derived from: plat_nand.c
  8 *  Author: Vitaly Wool <vitalywool@gmail.com>
  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
 32struct 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
 40static 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
 45static 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
 60static 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
 68static 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
112static 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
130static const struct nand_controller_ops ts72xx_nand_ops = {
131	.attach_chip = ts72xx_nand_attach_chip,
132	.exec_op = ts72xx_nand_exec_op,
133};
134
135static 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
184err_clean_nand:
185	nand_cleanup(&data->chip);
186err_handle_put:
187	fwnode_handle_put(child);
188	return err;
189}
190
191static 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
204static const struct of_device_id ts72xx_id_table[] = {
205	{ .compatible = "technologic,ts7200-nand" },
206	{ /* sentinel */ }
207};
208MODULE_DEVICE_TABLE(of, ts72xx_id_table);
209
210static 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 = ts72xx_nand_remove,
217};
218module_platform_driver(ts72xx_nand_driver);
219
220MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>");
221MODULE_DESCRIPTION("Technologic Systems TS72xx NAND controller driver");
222MODULE_LICENSE("GPL");