mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-07-01 23:53:16 -04:00
The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Acked-by: Tudor Ambarus <tudor.ambarus@linaro.org> Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com> # atmel Reviewed-by: Paul Cercueil <paul@crapouillou.net> # ingenic Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> # ingenic Acked-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> # intel Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> # meson Acked-by: Roger Quadros <rogerq@kernel.org> # omap_elm Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> # renesas Reviewed-by: Heiko Stuebner <heiko@sntech.de> # rockchip Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com> # sunxi Acked-by: Thierry Reding <treding@nvidia.com> # tegra Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20230411113816.3472237-1-u.kleine-koenig@pengutronix.de
209 lines
4.8 KiB
C
209 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Oxford Semiconductor OXNAS NAND driver
|
|
|
|
* Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
|
|
* Heavily based on plat_nand.c :
|
|
* Author: Vitaly Wool <vitalywool@gmail.com>
|
|
* Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
|
|
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
|
|
*/
|
|
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/rawnand.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/of.h>
|
|
|
|
/* Nand commands */
|
|
#define OXNAS_NAND_CMD_ALE BIT(18)
|
|
#define OXNAS_NAND_CMD_CLE BIT(19)
|
|
|
|
#define OXNAS_NAND_MAX_CHIPS 1
|
|
|
|
struct oxnas_nand_ctrl {
|
|
struct nand_controller base;
|
|
void __iomem *io_base;
|
|
struct clk *clk;
|
|
struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS];
|
|
unsigned int nchips;
|
|
};
|
|
|
|
static uint8_t oxnas_nand_read_byte(struct nand_chip *chip)
|
|
{
|
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
|
|
|
return readb(oxnas->io_base);
|
|
}
|
|
|
|
static void oxnas_nand_read_buf(struct nand_chip *chip, u8 *buf, int len)
|
|
{
|
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
|
|
|
ioread8_rep(oxnas->io_base, buf, len);
|
|
}
|
|
|
|
static void oxnas_nand_write_buf(struct nand_chip *chip, const u8 *buf,
|
|
int len)
|
|
{
|
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
|
|
|
iowrite8_rep(oxnas->io_base, buf, len);
|
|
}
|
|
|
|
/* Single CS command control */
|
|
static void oxnas_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
|
|
unsigned int ctrl)
|
|
{
|
|
struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip);
|
|
|
|
if (ctrl & NAND_CLE)
|
|
writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE);
|
|
else if (ctrl & NAND_ALE)
|
|
writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE);
|
|
}
|
|
|
|
/*
|
|
* Probe for the NAND device.
|
|
*/
|
|
static int oxnas_nand_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct device_node *nand_np;
|
|
struct oxnas_nand_ctrl *oxnas;
|
|
struct nand_chip *chip;
|
|
struct mtd_info *mtd;
|
|
int count = 0;
|
|
int err = 0;
|
|
int i;
|
|
|
|
/* Allocate memory for the device structure (and zero it) */
|
|
oxnas = devm_kzalloc(&pdev->dev, sizeof(*oxnas),
|
|
GFP_KERNEL);
|
|
if (!oxnas)
|
|
return -ENOMEM;
|
|
|
|
nand_controller_init(&oxnas->base);
|
|
|
|
oxnas->io_base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(oxnas->io_base))
|
|
return PTR_ERR(oxnas->io_base);
|
|
|
|
oxnas->clk = devm_clk_get(&pdev->dev, NULL);
|
|
if (IS_ERR(oxnas->clk))
|
|
oxnas->clk = NULL;
|
|
|
|
/* Only a single chip node is supported */
|
|
count = of_get_child_count(np);
|
|
if (count > 1)
|
|
return -EINVAL;
|
|
|
|
err = clk_prepare_enable(oxnas->clk);
|
|
if (err)
|
|
return err;
|
|
|
|
device_reset_optional(&pdev->dev);
|
|
|
|
for_each_child_of_node(np, nand_np) {
|
|
chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip),
|
|
GFP_KERNEL);
|
|
if (!chip) {
|
|
err = -ENOMEM;
|
|
goto err_release_child;
|
|
}
|
|
|
|
chip->controller = &oxnas->base;
|
|
|
|
nand_set_flash_node(chip, nand_np);
|
|
nand_set_controller_data(chip, oxnas);
|
|
|
|
mtd = nand_to_mtd(chip);
|
|
mtd->dev.parent = &pdev->dev;
|
|
mtd->priv = chip;
|
|
|
|
chip->legacy.cmd_ctrl = oxnas_nand_cmd_ctrl;
|
|
chip->legacy.read_buf = oxnas_nand_read_buf;
|
|
chip->legacy.read_byte = oxnas_nand_read_byte;
|
|
chip->legacy.write_buf = oxnas_nand_write_buf;
|
|
chip->legacy.chip_delay = 30;
|
|
|
|
/* Scan to find existence of the device */
|
|
err = nand_scan(chip, 1);
|
|
if (err)
|
|
goto err_release_child;
|
|
|
|
err = mtd_device_register(mtd, NULL, 0);
|
|
if (err)
|
|
goto err_cleanup_nand;
|
|
|
|
oxnas->chips[oxnas->nchips++] = chip;
|
|
}
|
|
|
|
/* Exit if no chips found */
|
|
if (!oxnas->nchips) {
|
|
err = -ENODEV;
|
|
goto err_clk_unprepare;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, oxnas);
|
|
|
|
return 0;
|
|
|
|
err_cleanup_nand:
|
|
nand_cleanup(chip);
|
|
err_release_child:
|
|
of_node_put(nand_np);
|
|
|
|
for (i = 0; i < oxnas->nchips; i++) {
|
|
chip = oxnas->chips[i];
|
|
WARN_ON(mtd_device_unregister(nand_to_mtd(chip)));
|
|
nand_cleanup(chip);
|
|
}
|
|
|
|
err_clk_unprepare:
|
|
clk_disable_unprepare(oxnas->clk);
|
|
return err;
|
|
}
|
|
|
|
static void oxnas_nand_remove(struct platform_device *pdev)
|
|
{
|
|
struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev);
|
|
struct nand_chip *chip;
|
|
int i;
|
|
|
|
for (i = 0; i < oxnas->nchips; i++) {
|
|
chip = oxnas->chips[i];
|
|
WARN_ON(mtd_device_unregister(nand_to_mtd(chip)));
|
|
nand_cleanup(chip);
|
|
}
|
|
|
|
clk_disable_unprepare(oxnas->clk);
|
|
}
|
|
|
|
static const struct of_device_id oxnas_nand_match[] = {
|
|
{ .compatible = "oxsemi,ox820-nand" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, oxnas_nand_match);
|
|
|
|
static struct platform_driver oxnas_nand_driver = {
|
|
.probe = oxnas_nand_probe,
|
|
.remove_new = oxnas_nand_remove,
|
|
.driver = {
|
|
.name = "oxnas_nand",
|
|
.of_match_table = oxnas_nand_match,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(oxnas_nand_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
|
MODULE_DESCRIPTION("Oxnas NAND driver");
|
|
MODULE_ALIAS("platform:oxnas_nand");
|