1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 268ee4b1cSKyungmin Park /* 368ee4b1cSKyungmin Park * Copyright (c) 2005 Samsung Electronics 468ee4b1cSKyungmin Park * Kyungmin Park <kyungmin.park@samsung.com> 568ee4b1cSKyungmin Park * 668ee4b1cSKyungmin Park * Overview: 768ee4b1cSKyungmin Park * This is a device driver for the OneNAND flash for generic boards. 868ee4b1cSKyungmin Park */ 968ee4b1cSKyungmin Park 1068ee4b1cSKyungmin Park #include <linux/module.h> 11de25968cSTim Schmielau #include <linux/slab.h> 1227f4e083SKyungmin Park #include <linux/platform_device.h> 1368ee4b1cSKyungmin Park #include <linux/mtd/mtd.h> 1468ee4b1cSKyungmin Park #include <linux/mtd/onenand.h> 1568ee4b1cSKyungmin Park #include <linux/mtd/partitions.h> 162584cf83SDan Williams #include <linux/io.h> 1768ee4b1cSKyungmin Park 18778dbcc1SMagnus Damm /* 19778dbcc1SMagnus Damm * Note: Driver name and platform data format have been updated! 20778dbcc1SMagnus Damm * 21778dbcc1SMagnus Damm * This version of the driver is named "onenand-flash" and takes struct 22778dbcc1SMagnus Damm * onenand_platform_data as platform data. The old ARM-specific version 23778dbcc1SMagnus Damm * with the name "onenand" used to take struct flash_platform_data. 24778dbcc1SMagnus Damm */ 25778dbcc1SMagnus Damm #define DRIVER_NAME "onenand-flash" 2668ee4b1cSKyungmin Park 2768ee4b1cSKyungmin Park struct onenand_info { 2868ee4b1cSKyungmin Park struct mtd_info mtd; 2968ee4b1cSKyungmin Park struct onenand_chip onenand; 3068ee4b1cSKyungmin Park }; 3168ee4b1cSKyungmin Park 3206f25510SBill Pemberton static int generic_onenand_probe(struct platform_device *pdev) 3368ee4b1cSKyungmin Park { 3468ee4b1cSKyungmin Park struct onenand_info *info; 35e09f7f99SJingoo Han struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev); 3668ee4b1cSKyungmin Park struct resource *res = pdev->resource; 37778dbcc1SMagnus Damm unsigned long size = resource_size(res); 3868ee4b1cSKyungmin Park int err; 3968ee4b1cSKyungmin Park 4095b93a0cSBurman Yan info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); 4168ee4b1cSKyungmin Park if (!info) 4268ee4b1cSKyungmin Park return -ENOMEM; 4368ee4b1cSKyungmin Park 44778dbcc1SMagnus Damm if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { 4568ee4b1cSKyungmin Park err = -EBUSY; 4668ee4b1cSKyungmin Park goto out_free_info; 4768ee4b1cSKyungmin Park } 4868ee4b1cSKyungmin Park 4968ee4b1cSKyungmin Park info->onenand.base = ioremap(res->start, size); 5068ee4b1cSKyungmin Park if (!info->onenand.base) { 5168ee4b1cSKyungmin Park err = -ENOMEM; 5268ee4b1cSKyungmin Park goto out_release_mem_region; 5368ee4b1cSKyungmin Park } 5468ee4b1cSKyungmin Park 558b3ae733SBrian Norris info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL; 562c22120fSKyungmin Park info->onenand.irq = platform_get_irq(pdev, 0); 5768ee4b1cSKyungmin Park 58fe003bc8SFrans Klaver info->mtd.dev.parent = &pdev->dev; 5968ee4b1cSKyungmin Park info->mtd.priv = &info->onenand; 6068ee4b1cSKyungmin Park 6168ee4b1cSKyungmin Park if (onenand_scan(&info->mtd, 1)) { 6268ee4b1cSKyungmin Park err = -ENXIO; 6368ee4b1cSKyungmin Park goto out_iounmap; 6468ee4b1cSKyungmin Park } 6568ee4b1cSKyungmin Park 66e8b0ac39SRafał Miłecki err = mtd_device_register(&info->mtd, pdata ? pdata->parts : NULL, 6792ffb00dSDmitry Eremin-Solenikov pdata ? pdata->nr_parts : 0); 6868ee4b1cSKyungmin Park 697a192ec3SMing Lei platform_set_drvdata(pdev, info); 7068ee4b1cSKyungmin Park 7168ee4b1cSKyungmin Park return 0; 7268ee4b1cSKyungmin Park 7368ee4b1cSKyungmin Park out_iounmap: 7468ee4b1cSKyungmin Park iounmap(info->onenand.base); 7568ee4b1cSKyungmin Park out_release_mem_region: 7668ee4b1cSKyungmin Park release_mem_region(res->start, size); 7768ee4b1cSKyungmin Park out_free_info: 7868ee4b1cSKyungmin Park kfree(info); 7968ee4b1cSKyungmin Park 8068ee4b1cSKyungmin Park return err; 8168ee4b1cSKyungmin Park } 8268ee4b1cSKyungmin Park 83810b7e06SBill Pemberton static int generic_onenand_remove(struct platform_device *pdev) 8468ee4b1cSKyungmin Park { 857a192ec3SMing Lei struct onenand_info *info = platform_get_drvdata(pdev); 8668ee4b1cSKyungmin Park struct resource *res = pdev->resource; 87778dbcc1SMagnus Damm unsigned long size = resource_size(res); 8868ee4b1cSKyungmin Park 8968ee4b1cSKyungmin Park if (info) { 9068ee4b1cSKyungmin Park onenand_release(&info->mtd); 9168ee4b1cSKyungmin Park release_mem_region(res->start, size); 9268ee4b1cSKyungmin Park iounmap(info->onenand.base); 9368ee4b1cSKyungmin Park kfree(info); 9468ee4b1cSKyungmin Park } 9568ee4b1cSKyungmin Park 9668ee4b1cSKyungmin Park return 0; 9768ee4b1cSKyungmin Park } 9868ee4b1cSKyungmin Park 997a192ec3SMing Lei static struct platform_driver generic_onenand_driver = { 1007a192ec3SMing Lei .driver = { 10168ee4b1cSKyungmin Park .name = DRIVER_NAME, 1027a192ec3SMing Lei }, 10368ee4b1cSKyungmin Park .probe = generic_onenand_probe, 1045153b88cSBill Pemberton .remove = generic_onenand_remove, 10568ee4b1cSKyungmin Park }; 10668ee4b1cSKyungmin Park 107f99640deSAxel Lin module_platform_driver(generic_onenand_driver); 10868ee4b1cSKyungmin Park 10968ee4b1cSKyungmin Park MODULE_LICENSE("GPL"); 11068ee4b1cSKyungmin Park MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); 11168ee4b1cSKyungmin Park MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards"); 112f99640deSAxel Lin MODULE_ALIAS("platform:" DRIVER_NAME); 113