brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · 0e45a6f Raw
223 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Technologic Systems TS72xx NAND controller driver4 *5 * Copyright (C) 2023 Nikita Shubin <nikita.shubin@maquefel.me>6 *7 * Derived from: plat_nand.c8 *  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 to169	 * 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_new = 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");223