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