1d2912cb1SThomas 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 { 25b36bf0a0SMiquel Raynal struct nand_controller controller; 261b578193SWolfgang Grandegger struct nand_chip nand_chip; 271b578193SWolfgang Grandegger void __iomem *io_base; 281b578193SWolfgang Grandegger struct device *dev; 291b578193SWolfgang Grandegger }; 301b578193SWolfgang Grandegger 311b578193SWolfgang Grandegger /** 321b578193SWolfgang Grandegger * socrates_nand_write_buf - write buffer to chip 33c0739d85SBoris Brezillon * @this: NAND chip object 341b578193SWolfgang Grandegger * @buf: data buffer 351b578193SWolfgang Grandegger * @len: number of bytes to write 361b578193SWolfgang Grandegger */ 37c0739d85SBoris Brezillon static void socrates_nand_write_buf(struct nand_chip *this, const uint8_t *buf, 38c0739d85SBoris Brezillon int len) 391b578193SWolfgang Grandegger { 401b578193SWolfgang Grandegger int i; 41d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 421b578193SWolfgang Grandegger 431b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 441b578193SWolfgang Grandegger out_be32(host->io_base, FPGA_NAND_ENABLE | 451b578193SWolfgang Grandegger FPGA_NAND_CMD_WRITE | 461b578193SWolfgang Grandegger (buf[i] << FPGA_NAND_DATA_SHIFT)); 471b578193SWolfgang Grandegger } 481b578193SWolfgang Grandegger } 491b578193SWolfgang Grandegger 501b578193SWolfgang Grandegger /** 511b578193SWolfgang Grandegger * socrates_nand_read_buf - read chip data into buffer 527e534323SBoris Brezillon * @this: NAND chip object 531b578193SWolfgang Grandegger * @buf: buffer to store date 541b578193SWolfgang Grandegger * @len: number of bytes to read 551b578193SWolfgang Grandegger */ 567e534323SBoris Brezillon static void socrates_nand_read_buf(struct nand_chip *this, uint8_t *buf, 577e534323SBoris Brezillon int len) 581b578193SWolfgang Grandegger { 591b578193SWolfgang Grandegger int i; 60d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 611b578193SWolfgang Grandegger uint32_t val; 621b578193SWolfgang Grandegger 631b578193SWolfgang Grandegger val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; 641b578193SWolfgang Grandegger 651b578193SWolfgang Grandegger out_be32(host->io_base, val); 661b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 671b578193SWolfgang Grandegger buf[i] = (in_be32(host->io_base) >> 681b578193SWolfgang Grandegger FPGA_NAND_DATA_SHIFT) & 0xff; 691b578193SWolfgang Grandegger } 701b578193SWolfgang Grandegger } 711b578193SWolfgang Grandegger 721b578193SWolfgang Grandegger /** 731b578193SWolfgang Grandegger * socrates_nand_read_byte - read one byte from the chip 741b578193SWolfgang Grandegger * @mtd: MTD device structure 751b578193SWolfgang Grandegger */ 767e534323SBoris Brezillon static uint8_t socrates_nand_read_byte(struct nand_chip *this) 771b578193SWolfgang Grandegger { 781b578193SWolfgang Grandegger uint8_t byte; 797e534323SBoris Brezillon socrates_nand_read_buf(this, &byte, sizeof(byte)); 801b578193SWolfgang Grandegger return byte; 811b578193SWolfgang Grandegger } 821b578193SWolfgang Grandegger 831b578193SWolfgang Grandegger /* 841b578193SWolfgang Grandegger * Hardware specific access to control-lines 851b578193SWolfgang Grandegger */ 860f808c16SBoris Brezillon static void socrates_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd, 871b578193SWolfgang Grandegger unsigned int ctrl) 881b578193SWolfgang Grandegger { 89d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 901b578193SWolfgang Grandegger uint32_t val; 911b578193SWolfgang Grandegger 921b578193SWolfgang Grandegger if (cmd == NAND_CMD_NONE) 931b578193SWolfgang Grandegger return; 941b578193SWolfgang Grandegger 951b578193SWolfgang Grandegger if (ctrl & NAND_CLE) 961b578193SWolfgang Grandegger val = FPGA_NAND_CMD_COMMAND; 971b578193SWolfgang Grandegger else 981b578193SWolfgang Grandegger val = FPGA_NAND_CMD_ADDR; 991b578193SWolfgang Grandegger 1001b578193SWolfgang Grandegger if (ctrl & NAND_NCE) 1011b578193SWolfgang Grandegger val |= FPGA_NAND_ENABLE; 1021b578193SWolfgang Grandegger 1031b578193SWolfgang Grandegger val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; 1041b578193SWolfgang Grandegger 1051b578193SWolfgang Grandegger out_be32(host->io_base, val); 1061b578193SWolfgang Grandegger } 1071b578193SWolfgang Grandegger 1081b578193SWolfgang Grandegger /* 1091b578193SWolfgang Grandegger * Read the Device Ready pin. 1101b578193SWolfgang Grandegger */ 11150a487e7SBoris Brezillon static int socrates_nand_device_ready(struct nand_chip *nand_chip) 1121b578193SWolfgang Grandegger { 113d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 1141b578193SWolfgang Grandegger 1151b578193SWolfgang Grandegger if (in_be32(host->io_base) & FPGA_NAND_BUSY) 1161b578193SWolfgang Grandegger return 0; /* busy */ 1171b578193SWolfgang Grandegger return 1; 1181b578193SWolfgang Grandegger } 1191b578193SWolfgang Grandegger 120b36bf0a0SMiquel Raynal static int socrates_attach_chip(struct nand_chip *chip) 121b36bf0a0SMiquel Raynal { 122b36bf0a0SMiquel Raynal chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 123*cf75f00fSMiquel Raynal 124*cf75f00fSMiquel Raynal if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 125b36bf0a0SMiquel Raynal chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 126b36bf0a0SMiquel Raynal 127b36bf0a0SMiquel Raynal return 0; 128b36bf0a0SMiquel Raynal } 129b36bf0a0SMiquel Raynal 130b36bf0a0SMiquel Raynal static const struct nand_controller_ops socrates_ops = { 131b36bf0a0SMiquel Raynal .attach_chip = socrates_attach_chip, 132b36bf0a0SMiquel Raynal }; 133b36bf0a0SMiquel Raynal 1341b578193SWolfgang Grandegger /* 1351b578193SWolfgang Grandegger * Probe for the NAND device. 1361b578193SWolfgang Grandegger */ 13706f25510SBill Pemberton static int socrates_nand_probe(struct platform_device *ofdev) 1381b578193SWolfgang Grandegger { 1391b578193SWolfgang Grandegger struct socrates_nand_host *host; 1401b578193SWolfgang Grandegger struct mtd_info *mtd; 1411b578193SWolfgang Grandegger struct nand_chip *nand_chip; 1421b578193SWolfgang Grandegger int res; 1431b578193SWolfgang Grandegger 1441b578193SWolfgang Grandegger /* Allocate memory for the device structure (and zero it) */ 145cf3a9b56SSachin Kamat host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); 146cf3a9b56SSachin Kamat if (!host) 1471b578193SWolfgang Grandegger return -ENOMEM; 1481b578193SWolfgang Grandegger 149c8a4d0fdSAnatolij Gustschin host->io_base = of_iomap(ofdev->dev.of_node, 0); 1501b578193SWolfgang Grandegger if (host->io_base == NULL) { 1515422933dSSachin Kamat dev_err(&ofdev->dev, "ioremap failed\n"); 1521b578193SWolfgang Grandegger return -EIO; 1531b578193SWolfgang Grandegger } 1541b578193SWolfgang Grandegger 1551b578193SWolfgang Grandegger nand_chip = &host->nand_chip; 156a723bf6aSBoris BREZILLON mtd = nand_to_mtd(nand_chip); 1571b578193SWolfgang Grandegger host->dev = &ofdev->dev; 1581b578193SWolfgang Grandegger 159b36bf0a0SMiquel Raynal nand_controller_init(&host->controller); 160b36bf0a0SMiquel Raynal host->controller.ops = &socrates_ops; 161b36bf0a0SMiquel Raynal nand_chip->controller = &host->controller; 162b36bf0a0SMiquel Raynal 163d699ed25SBoris BREZILLON /* link the private data structures */ 164d699ed25SBoris BREZILLON nand_set_controller_data(nand_chip, host); 165a61ae81aSBrian Norris nand_set_flash_node(nand_chip, ofdev->dev.of_node); 1661b578193SWolfgang Grandegger mtd->name = "socrates_nand"; 1671b578193SWolfgang Grandegger mtd->dev.parent = &ofdev->dev; 1681b578193SWolfgang Grandegger 169bf6065c6SBoris Brezillon nand_chip->legacy.cmd_ctrl = socrates_nand_cmd_ctrl; 170716bbbabSBoris Brezillon nand_chip->legacy.read_byte = socrates_nand_read_byte; 171716bbbabSBoris Brezillon nand_chip->legacy.write_buf = socrates_nand_write_buf; 172716bbbabSBoris Brezillon nand_chip->legacy.read_buf = socrates_nand_read_buf; 1738395b753SBoris Brezillon nand_chip->legacy.dev_ready = socrates_nand_device_ready; 1741b578193SWolfgang Grandegger 1751b578193SWolfgang Grandegger /* TODO: I have no idea what real delay is. */ 1763cece3abSBoris Brezillon nand_chip->legacy.chip_delay = 20; /* 20us command delay time */ 1771b578193SWolfgang Grandegger 1781b578193SWolfgang Grandegger dev_set_drvdata(&ofdev->dev, host); 1791b578193SWolfgang Grandegger 18000ad378fSBoris Brezillon res = nand_scan(nand_chip, 1); 18183f48f80SMasahiro Yamada if (res) 1821b578193SWolfgang Grandegger goto out; 1831b578193SWolfgang Grandegger 184a61ae81aSBrian Norris res = mtd_device_register(mtd, NULL, 0); 1851b578193SWolfgang Grandegger if (!res) 1861b578193SWolfgang Grandegger return res; 1871b578193SWolfgang Grandegger 1889c6c2e5cSMiquel Raynal nand_cleanup(nand_chip); 1891b578193SWolfgang Grandegger 1901b578193SWolfgang Grandegger out: 1911b578193SWolfgang Grandegger iounmap(host->io_base); 1921b578193SWolfgang Grandegger return res; 1931b578193SWolfgang Grandegger } 1941b578193SWolfgang Grandegger 1951b578193SWolfgang Grandegger /* 1961b578193SWolfgang Grandegger * Remove a NAND device. 1971b578193SWolfgang Grandegger */ 198810b7e06SBill Pemberton static int socrates_nand_remove(struct platform_device *ofdev) 1991b578193SWolfgang Grandegger { 2001b578193SWolfgang Grandegger struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); 201c121cb98SMiquel Raynal struct nand_chip *chip = &host->nand_chip; 202c121cb98SMiquel Raynal int ret; 2031b578193SWolfgang Grandegger 204c121cb98SMiquel Raynal ret = mtd_device_unregister(nand_to_mtd(chip)); 205c121cb98SMiquel Raynal WARN_ON(ret); 206c121cb98SMiquel Raynal nand_cleanup(chip); 2071b578193SWolfgang Grandegger 2081b578193SWolfgang Grandegger iounmap(host->io_base); 2091b578193SWolfgang Grandegger 2101b578193SWolfgang Grandegger return 0; 2111b578193SWolfgang Grandegger } 2121b578193SWolfgang Grandegger 213b2d4fbabSMárton Németh static const struct of_device_id socrates_nand_match[] = 2141b578193SWolfgang Grandegger { 2151b578193SWolfgang Grandegger { 2161b578193SWolfgang Grandegger .compatible = "abb,socrates-nand", 2171b578193SWolfgang Grandegger }, 2181b578193SWolfgang Grandegger {}, 2191b578193SWolfgang Grandegger }; 2201b578193SWolfgang Grandegger 2211b578193SWolfgang Grandegger MODULE_DEVICE_TABLE(of, socrates_nand_match); 2221b578193SWolfgang Grandegger 2231c48a5c9SGrant Likely static struct platform_driver socrates_nand_driver = { 2244018294bSGrant Likely .driver = { 2251b578193SWolfgang Grandegger .name = "socrates_nand", 2264018294bSGrant Likely .of_match_table = socrates_nand_match, 2274018294bSGrant Likely }, 2281b578193SWolfgang Grandegger .probe = socrates_nand_probe, 2295153b88cSBill Pemberton .remove = socrates_nand_remove, 2301b578193SWolfgang Grandegger }; 2311b578193SWolfgang Grandegger 232f99640deSAxel Lin module_platform_driver(socrates_nand_driver); 2331b578193SWolfgang Grandegger 2341b578193SWolfgang Grandegger MODULE_LICENSE("GPL"); 2351b578193SWolfgang Grandegger MODULE_AUTHOR("Ilya Yanok"); 2361b578193SWolfgang Grandegger MODULE_DESCRIPTION("NAND driver for Socrates board"); 237