xref: /linux/drivers/mtd/nand/onenand/generic.c (revision d0c9a21c8e0b2d7c55a2174f47bd0ea1d7302de6)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
226777d37SBoris Brezillon /*
326777d37SBoris Brezillon  *  Copyright (c) 2005 Samsung Electronics
426777d37SBoris Brezillon  *  Kyungmin Park <kyungmin.park@samsung.com>
526777d37SBoris Brezillon  *
626777d37SBoris Brezillon  *  Overview:
726777d37SBoris Brezillon  *   This is a device driver for the OneNAND flash for generic boards.
826777d37SBoris Brezillon  */
926777d37SBoris Brezillon 
1026777d37SBoris Brezillon #include <linux/module.h>
1126777d37SBoris Brezillon #include <linux/slab.h>
1226777d37SBoris Brezillon #include <linux/platform_device.h>
1326777d37SBoris Brezillon #include <linux/mtd/mtd.h>
1426777d37SBoris Brezillon #include <linux/mtd/onenand.h>
1526777d37SBoris Brezillon #include <linux/mtd/partitions.h>
1626777d37SBoris Brezillon #include <linux/io.h>
1726777d37SBoris Brezillon 
1826777d37SBoris Brezillon /*
1926777d37SBoris Brezillon  * Note: Driver name and platform data format have been updated!
2026777d37SBoris Brezillon  *
2126777d37SBoris Brezillon  * This version of the driver is named "onenand-flash" and takes struct
2226777d37SBoris Brezillon  * onenand_platform_data as platform data. The old ARM-specific version
2326777d37SBoris Brezillon  * with the name "onenand" used to take struct flash_platform_data.
2426777d37SBoris Brezillon  */
2526777d37SBoris Brezillon #define DRIVER_NAME	"onenand-flash"
2626777d37SBoris Brezillon 
2726777d37SBoris Brezillon struct onenand_info {
2826777d37SBoris Brezillon 	struct mtd_info		mtd;
2926777d37SBoris Brezillon 	struct onenand_chip	onenand;
3026777d37SBoris Brezillon };
3126777d37SBoris Brezillon 
generic_onenand_probe(struct platform_device * pdev)3226777d37SBoris Brezillon static int generic_onenand_probe(struct platform_device *pdev)
3326777d37SBoris Brezillon {
3426777d37SBoris Brezillon 	struct onenand_info *info;
3526777d37SBoris Brezillon 	struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev);
3626777d37SBoris Brezillon 	struct resource *res = pdev->resource;
3726777d37SBoris Brezillon 	unsigned long size = resource_size(res);
3826777d37SBoris Brezillon 	int err;
3926777d37SBoris Brezillon 
4026777d37SBoris Brezillon 	info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
4126777d37SBoris Brezillon 	if (!info)
4226777d37SBoris Brezillon 		return -ENOMEM;
4326777d37SBoris Brezillon 
4426777d37SBoris Brezillon 	if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) {
4526777d37SBoris Brezillon 		err = -EBUSY;
4626777d37SBoris Brezillon 		goto out_free_info;
4726777d37SBoris Brezillon 	}
4826777d37SBoris Brezillon 
4926777d37SBoris Brezillon 	info->onenand.base = ioremap(res->start, size);
5026777d37SBoris Brezillon 	if (!info->onenand.base) {
5126777d37SBoris Brezillon 		err = -ENOMEM;
5226777d37SBoris Brezillon 		goto out_release_mem_region;
5326777d37SBoris Brezillon 	}
5426777d37SBoris Brezillon 
5526777d37SBoris Brezillon 	info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
563e68f331SJiasheng Jiang 
573e68f331SJiasheng Jiang 	err = platform_get_irq(pdev, 0);
583e68f331SJiasheng Jiang 	if (err < 0)
593e68f331SJiasheng Jiang 		goto out_iounmap;
603e68f331SJiasheng Jiang 
613e68f331SJiasheng Jiang 	info->onenand.irq = err;
6226777d37SBoris Brezillon 
6326777d37SBoris Brezillon 	info->mtd.dev.parent = &pdev->dev;
6426777d37SBoris Brezillon 	info->mtd.priv = &info->onenand;
6526777d37SBoris Brezillon 
6626777d37SBoris Brezillon 	if (onenand_scan(&info->mtd, 1)) {
6726777d37SBoris Brezillon 		err = -ENXIO;
6826777d37SBoris Brezillon 		goto out_iounmap;
6926777d37SBoris Brezillon 	}
7026777d37SBoris Brezillon 
71e8b0ac39SRafał Miłecki 	err = mtd_device_register(&info->mtd, pdata ? pdata->parts : NULL,
7226777d37SBoris Brezillon 				  pdata ? pdata->nr_parts : 0);
7326777d37SBoris Brezillon 
7426777d37SBoris Brezillon 	platform_set_drvdata(pdev, info);
7526777d37SBoris Brezillon 
7626777d37SBoris Brezillon 	return 0;
7726777d37SBoris Brezillon 
7826777d37SBoris Brezillon out_iounmap:
7926777d37SBoris Brezillon 	iounmap(info->onenand.base);
8026777d37SBoris Brezillon out_release_mem_region:
8126777d37SBoris Brezillon 	release_mem_region(res->start, size);
8226777d37SBoris Brezillon out_free_info:
8326777d37SBoris Brezillon 	kfree(info);
8426777d37SBoris Brezillon 
8526777d37SBoris Brezillon 	return err;
8626777d37SBoris Brezillon }
8726777d37SBoris Brezillon 
generic_onenand_remove(struct platform_device * pdev)88ec185b18SUwe Kleine-König static void generic_onenand_remove(struct platform_device *pdev)
8926777d37SBoris Brezillon {
9026777d37SBoris Brezillon 	struct onenand_info *info = platform_get_drvdata(pdev);
9126777d37SBoris Brezillon 	struct resource *res = pdev->resource;
9226777d37SBoris Brezillon 	unsigned long size = resource_size(res);
9326777d37SBoris Brezillon 
9426777d37SBoris Brezillon 	if (info) {
9526777d37SBoris Brezillon 		onenand_release(&info->mtd);
9626777d37SBoris Brezillon 		release_mem_region(res->start, size);
9726777d37SBoris Brezillon 		iounmap(info->onenand.base);
9826777d37SBoris Brezillon 		kfree(info);
9926777d37SBoris Brezillon 	}
10026777d37SBoris Brezillon }
10126777d37SBoris Brezillon 
10226777d37SBoris Brezillon static struct platform_driver generic_onenand_driver = {
10326777d37SBoris Brezillon 	.driver = {
10426777d37SBoris Brezillon 		.name		= DRIVER_NAME,
10526777d37SBoris Brezillon 	},
10626777d37SBoris Brezillon 	.probe		= generic_onenand_probe,
107*f8470006SUwe Kleine-König 	.remove		= generic_onenand_remove,
10826777d37SBoris Brezillon };
10926777d37SBoris Brezillon 
11026777d37SBoris Brezillon module_platform_driver(generic_onenand_driver);
11126777d37SBoris Brezillon 
11226777d37SBoris Brezillon MODULE_LICENSE("GPL");
11326777d37SBoris Brezillon MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
11426777d37SBoris Brezillon MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards");
11526777d37SBoris Brezillon MODULE_ALIAS("platform:" DRIVER_NAME);
116