18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Generic NAND driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Vitaly Wool <vitalywool@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/platnand.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct plat_nand_data { 178c2ecf20Sopenharmony_ci struct nand_controller controller; 188c2ecf20Sopenharmony_ci struct nand_chip chip; 198c2ecf20Sopenharmony_ci void __iomem *io_base; 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int plat_nand_attach_chip(struct nand_chip *chip) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && 258c2ecf20Sopenharmony_ci chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 268c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci return 0; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic const struct nand_controller_ops plat_nand_ops = { 328c2ecf20Sopenharmony_ci .attach_chip = plat_nand_attach_chip, 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Probe for the NAND device. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic int plat_nand_probe(struct platform_device *pdev) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 418c2ecf20Sopenharmony_ci struct plat_nand_data *data; 428c2ecf20Sopenharmony_ci struct mtd_info *mtd; 438c2ecf20Sopenharmony_ci struct resource *res; 448c2ecf20Sopenharmony_ci const char **part_types; 458c2ecf20Sopenharmony_ci int err = 0; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!pdata) { 488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "platform_nand_data is missing\n"); 498c2ecf20Sopenharmony_ci return -EINVAL; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (pdata->chip.nr_chips < 1) { 538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid number of chips specified\n"); 548c2ecf20Sopenharmony_ci return -EINVAL; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Allocate memory for the device structure (and zero it) */ 588c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), 598c2ecf20Sopenharmony_ci GFP_KERNEL); 608c2ecf20Sopenharmony_ci if (!data) 618c2ecf20Sopenharmony_ci return -ENOMEM; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci data->controller.ops = &plat_nand_ops; 648c2ecf20Sopenharmony_ci nand_controller_init(&data->controller); 658c2ecf20Sopenharmony_ci data->chip.controller = &data->controller; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 688c2ecf20Sopenharmony_ci data->io_base = devm_ioremap_resource(&pdev->dev, res); 698c2ecf20Sopenharmony_ci if (IS_ERR(data->io_base)) 708c2ecf20Sopenharmony_ci return PTR_ERR(data->io_base); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci nand_set_flash_node(&data->chip, pdev->dev.of_node); 738c2ecf20Sopenharmony_ci mtd = nand_to_mtd(&data->chip); 748c2ecf20Sopenharmony_ci mtd->dev.parent = &pdev->dev; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci data->chip.legacy.IO_ADDR_R = data->io_base; 778c2ecf20Sopenharmony_ci data->chip.legacy.IO_ADDR_W = data->io_base; 788c2ecf20Sopenharmony_ci data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; 798c2ecf20Sopenharmony_ci data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; 808c2ecf20Sopenharmony_ci data->chip.legacy.select_chip = pdata->ctrl.select_chip; 818c2ecf20Sopenharmony_ci data->chip.legacy.write_buf = pdata->ctrl.write_buf; 828c2ecf20Sopenharmony_ci data->chip.legacy.read_buf = pdata->ctrl.read_buf; 838c2ecf20Sopenharmony_ci data->chip.legacy.chip_delay = pdata->chip.chip_delay; 848c2ecf20Sopenharmony_ci data->chip.options |= pdata->chip.options; 858c2ecf20Sopenharmony_ci data->chip.bbt_options |= pdata->chip.bbt_options; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Handle any platform specific setup */ 908c2ecf20Sopenharmony_ci if (pdata->ctrl.probe) { 918c2ecf20Sopenharmony_ci err = pdata->ctrl.probe(pdev); 928c2ecf20Sopenharmony_ci if (err) 938c2ecf20Sopenharmony_ci goto out; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * This driver assumes that the default ECC engine should be TYPE_SOFT. 988c2ecf20Sopenharmony_ci * Set ->engine_type before registering the NAND devices in order to 998c2ecf20Sopenharmony_ci * provide a driver specific default value. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Scan to find existence of the device */ 1048c2ecf20Sopenharmony_ci err = nand_scan(&data->chip, pdata->chip.nr_chips); 1058c2ecf20Sopenharmony_ci if (err) 1068c2ecf20Sopenharmony_ci goto out; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci part_types = pdata->chip.part_probe_types; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci err = mtd_device_parse_register(mtd, part_types, NULL, 1118c2ecf20Sopenharmony_ci pdata->chip.partitions, 1128c2ecf20Sopenharmony_ci pdata->chip.nr_partitions); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (!err) 1158c2ecf20Sopenharmony_ci return err; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci nand_cleanup(&data->chip); 1188c2ecf20Sopenharmony_ciout: 1198c2ecf20Sopenharmony_ci if (pdata->ctrl.remove) 1208c2ecf20Sopenharmony_ci pdata->ctrl.remove(pdev); 1218c2ecf20Sopenharmony_ci return err; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * Remove a NAND device. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic int plat_nand_remove(struct platform_device *pdev) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct plat_nand_data *data = platform_get_drvdata(pdev); 1308c2ecf20Sopenharmony_ci struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 1318c2ecf20Sopenharmony_ci struct nand_chip *chip = &data->chip; 1328c2ecf20Sopenharmony_ci int ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 1358c2ecf20Sopenharmony_ci WARN_ON(ret); 1368c2ecf20Sopenharmony_ci nand_cleanup(chip); 1378c2ecf20Sopenharmony_ci if (pdata->ctrl.remove) 1388c2ecf20Sopenharmony_ci pdata->ctrl.remove(pdev); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct of_device_id plat_nand_match[] = { 1448c2ecf20Sopenharmony_ci { .compatible = "gen_nand" }, 1458c2ecf20Sopenharmony_ci {}, 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, plat_nand_match); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct platform_driver plat_nand_driver = { 1508c2ecf20Sopenharmony_ci .probe = plat_nand_probe, 1518c2ecf20Sopenharmony_ci .remove = plat_nand_remove, 1528c2ecf20Sopenharmony_ci .driver = { 1538c2ecf20Sopenharmony_ci .name = "gen_nand", 1548c2ecf20Sopenharmony_ci .of_match_table = plat_nand_match, 1558c2ecf20Sopenharmony_ci }, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cimodule_platform_driver(plat_nand_driver); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vitaly Wool"); 1628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Simple generic NAND driver"); 1638c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:gen_nand"); 164