18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2004 Richard Purdie 48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Dmitry Baryshkov 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on Sharp's NAND driver sharp_sl.c 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/genhd.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/sharpsl.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct sharpsl_nand { 238c2ecf20Sopenharmony_ci struct nand_controller controller; 248c2ecf20Sopenharmony_ci struct nand_chip chip; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci void __iomem *io; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* register offset */ 358c2ecf20Sopenharmony_ci#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ 368c2ecf20Sopenharmony_ci#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ 378c2ecf20Sopenharmony_ci#define ECCCP 0x08 /* column parity 5 - 0 bit */ 388c2ecf20Sopenharmony_ci#define ECCCNTR 0x0C /* ECC byte counter */ 398c2ecf20Sopenharmony_ci#define ECCCLRR 0x10 /* cleare ECC */ 408c2ecf20Sopenharmony_ci#define FLASHIO 0x14 /* Flash I/O */ 418c2ecf20Sopenharmony_ci#define FLASHCTL 0x18 /* Flash Control */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Flash control bit */ 448c2ecf20Sopenharmony_ci#define FLRYBY (1 << 5) 458c2ecf20Sopenharmony_ci#define FLCE1 (1 << 4) 468c2ecf20Sopenharmony_ci#define FLWP (1 << 3) 478c2ecf20Sopenharmony_ci#define FLALE (1 << 2) 488c2ecf20Sopenharmony_ci#define FLCLE (1 << 1) 498c2ecf20Sopenharmony_ci#define FLCE0 (1 << 0) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * hardware specific access to control-lines 538c2ecf20Sopenharmony_ci * ctrl: 548c2ecf20Sopenharmony_ci * NAND_CNE: bit 0 -> ! bit 0 & 4 558c2ecf20Sopenharmony_ci * NAND_CLE: bit 1 -> bit 1 568c2ecf20Sopenharmony_ci * NAND_ALE: bit 2 -> bit 2 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic void sharpsl_nand_hwcontrol(struct nand_chip *chip, int cmd, 608c2ecf20Sopenharmony_ci unsigned int ctrl) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (ctrl & NAND_CTRL_CHANGE) { 658c2ecf20Sopenharmony_ci unsigned char bits = ctrl & 0x07; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci bits |= (ctrl & 0x01) << 4; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci bits ^= 0x11; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (cmd != NAND_CMD_NONE) 758c2ecf20Sopenharmony_ci writeb(cmd, chip->legacy.IO_ADDR_W); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int sharpsl_nand_dev_ready(struct nand_chip *chip) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 818c2ecf20Sopenharmony_ci return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void sharpsl_nand_enable_hwecc(struct nand_chip *chip, int mode) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 878c2ecf20Sopenharmony_ci writeb(0, sharpsl->io + ECCCLRR); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int sharpsl_nand_calculate_ecc(struct nand_chip *chip, 918c2ecf20Sopenharmony_ci const u_char * dat, u_char * ecc_code) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct sharpsl_nand *sharpsl = mtd_to_sharpsl(nand_to_mtd(chip)); 948c2ecf20Sopenharmony_ci ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); 958c2ecf20Sopenharmony_ci ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); 968c2ecf20Sopenharmony_ci ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; 978c2ecf20Sopenharmony_ci return readb(sharpsl->io + ECCCNTR) != 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int sharpsl_attach_chip(struct nand_chip *chip) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci chip->ecc.size = 256; 1068c2ecf20Sopenharmony_ci chip->ecc.bytes = 3; 1078c2ecf20Sopenharmony_ci chip->ecc.strength = 1; 1088c2ecf20Sopenharmony_ci chip->ecc.hwctl = sharpsl_nand_enable_hwecc; 1098c2ecf20Sopenharmony_ci chip->ecc.calculate = sharpsl_nand_calculate_ecc; 1108c2ecf20Sopenharmony_ci chip->ecc.correct = nand_correct_data; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const struct nand_controller_ops sharpsl_ops = { 1168c2ecf20Sopenharmony_ci .attach_chip = sharpsl_attach_chip, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * Main initialization routine 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistatic int sharpsl_nand_probe(struct platform_device *pdev) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct nand_chip *this; 1258c2ecf20Sopenharmony_ci struct mtd_info *mtd; 1268c2ecf20Sopenharmony_ci struct resource *r; 1278c2ecf20Sopenharmony_ci int err = 0; 1288c2ecf20Sopenharmony_ci struct sharpsl_nand *sharpsl; 1298c2ecf20Sopenharmony_ci struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (!data) { 1328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform data!\n"); 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Allocate memory for MTD device structure and private data */ 1378c2ecf20Sopenharmony_ci sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); 1388c2ecf20Sopenharmony_ci if (!sharpsl) 1398c2ecf20Sopenharmony_ci return -ENOMEM; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1428c2ecf20Sopenharmony_ci if (!r) { 1438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no io memory resource defined!\n"); 1448c2ecf20Sopenharmony_ci err = -ENODEV; 1458c2ecf20Sopenharmony_ci goto err_get_res; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* map physical address */ 1498c2ecf20Sopenharmony_ci sharpsl->io = ioremap(r->start, resource_size(r)); 1508c2ecf20Sopenharmony_ci if (!sharpsl->io) { 1518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ioremap to access Sharp SL NAND chip failed\n"); 1528c2ecf20Sopenharmony_ci err = -EIO; 1538c2ecf20Sopenharmony_ci goto err_ioremap; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Get pointer to private data */ 1578c2ecf20Sopenharmony_ci this = (struct nand_chip *)(&sharpsl->chip); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci nand_controller_init(&sharpsl->controller); 1608c2ecf20Sopenharmony_ci sharpsl->controller.ops = &sharpsl_ops; 1618c2ecf20Sopenharmony_ci this->controller = &sharpsl->controller; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Link the private data with the MTD structure */ 1648c2ecf20Sopenharmony_ci mtd = nand_to_mtd(this); 1658c2ecf20Sopenharmony_ci mtd->dev.parent = &pdev->dev; 1668c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, data->ecc_layout); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sharpsl); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * PXA initialize 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Set address of NAND IO lines */ 1768c2ecf20Sopenharmony_ci this->legacy.IO_ADDR_R = sharpsl->io + FLASHIO; 1778c2ecf20Sopenharmony_ci this->legacy.IO_ADDR_W = sharpsl->io + FLASHIO; 1788c2ecf20Sopenharmony_ci /* Set address of hardware control function */ 1798c2ecf20Sopenharmony_ci this->legacy.cmd_ctrl = sharpsl_nand_hwcontrol; 1808c2ecf20Sopenharmony_ci this->legacy.dev_ready = sharpsl_nand_dev_ready; 1818c2ecf20Sopenharmony_ci /* 15 us command delay time */ 1828c2ecf20Sopenharmony_ci this->legacy.chip_delay = 15; 1838c2ecf20Sopenharmony_ci this->badblock_pattern = data->badblock_pattern; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Scan to find existence of the device */ 1868c2ecf20Sopenharmony_ci err = nand_scan(this, 1); 1878c2ecf20Sopenharmony_ci if (err) 1888c2ecf20Sopenharmony_ci goto err_scan; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Register the partitions */ 1918c2ecf20Sopenharmony_ci mtd->name = "sharpsl-nand"; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci err = mtd_device_parse_register(mtd, data->part_parsers, NULL, 1948c2ecf20Sopenharmony_ci data->partitions, data->nr_partitions); 1958c2ecf20Sopenharmony_ci if (err) 1968c2ecf20Sopenharmony_ci goto err_add; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Return happy */ 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cierr_add: 2028c2ecf20Sopenharmony_ci nand_cleanup(this); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cierr_scan: 2058c2ecf20Sopenharmony_ci iounmap(sharpsl->io); 2068c2ecf20Sopenharmony_cierr_ioremap: 2078c2ecf20Sopenharmony_cierr_get_res: 2088c2ecf20Sopenharmony_ci kfree(sharpsl); 2098c2ecf20Sopenharmony_ci return err; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/* 2138c2ecf20Sopenharmony_ci * Clean up routine 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic int sharpsl_nand_remove(struct platform_device *pdev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); 2188c2ecf20Sopenharmony_ci struct nand_chip *chip = &sharpsl->chip; 2198c2ecf20Sopenharmony_ci int ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Unregister device */ 2228c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 2238c2ecf20Sopenharmony_ci WARN_ON(ret); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Release resources */ 2268c2ecf20Sopenharmony_ci nand_cleanup(chip); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci iounmap(sharpsl->io); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Free the driver's structure */ 2318c2ecf20Sopenharmony_ci kfree(sharpsl); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic struct platform_driver sharpsl_nand_driver = { 2378c2ecf20Sopenharmony_ci .driver = { 2388c2ecf20Sopenharmony_ci .name = "sharpsl-nand", 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci .probe = sharpsl_nand_probe, 2418c2ecf20Sopenharmony_ci .remove = sharpsl_nand_remove, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cimodule_platform_driver(sharpsl_nand_driver); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); 2488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); 249