11b578193SWolfgang Grandegger /* 21b578193SWolfgang Grandegger * Copyright © 2008 Ilya Yanok, Emcraft Systems 31b578193SWolfgang Grandegger * 41b578193SWolfgang Grandegger * 51b578193SWolfgang Grandegger * This program is free software; you can redistribute it and/or modify 61b578193SWolfgang Grandegger * it under the terms of the GNU General Public License version 2 as 71b578193SWolfgang Grandegger * published by the Free Software Foundation. 81b578193SWolfgang Grandegger * 91b578193SWolfgang Grandegger */ 101b578193SWolfgang Grandegger 111b578193SWolfgang Grandegger #include <linux/slab.h> 121b578193SWolfgang Grandegger #include <linux/module.h> 131b578193SWolfgang Grandegger #include <linux/mtd/mtd.h> 14d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h> 151b578193SWolfgang Grandegger #include <linux/mtd/partitions.h> 16c11eede6SRob Herring #include <linux/of_address.h> 171b578193SWolfgang Grandegger #include <linux/of_platform.h> 181b578193SWolfgang Grandegger #include <linux/io.h> 191b578193SWolfgang Grandegger 201b578193SWolfgang Grandegger #define FPGA_NAND_CMD_MASK (0x7 << 28) 211b578193SWolfgang Grandegger #define FPGA_NAND_CMD_COMMAND (0x0 << 28) 221b578193SWolfgang Grandegger #define FPGA_NAND_CMD_ADDR (0x1 << 28) 231b578193SWolfgang Grandegger #define FPGA_NAND_CMD_READ (0x2 << 28) 241b578193SWolfgang Grandegger #define FPGA_NAND_CMD_WRITE (0x3 << 28) 251b578193SWolfgang Grandegger #define FPGA_NAND_BUSY (0x1 << 15) 261b578193SWolfgang Grandegger #define FPGA_NAND_ENABLE (0x1 << 31) 271b578193SWolfgang Grandegger #define FPGA_NAND_DATA_SHIFT 16 281b578193SWolfgang Grandegger 291b578193SWolfgang Grandegger struct socrates_nand_host { 301b578193SWolfgang Grandegger struct nand_chip nand_chip; 311b578193SWolfgang Grandegger void __iomem *io_base; 321b578193SWolfgang Grandegger struct device *dev; 331b578193SWolfgang Grandegger }; 341b578193SWolfgang Grandegger 351b578193SWolfgang Grandegger /** 361b578193SWolfgang Grandegger * socrates_nand_write_buf - write buffer to chip 37c0739d85SBoris Brezillon * @this: NAND chip object 381b578193SWolfgang Grandegger * @buf: data buffer 391b578193SWolfgang Grandegger * @len: number of bytes to write 401b578193SWolfgang Grandegger */ 41c0739d85SBoris Brezillon static void socrates_nand_write_buf(struct nand_chip *this, const uint8_t *buf, 42c0739d85SBoris Brezillon int len) 431b578193SWolfgang Grandegger { 441b578193SWolfgang Grandegger int i; 45d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 461b578193SWolfgang Grandegger 471b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 481b578193SWolfgang Grandegger out_be32(host->io_base, FPGA_NAND_ENABLE | 491b578193SWolfgang Grandegger FPGA_NAND_CMD_WRITE | 501b578193SWolfgang Grandegger (buf[i] << FPGA_NAND_DATA_SHIFT)); 511b578193SWolfgang Grandegger } 521b578193SWolfgang Grandegger } 531b578193SWolfgang Grandegger 541b578193SWolfgang Grandegger /** 551b578193SWolfgang Grandegger * socrates_nand_read_buf - read chip data into buffer 567e534323SBoris Brezillon * @this: NAND chip object 571b578193SWolfgang Grandegger * @buf: buffer to store date 581b578193SWolfgang Grandegger * @len: number of bytes to read 591b578193SWolfgang Grandegger */ 607e534323SBoris Brezillon static void socrates_nand_read_buf(struct nand_chip *this, uint8_t *buf, 617e534323SBoris Brezillon int len) 621b578193SWolfgang Grandegger { 631b578193SWolfgang Grandegger int i; 64d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 651b578193SWolfgang Grandegger uint32_t val; 661b578193SWolfgang Grandegger 671b578193SWolfgang Grandegger val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; 681b578193SWolfgang Grandegger 691b578193SWolfgang Grandegger out_be32(host->io_base, val); 701b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 711b578193SWolfgang Grandegger buf[i] = (in_be32(host->io_base) >> 721b578193SWolfgang Grandegger FPGA_NAND_DATA_SHIFT) & 0xff; 731b578193SWolfgang Grandegger } 741b578193SWolfgang Grandegger } 751b578193SWolfgang Grandegger 761b578193SWolfgang Grandegger /** 771b578193SWolfgang Grandegger * socrates_nand_read_byte - read one byte from the chip 781b578193SWolfgang Grandegger * @mtd: MTD device structure 791b578193SWolfgang Grandegger */ 807e534323SBoris Brezillon static uint8_t socrates_nand_read_byte(struct nand_chip *this) 811b578193SWolfgang Grandegger { 821b578193SWolfgang Grandegger uint8_t byte; 837e534323SBoris Brezillon socrates_nand_read_buf(this, &byte, sizeof(byte)); 841b578193SWolfgang Grandegger return byte; 851b578193SWolfgang Grandegger } 861b578193SWolfgang Grandegger 871b578193SWolfgang Grandegger /* 881b578193SWolfgang Grandegger * Hardware specific access to control-lines 891b578193SWolfgang Grandegger */ 900f808c16SBoris Brezillon static void socrates_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd, 911b578193SWolfgang Grandegger unsigned int ctrl) 921b578193SWolfgang Grandegger { 93d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 941b578193SWolfgang Grandegger uint32_t val; 951b578193SWolfgang Grandegger 961b578193SWolfgang Grandegger if (cmd == NAND_CMD_NONE) 971b578193SWolfgang Grandegger return; 981b578193SWolfgang Grandegger 991b578193SWolfgang Grandegger if (ctrl & NAND_CLE) 1001b578193SWolfgang Grandegger val = FPGA_NAND_CMD_COMMAND; 1011b578193SWolfgang Grandegger else 1021b578193SWolfgang Grandegger val = FPGA_NAND_CMD_ADDR; 1031b578193SWolfgang Grandegger 1041b578193SWolfgang Grandegger if (ctrl & NAND_NCE) 1051b578193SWolfgang Grandegger val |= FPGA_NAND_ENABLE; 1061b578193SWolfgang Grandegger 1071b578193SWolfgang Grandegger val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; 1081b578193SWolfgang Grandegger 1091b578193SWolfgang Grandegger out_be32(host->io_base, val); 1101b578193SWolfgang Grandegger } 1111b578193SWolfgang Grandegger 1121b578193SWolfgang Grandegger /* 1131b578193SWolfgang Grandegger * Read the Device Ready pin. 1141b578193SWolfgang Grandegger */ 11550a487e7SBoris Brezillon static int socrates_nand_device_ready(struct nand_chip *nand_chip) 1161b578193SWolfgang Grandegger { 117d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 1181b578193SWolfgang Grandegger 1191b578193SWolfgang Grandegger if (in_be32(host->io_base) & FPGA_NAND_BUSY) 1201b578193SWolfgang Grandegger return 0; /* busy */ 1211b578193SWolfgang Grandegger return 1; 1221b578193SWolfgang Grandegger } 1231b578193SWolfgang Grandegger 1241b578193SWolfgang Grandegger /* 1251b578193SWolfgang Grandegger * Probe for the NAND device. 1261b578193SWolfgang Grandegger */ 12706f25510SBill Pemberton static int socrates_nand_probe(struct platform_device *ofdev) 1281b578193SWolfgang Grandegger { 1291b578193SWolfgang Grandegger struct socrates_nand_host *host; 1301b578193SWolfgang Grandegger struct mtd_info *mtd; 1311b578193SWolfgang Grandegger struct nand_chip *nand_chip; 1321b578193SWolfgang Grandegger int res; 1331b578193SWolfgang Grandegger 1341b578193SWolfgang Grandegger /* Allocate memory for the device structure (and zero it) */ 135cf3a9b56SSachin Kamat host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); 136cf3a9b56SSachin Kamat if (!host) 1371b578193SWolfgang Grandegger return -ENOMEM; 1381b578193SWolfgang Grandegger 139c8a4d0fdSAnatolij Gustschin host->io_base = of_iomap(ofdev->dev.of_node, 0); 1401b578193SWolfgang Grandegger if (host->io_base == NULL) { 1415422933dSSachin Kamat dev_err(&ofdev->dev, "ioremap failed\n"); 1421b578193SWolfgang Grandegger return -EIO; 1431b578193SWolfgang Grandegger } 1441b578193SWolfgang Grandegger 1451b578193SWolfgang Grandegger nand_chip = &host->nand_chip; 146a723bf6aSBoris BREZILLON mtd = nand_to_mtd(nand_chip); 1471b578193SWolfgang Grandegger host->dev = &ofdev->dev; 1481b578193SWolfgang Grandegger 149d699ed25SBoris BREZILLON /* link the private data structures */ 150d699ed25SBoris BREZILLON nand_set_controller_data(nand_chip, host); 151a61ae81aSBrian Norris nand_set_flash_node(nand_chip, ofdev->dev.of_node); 1521b578193SWolfgang Grandegger mtd->name = "socrates_nand"; 1531b578193SWolfgang Grandegger mtd->dev.parent = &ofdev->dev; 1541b578193SWolfgang Grandegger 155*bf6065c6SBoris Brezillon nand_chip->legacy.cmd_ctrl = socrates_nand_cmd_ctrl; 156716bbbabSBoris Brezillon nand_chip->legacy.read_byte = socrates_nand_read_byte; 157716bbbabSBoris Brezillon nand_chip->legacy.write_buf = socrates_nand_write_buf; 158716bbbabSBoris Brezillon nand_chip->legacy.read_buf = socrates_nand_read_buf; 1591b578193SWolfgang Grandegger nand_chip->dev_ready = socrates_nand_device_ready; 1601b578193SWolfgang Grandegger 1611b578193SWolfgang Grandegger nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ 162ce111afdSRafał Miłecki nand_chip->ecc.algo = NAND_ECC_HAMMING; 1631b578193SWolfgang Grandegger 1641b578193SWolfgang Grandegger /* TODO: I have no idea what real delay is. */ 1651b578193SWolfgang Grandegger nand_chip->chip_delay = 20; /* 20us command delay time */ 1661b578193SWolfgang Grandegger 1671b578193SWolfgang Grandegger dev_set_drvdata(&ofdev->dev, host); 1681b578193SWolfgang Grandegger 16900ad378fSBoris Brezillon res = nand_scan(nand_chip, 1); 17083f48f80SMasahiro Yamada if (res) 1711b578193SWolfgang Grandegger goto out; 1721b578193SWolfgang Grandegger 173a61ae81aSBrian Norris res = mtd_device_register(mtd, NULL, 0); 1741b578193SWolfgang Grandegger if (!res) 1751b578193SWolfgang Grandegger return res; 1761b578193SWolfgang Grandegger 17759ac276fSBoris Brezillon nand_release(nand_chip); 1781b578193SWolfgang Grandegger 1791b578193SWolfgang Grandegger out: 1801b578193SWolfgang Grandegger iounmap(host->io_base); 1811b578193SWolfgang Grandegger return res; 1821b578193SWolfgang Grandegger } 1831b578193SWolfgang Grandegger 1841b578193SWolfgang Grandegger /* 1851b578193SWolfgang Grandegger * Remove a NAND device. 1861b578193SWolfgang Grandegger */ 187810b7e06SBill Pemberton static int socrates_nand_remove(struct platform_device *ofdev) 1881b578193SWolfgang Grandegger { 1891b578193SWolfgang Grandegger struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); 1901b578193SWolfgang Grandegger 19159ac276fSBoris Brezillon nand_release(&host->nand_chip); 1921b578193SWolfgang Grandegger 1931b578193SWolfgang Grandegger iounmap(host->io_base); 1941b578193SWolfgang Grandegger 1951b578193SWolfgang Grandegger return 0; 1961b578193SWolfgang Grandegger } 1971b578193SWolfgang Grandegger 198b2d4fbabSMárton Németh static const struct of_device_id socrates_nand_match[] = 1991b578193SWolfgang Grandegger { 2001b578193SWolfgang Grandegger { 2011b578193SWolfgang Grandegger .compatible = "abb,socrates-nand", 2021b578193SWolfgang Grandegger }, 2031b578193SWolfgang Grandegger {}, 2041b578193SWolfgang Grandegger }; 2051b578193SWolfgang Grandegger 2061b578193SWolfgang Grandegger MODULE_DEVICE_TABLE(of, socrates_nand_match); 2071b578193SWolfgang Grandegger 2081c48a5c9SGrant Likely static struct platform_driver socrates_nand_driver = { 2094018294bSGrant Likely .driver = { 2101b578193SWolfgang Grandegger .name = "socrates_nand", 2114018294bSGrant Likely .of_match_table = socrates_nand_match, 2124018294bSGrant Likely }, 2131b578193SWolfgang Grandegger .probe = socrates_nand_probe, 2145153b88cSBill Pemberton .remove = socrates_nand_remove, 2151b578193SWolfgang Grandegger }; 2161b578193SWolfgang Grandegger 217f99640deSAxel Lin module_platform_driver(socrates_nand_driver); 2181b578193SWolfgang Grandegger 2191b578193SWolfgang Grandegger MODULE_LICENSE("GPL"); 2201b578193SWolfgang Grandegger MODULE_AUTHOR("Ilya Yanok"); 2211b578193SWolfgang Grandegger MODULE_DESCRIPTION("NAND driver for Socrates board"); 222