1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21b578193SWolfgang Grandegger /* 31b578193SWolfgang Grandegger * Copyright © 2008 Ilya Yanok, Emcraft Systems 41b578193SWolfgang Grandegger */ 51b578193SWolfgang Grandegger 61b578193SWolfgang Grandegger #include <linux/slab.h> 71b578193SWolfgang Grandegger #include <linux/module.h> 81b578193SWolfgang Grandegger #include <linux/mtd/mtd.h> 9d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h> 101b578193SWolfgang Grandegger #include <linux/mtd/partitions.h> 11c11eede6SRob Herring #include <linux/of_address.h> 121b578193SWolfgang Grandegger #include <linux/of_platform.h> 131b578193SWolfgang Grandegger #include <linux/io.h> 141b578193SWolfgang Grandegger 151b578193SWolfgang Grandegger #define FPGA_NAND_CMD_MASK (0x7 << 28) 161b578193SWolfgang Grandegger #define FPGA_NAND_CMD_COMMAND (0x0 << 28) 171b578193SWolfgang Grandegger #define FPGA_NAND_CMD_ADDR (0x1 << 28) 181b578193SWolfgang Grandegger #define FPGA_NAND_CMD_READ (0x2 << 28) 191b578193SWolfgang Grandegger #define FPGA_NAND_CMD_WRITE (0x3 << 28) 201b578193SWolfgang Grandegger #define FPGA_NAND_BUSY (0x1 << 15) 211b578193SWolfgang Grandegger #define FPGA_NAND_ENABLE (0x1 << 31) 221b578193SWolfgang Grandegger #define FPGA_NAND_DATA_SHIFT 16 231b578193SWolfgang Grandegger 241b578193SWolfgang Grandegger struct socrates_nand_host { 251b578193SWolfgang Grandegger struct nand_chip nand_chip; 261b578193SWolfgang Grandegger void __iomem *io_base; 271b578193SWolfgang Grandegger struct device *dev; 281b578193SWolfgang Grandegger }; 291b578193SWolfgang Grandegger 301b578193SWolfgang Grandegger /** 311b578193SWolfgang Grandegger * socrates_nand_write_buf - write buffer to chip 32c0739d85SBoris Brezillon * @this: NAND chip object 331b578193SWolfgang Grandegger * @buf: data buffer 341b578193SWolfgang Grandegger * @len: number of bytes to write 351b578193SWolfgang Grandegger */ 36c0739d85SBoris Brezillon static void socrates_nand_write_buf(struct nand_chip *this, const uint8_t *buf, 37c0739d85SBoris Brezillon int len) 381b578193SWolfgang Grandegger { 391b578193SWolfgang Grandegger int i; 40d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 411b578193SWolfgang Grandegger 421b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 431b578193SWolfgang Grandegger out_be32(host->io_base, FPGA_NAND_ENABLE | 441b578193SWolfgang Grandegger FPGA_NAND_CMD_WRITE | 451b578193SWolfgang Grandegger (buf[i] << FPGA_NAND_DATA_SHIFT)); 461b578193SWolfgang Grandegger } 471b578193SWolfgang Grandegger } 481b578193SWolfgang Grandegger 491b578193SWolfgang Grandegger /** 501b578193SWolfgang Grandegger * socrates_nand_read_buf - read chip data into buffer 517e534323SBoris Brezillon * @this: NAND chip object 521b578193SWolfgang Grandegger * @buf: buffer to store date 531b578193SWolfgang Grandegger * @len: number of bytes to read 541b578193SWolfgang Grandegger */ 557e534323SBoris Brezillon static void socrates_nand_read_buf(struct nand_chip *this, uint8_t *buf, 567e534323SBoris Brezillon int len) 571b578193SWolfgang Grandegger { 581b578193SWolfgang Grandegger int i; 59d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 601b578193SWolfgang Grandegger uint32_t val; 611b578193SWolfgang Grandegger 621b578193SWolfgang Grandegger val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; 631b578193SWolfgang Grandegger 641b578193SWolfgang Grandegger out_be32(host->io_base, val); 651b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 661b578193SWolfgang Grandegger buf[i] = (in_be32(host->io_base) >> 671b578193SWolfgang Grandegger FPGA_NAND_DATA_SHIFT) & 0xff; 681b578193SWolfgang Grandegger } 691b578193SWolfgang Grandegger } 701b578193SWolfgang Grandegger 711b578193SWolfgang Grandegger /** 721b578193SWolfgang Grandegger * socrates_nand_read_byte - read one byte from the chip 731b578193SWolfgang Grandegger * @mtd: MTD device structure 741b578193SWolfgang Grandegger */ 757e534323SBoris Brezillon static uint8_t socrates_nand_read_byte(struct nand_chip *this) 761b578193SWolfgang Grandegger { 771b578193SWolfgang Grandegger uint8_t byte; 787e534323SBoris Brezillon socrates_nand_read_buf(this, &byte, sizeof(byte)); 791b578193SWolfgang Grandegger return byte; 801b578193SWolfgang Grandegger } 811b578193SWolfgang Grandegger 821b578193SWolfgang Grandegger /* 831b578193SWolfgang Grandegger * Hardware specific access to control-lines 841b578193SWolfgang Grandegger */ 850f808c16SBoris Brezillon static void socrates_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd, 861b578193SWolfgang Grandegger unsigned int ctrl) 871b578193SWolfgang Grandegger { 88d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 891b578193SWolfgang Grandegger uint32_t val; 901b578193SWolfgang Grandegger 911b578193SWolfgang Grandegger if (cmd == NAND_CMD_NONE) 921b578193SWolfgang Grandegger return; 931b578193SWolfgang Grandegger 941b578193SWolfgang Grandegger if (ctrl & NAND_CLE) 951b578193SWolfgang Grandegger val = FPGA_NAND_CMD_COMMAND; 961b578193SWolfgang Grandegger else 971b578193SWolfgang Grandegger val = FPGA_NAND_CMD_ADDR; 981b578193SWolfgang Grandegger 991b578193SWolfgang Grandegger if (ctrl & NAND_NCE) 1001b578193SWolfgang Grandegger val |= FPGA_NAND_ENABLE; 1011b578193SWolfgang Grandegger 1021b578193SWolfgang Grandegger val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; 1031b578193SWolfgang Grandegger 1041b578193SWolfgang Grandegger out_be32(host->io_base, val); 1051b578193SWolfgang Grandegger } 1061b578193SWolfgang Grandegger 1071b578193SWolfgang Grandegger /* 1081b578193SWolfgang Grandegger * Read the Device Ready pin. 1091b578193SWolfgang Grandegger */ 11050a487e7SBoris Brezillon static int socrates_nand_device_ready(struct nand_chip *nand_chip) 1111b578193SWolfgang Grandegger { 112d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 1131b578193SWolfgang Grandegger 1141b578193SWolfgang Grandegger if (in_be32(host->io_base) & FPGA_NAND_BUSY) 1151b578193SWolfgang Grandegger return 0; /* busy */ 1161b578193SWolfgang Grandegger return 1; 1171b578193SWolfgang Grandegger } 1181b578193SWolfgang Grandegger 1191b578193SWolfgang Grandegger /* 1201b578193SWolfgang Grandegger * Probe for the NAND device. 1211b578193SWolfgang Grandegger */ 12206f25510SBill Pemberton static int socrates_nand_probe(struct platform_device *ofdev) 1231b578193SWolfgang Grandegger { 1241b578193SWolfgang Grandegger struct socrates_nand_host *host; 1251b578193SWolfgang Grandegger struct mtd_info *mtd; 1261b578193SWolfgang Grandegger struct nand_chip *nand_chip; 1271b578193SWolfgang Grandegger int res; 1281b578193SWolfgang Grandegger 1291b578193SWolfgang Grandegger /* Allocate memory for the device structure (and zero it) */ 130cf3a9b56SSachin Kamat host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); 131cf3a9b56SSachin Kamat if (!host) 1321b578193SWolfgang Grandegger return -ENOMEM; 1331b578193SWolfgang Grandegger 134c8a4d0fdSAnatolij Gustschin host->io_base = of_iomap(ofdev->dev.of_node, 0); 1351b578193SWolfgang Grandegger if (host->io_base == NULL) { 1365422933dSSachin Kamat dev_err(&ofdev->dev, "ioremap failed\n"); 1371b578193SWolfgang Grandegger return -EIO; 1381b578193SWolfgang Grandegger } 1391b578193SWolfgang Grandegger 1401b578193SWolfgang Grandegger nand_chip = &host->nand_chip; 141a723bf6aSBoris BREZILLON mtd = nand_to_mtd(nand_chip); 1421b578193SWolfgang Grandegger host->dev = &ofdev->dev; 1431b578193SWolfgang Grandegger 144d699ed25SBoris BREZILLON /* link the private data structures */ 145d699ed25SBoris BREZILLON nand_set_controller_data(nand_chip, host); 146a61ae81aSBrian Norris nand_set_flash_node(nand_chip, ofdev->dev.of_node); 1471b578193SWolfgang Grandegger mtd->name = "socrates_nand"; 1481b578193SWolfgang Grandegger mtd->dev.parent = &ofdev->dev; 1491b578193SWolfgang Grandegger 150bf6065c6SBoris Brezillon nand_chip->legacy.cmd_ctrl = socrates_nand_cmd_ctrl; 151716bbbabSBoris Brezillon nand_chip->legacy.read_byte = socrates_nand_read_byte; 152716bbbabSBoris Brezillon nand_chip->legacy.write_buf = socrates_nand_write_buf; 153716bbbabSBoris Brezillon nand_chip->legacy.read_buf = socrates_nand_read_buf; 1548395b753SBoris Brezillon nand_chip->legacy.dev_ready = socrates_nand_device_ready; 1551b578193SWolfgang Grandegger 1561b578193SWolfgang Grandegger nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ 157ce111afdSRafał Miłecki nand_chip->ecc.algo = NAND_ECC_HAMMING; 1581b578193SWolfgang Grandegger 1591b578193SWolfgang Grandegger /* TODO: I have no idea what real delay is. */ 1603cece3abSBoris Brezillon nand_chip->legacy.chip_delay = 20; /* 20us command delay time */ 1611b578193SWolfgang Grandegger 1621b578193SWolfgang Grandegger dev_set_drvdata(&ofdev->dev, host); 1631b578193SWolfgang Grandegger 16400ad378fSBoris Brezillon res = nand_scan(nand_chip, 1); 16583f48f80SMasahiro Yamada if (res) 1661b578193SWolfgang Grandegger goto out; 1671b578193SWolfgang Grandegger 168a61ae81aSBrian Norris res = mtd_device_register(mtd, NULL, 0); 1691b578193SWolfgang Grandegger if (!res) 1701b578193SWolfgang Grandegger return res; 1711b578193SWolfgang Grandegger 17259ac276fSBoris Brezillon nand_release(nand_chip); 1731b578193SWolfgang Grandegger 1741b578193SWolfgang Grandegger out: 1751b578193SWolfgang Grandegger iounmap(host->io_base); 1761b578193SWolfgang Grandegger return res; 1771b578193SWolfgang Grandegger } 1781b578193SWolfgang Grandegger 1791b578193SWolfgang Grandegger /* 1801b578193SWolfgang Grandegger * Remove a NAND device. 1811b578193SWolfgang Grandegger */ 182810b7e06SBill Pemberton static int socrates_nand_remove(struct platform_device *ofdev) 1831b578193SWolfgang Grandegger { 1841b578193SWolfgang Grandegger struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); 1851b578193SWolfgang Grandegger 18659ac276fSBoris Brezillon nand_release(&host->nand_chip); 1871b578193SWolfgang Grandegger 1881b578193SWolfgang Grandegger iounmap(host->io_base); 1891b578193SWolfgang Grandegger 1901b578193SWolfgang Grandegger return 0; 1911b578193SWolfgang Grandegger } 1921b578193SWolfgang Grandegger 193b2d4fbabSMárton Németh static const struct of_device_id socrates_nand_match[] = 1941b578193SWolfgang Grandegger { 1951b578193SWolfgang Grandegger { 1961b578193SWolfgang Grandegger .compatible = "abb,socrates-nand", 1971b578193SWolfgang Grandegger }, 1981b578193SWolfgang Grandegger {}, 1991b578193SWolfgang Grandegger }; 2001b578193SWolfgang Grandegger 2011b578193SWolfgang Grandegger MODULE_DEVICE_TABLE(of, socrates_nand_match); 2021b578193SWolfgang Grandegger 2031c48a5c9SGrant Likely static struct platform_driver socrates_nand_driver = { 2044018294bSGrant Likely .driver = { 2051b578193SWolfgang Grandegger .name = "socrates_nand", 2064018294bSGrant Likely .of_match_table = socrates_nand_match, 2074018294bSGrant Likely }, 2081b578193SWolfgang Grandegger .probe = socrates_nand_probe, 2095153b88cSBill Pemberton .remove = socrates_nand_remove, 2101b578193SWolfgang Grandegger }; 2111b578193SWolfgang Grandegger 212f99640deSAxel Lin module_platform_driver(socrates_nand_driver); 2131b578193SWolfgang Grandegger 2141b578193SWolfgang Grandegger MODULE_LICENSE("GPL"); 2151b578193SWolfgang Grandegger MODULE_AUTHOR("Ilya Yanok"); 2161b578193SWolfgang Grandegger MODULE_DESCRIPTION("NAND driver for Socrates board"); 217