mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-09 07:14:48 +08:00
ec185b18c2
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
160 lines
3.9 KiB
C
160 lines
3.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Generic NAND driver
|
|
*
|
|
* Author: Vitaly Wool <vitalywool@gmail.com>
|
|
*/
|
|
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/platnand.h>
|
|
|
|
struct plat_nand_data {
|
|
struct nand_controller controller;
|
|
struct nand_chip chip;
|
|
void __iomem *io_base;
|
|
};
|
|
|
|
static int plat_nand_attach_chip(struct nand_chip *chip)
|
|
{
|
|
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT &&
|
|
chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN)
|
|
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct nand_controller_ops plat_nand_ops = {
|
|
.attach_chip = plat_nand_attach_chip,
|
|
};
|
|
|
|
/*
|
|
* Probe for the NAND device.
|
|
*/
|
|
static int plat_nand_probe(struct platform_device *pdev)
|
|
{
|
|
struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct plat_nand_data *data;
|
|
struct mtd_info *mtd;
|
|
const char **part_types;
|
|
int err = 0;
|
|
|
|
if (!pdata) {
|
|
dev_err(&pdev->dev, "platform_nand_data is missing\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (pdata->chip.nr_chips < 1) {
|
|
dev_err(&pdev->dev, "invalid number of chips specified\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Allocate memory for the device structure (and zero it) */
|
|
data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data),
|
|
GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
data->controller.ops = &plat_nand_ops;
|
|
nand_controller_init(&data->controller);
|
|
data->chip.controller = &data->controller;
|
|
|
|
data->io_base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(data->io_base))
|
|
return PTR_ERR(data->io_base);
|
|
|
|
nand_set_flash_node(&data->chip, pdev->dev.of_node);
|
|
mtd = nand_to_mtd(&data->chip);
|
|
mtd->dev.parent = &pdev->dev;
|
|
|
|
data->chip.legacy.IO_ADDR_R = data->io_base;
|
|
data->chip.legacy.IO_ADDR_W = data->io_base;
|
|
data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl;
|
|
data->chip.legacy.dev_ready = pdata->ctrl.dev_ready;
|
|
data->chip.legacy.select_chip = pdata->ctrl.select_chip;
|
|
data->chip.legacy.write_buf = pdata->ctrl.write_buf;
|
|
data->chip.legacy.read_buf = pdata->ctrl.read_buf;
|
|
data->chip.legacy.chip_delay = pdata->chip.chip_delay;
|
|
data->chip.options |= pdata->chip.options;
|
|
data->chip.bbt_options |= pdata->chip.bbt_options;
|
|
|
|
platform_set_drvdata(pdev, data);
|
|
|
|
/* Handle any platform specific setup */
|
|
if (pdata->ctrl.probe) {
|
|
err = pdata->ctrl.probe(pdev);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* This driver assumes that the default ECC engine should be TYPE_SOFT.
|
|
* Set ->engine_type before registering the NAND devices in order to
|
|
* provide a driver specific default value.
|
|
*/
|
|
data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
|
|
|
|
/* Scan to find existence of the device */
|
|
err = nand_scan(&data->chip, pdata->chip.nr_chips);
|
|
if (err)
|
|
goto out;
|
|
|
|
part_types = pdata->chip.part_probe_types;
|
|
|
|
err = mtd_device_parse_register(mtd, part_types, NULL,
|
|
pdata->chip.partitions,
|
|
pdata->chip.nr_partitions);
|
|
|
|
if (!err)
|
|
return err;
|
|
|
|
nand_cleanup(&data->chip);
|
|
out:
|
|
if (pdata->ctrl.remove)
|
|
pdata->ctrl.remove(pdev);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Remove a NAND device.
|
|
*/
|
|
static void plat_nand_remove(struct platform_device *pdev)
|
|
{
|
|
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
|
struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct nand_chip *chip = &data->chip;
|
|
int ret;
|
|
|
|
ret = mtd_device_unregister(nand_to_mtd(chip));
|
|
WARN_ON(ret);
|
|
nand_cleanup(chip);
|
|
if (pdata->ctrl.remove)
|
|
pdata->ctrl.remove(pdev);
|
|
}
|
|
|
|
static const struct of_device_id plat_nand_match[] = {
|
|
{ .compatible = "gen_nand" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, plat_nand_match);
|
|
|
|
static struct platform_driver plat_nand_driver = {
|
|
.probe = plat_nand_probe,
|
|
.remove_new = plat_nand_remove,
|
|
.driver = {
|
|
.name = "gen_nand",
|
|
.of_match_table = plat_nand_match,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(plat_nand_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Vitaly Wool");
|
|
MODULE_DESCRIPTION("Simple generic NAND driver");
|
|
MODULE_ALIAS("platform:gen_nand");
|