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 371b578193SWolfgang Grandegger * @mtd: MTD device structure 381b578193SWolfgang Grandegger * @buf: data buffer 391b578193SWolfgang Grandegger * @len: number of bytes to write 401b578193SWolfgang Grandegger */ 411b578193SWolfgang Grandegger static void socrates_nand_write_buf(struct mtd_info *mtd, 421b578193SWolfgang Grandegger const uint8_t *buf, int len) 431b578193SWolfgang Grandegger { 441b578193SWolfgang Grandegger int i; 454bd4ebccSBoris BREZILLON struct nand_chip *this = mtd_to_nand(mtd); 46d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 471b578193SWolfgang Grandegger 481b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 491b578193SWolfgang Grandegger out_be32(host->io_base, FPGA_NAND_ENABLE | 501b578193SWolfgang Grandegger FPGA_NAND_CMD_WRITE | 511b578193SWolfgang Grandegger (buf[i] << FPGA_NAND_DATA_SHIFT)); 521b578193SWolfgang Grandegger } 531b578193SWolfgang Grandegger } 541b578193SWolfgang Grandegger 551b578193SWolfgang Grandegger /** 561b578193SWolfgang Grandegger * socrates_nand_read_buf - read chip data into buffer 571b578193SWolfgang Grandegger * @mtd: MTD device structure 581b578193SWolfgang Grandegger * @buf: buffer to store date 591b578193SWolfgang Grandegger * @len: number of bytes to read 601b578193SWolfgang Grandegger */ 611b578193SWolfgang Grandegger static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) 621b578193SWolfgang Grandegger { 631b578193SWolfgang Grandegger int i; 644bd4ebccSBoris BREZILLON struct nand_chip *this = mtd_to_nand(mtd); 65d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(this); 661b578193SWolfgang Grandegger uint32_t val; 671b578193SWolfgang Grandegger 681b578193SWolfgang Grandegger val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; 691b578193SWolfgang Grandegger 701b578193SWolfgang Grandegger out_be32(host->io_base, val); 711b578193SWolfgang Grandegger for (i = 0; i < len; i++) { 721b578193SWolfgang Grandegger buf[i] = (in_be32(host->io_base) >> 731b578193SWolfgang Grandegger FPGA_NAND_DATA_SHIFT) & 0xff; 741b578193SWolfgang Grandegger } 751b578193SWolfgang Grandegger } 761b578193SWolfgang Grandegger 771b578193SWolfgang Grandegger /** 781b578193SWolfgang Grandegger * socrates_nand_read_byte - read one byte from the chip 791b578193SWolfgang Grandegger * @mtd: MTD device structure 801b578193SWolfgang Grandegger */ 811b578193SWolfgang Grandegger static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) 821b578193SWolfgang Grandegger { 831b578193SWolfgang Grandegger uint8_t byte; 841b578193SWolfgang Grandegger socrates_nand_read_buf(mtd, &byte, sizeof(byte)); 851b578193SWolfgang Grandegger return byte; 861b578193SWolfgang Grandegger } 871b578193SWolfgang Grandegger 881b578193SWolfgang Grandegger /* 891b578193SWolfgang Grandegger * Hardware specific access to control-lines 901b578193SWolfgang Grandegger */ 911b578193SWolfgang Grandegger static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, 921b578193SWolfgang Grandegger unsigned int ctrl) 931b578193SWolfgang Grandegger { 944bd4ebccSBoris BREZILLON struct nand_chip *nand_chip = mtd_to_nand(mtd); 95d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 961b578193SWolfgang Grandegger uint32_t val; 971b578193SWolfgang Grandegger 981b578193SWolfgang Grandegger if (cmd == NAND_CMD_NONE) 991b578193SWolfgang Grandegger return; 1001b578193SWolfgang Grandegger 1011b578193SWolfgang Grandegger if (ctrl & NAND_CLE) 1021b578193SWolfgang Grandegger val = FPGA_NAND_CMD_COMMAND; 1031b578193SWolfgang Grandegger else 1041b578193SWolfgang Grandegger val = FPGA_NAND_CMD_ADDR; 1051b578193SWolfgang Grandegger 1061b578193SWolfgang Grandegger if (ctrl & NAND_NCE) 1071b578193SWolfgang Grandegger val |= FPGA_NAND_ENABLE; 1081b578193SWolfgang Grandegger 1091b578193SWolfgang Grandegger val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; 1101b578193SWolfgang Grandegger 1111b578193SWolfgang Grandegger out_be32(host->io_base, val); 1121b578193SWolfgang Grandegger } 1131b578193SWolfgang Grandegger 1141b578193SWolfgang Grandegger /* 1151b578193SWolfgang Grandegger * Read the Device Ready pin. 1161b578193SWolfgang Grandegger */ 1171b578193SWolfgang Grandegger static int socrates_nand_device_ready(struct mtd_info *mtd) 1181b578193SWolfgang Grandegger { 1194bd4ebccSBoris BREZILLON struct nand_chip *nand_chip = mtd_to_nand(mtd); 120d699ed25SBoris BREZILLON struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 1211b578193SWolfgang Grandegger 1221b578193SWolfgang Grandegger if (in_be32(host->io_base) & FPGA_NAND_BUSY) 1231b578193SWolfgang Grandegger return 0; /* busy */ 1241b578193SWolfgang Grandegger return 1; 1251b578193SWolfgang Grandegger } 1261b578193SWolfgang Grandegger 1271b578193SWolfgang Grandegger /* 1281b578193SWolfgang Grandegger * Probe for the NAND device. 1291b578193SWolfgang Grandegger */ 13006f25510SBill Pemberton static int socrates_nand_probe(struct platform_device *ofdev) 1311b578193SWolfgang Grandegger { 1321b578193SWolfgang Grandegger struct socrates_nand_host *host; 1331b578193SWolfgang Grandegger struct mtd_info *mtd; 1341b578193SWolfgang Grandegger struct nand_chip *nand_chip; 1351b578193SWolfgang Grandegger int res; 1361b578193SWolfgang Grandegger 1371b578193SWolfgang Grandegger /* Allocate memory for the device structure (and zero it) */ 138cf3a9b56SSachin Kamat host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); 139cf3a9b56SSachin Kamat if (!host) 1401b578193SWolfgang Grandegger return -ENOMEM; 1411b578193SWolfgang Grandegger 142c8a4d0fdSAnatolij Gustschin host->io_base = of_iomap(ofdev->dev.of_node, 0); 1431b578193SWolfgang Grandegger if (host->io_base == NULL) { 1445422933dSSachin Kamat dev_err(&ofdev->dev, "ioremap failed\n"); 1451b578193SWolfgang Grandegger return -EIO; 1461b578193SWolfgang Grandegger } 1471b578193SWolfgang Grandegger 1481b578193SWolfgang Grandegger nand_chip = &host->nand_chip; 149a723bf6aSBoris BREZILLON mtd = nand_to_mtd(nand_chip); 1501b578193SWolfgang Grandegger host->dev = &ofdev->dev; 1511b578193SWolfgang Grandegger 152d699ed25SBoris BREZILLON /* link the private data structures */ 153d699ed25SBoris BREZILLON nand_set_controller_data(nand_chip, host); 154a61ae81aSBrian Norris nand_set_flash_node(nand_chip, ofdev->dev.of_node); 1551b578193SWolfgang Grandegger mtd->name = "socrates_nand"; 1561b578193SWolfgang Grandegger mtd->dev.parent = &ofdev->dev; 1571b578193SWolfgang Grandegger 1581b578193SWolfgang Grandegger /*should never be accessed directly */ 1591b578193SWolfgang Grandegger nand_chip->IO_ADDR_R = (void *)0xdeadbeef; 1601b578193SWolfgang Grandegger nand_chip->IO_ADDR_W = (void *)0xdeadbeef; 1611b578193SWolfgang Grandegger 1621b578193SWolfgang Grandegger nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; 1631b578193SWolfgang Grandegger nand_chip->read_byte = socrates_nand_read_byte; 1641b578193SWolfgang Grandegger nand_chip->write_buf = socrates_nand_write_buf; 1651b578193SWolfgang Grandegger nand_chip->read_buf = socrates_nand_read_buf; 1661b578193SWolfgang Grandegger nand_chip->dev_ready = socrates_nand_device_ready; 1671b578193SWolfgang Grandegger 1681b578193SWolfgang Grandegger nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ 169ce111afdSRafał Miłecki nand_chip->ecc.algo = NAND_ECC_HAMMING; 1701b578193SWolfgang Grandegger 1711b578193SWolfgang Grandegger /* TODO: I have no idea what real delay is. */ 1721b578193SWolfgang Grandegger nand_chip->chip_delay = 20; /* 20us command delay time */ 1731b578193SWolfgang Grandegger 1741b578193SWolfgang Grandegger dev_set_drvdata(&ofdev->dev, host); 1751b578193SWolfgang Grandegger 176*00ad378fSBoris Brezillon res = nand_scan(nand_chip, 1); 17783f48f80SMasahiro Yamada if (res) 1781b578193SWolfgang Grandegger goto out; 1791b578193SWolfgang Grandegger 180a61ae81aSBrian Norris res = mtd_device_register(mtd, NULL, 0); 1811b578193SWolfgang Grandegger if (!res) 1821b578193SWolfgang Grandegger return res; 1831b578193SWolfgang Grandegger 1841b578193SWolfgang Grandegger nand_release(mtd); 1851b578193SWolfgang Grandegger 1861b578193SWolfgang Grandegger out: 1871b578193SWolfgang Grandegger iounmap(host->io_base); 1881b578193SWolfgang Grandegger return res; 1891b578193SWolfgang Grandegger } 1901b578193SWolfgang Grandegger 1911b578193SWolfgang Grandegger /* 1921b578193SWolfgang Grandegger * Remove a NAND device. 1931b578193SWolfgang Grandegger */ 194810b7e06SBill Pemberton static int socrates_nand_remove(struct platform_device *ofdev) 1951b578193SWolfgang Grandegger { 1961b578193SWolfgang Grandegger struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); 197a723bf6aSBoris BREZILLON struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); 1981b578193SWolfgang Grandegger 1991b578193SWolfgang Grandegger nand_release(mtd); 2001b578193SWolfgang Grandegger 2011b578193SWolfgang Grandegger iounmap(host->io_base); 2021b578193SWolfgang Grandegger 2031b578193SWolfgang Grandegger return 0; 2041b578193SWolfgang Grandegger } 2051b578193SWolfgang Grandegger 206b2d4fbabSMárton Németh static const struct of_device_id socrates_nand_match[] = 2071b578193SWolfgang Grandegger { 2081b578193SWolfgang Grandegger { 2091b578193SWolfgang Grandegger .compatible = "abb,socrates-nand", 2101b578193SWolfgang Grandegger }, 2111b578193SWolfgang Grandegger {}, 2121b578193SWolfgang Grandegger }; 2131b578193SWolfgang Grandegger 2141b578193SWolfgang Grandegger MODULE_DEVICE_TABLE(of, socrates_nand_match); 2151b578193SWolfgang Grandegger 2161c48a5c9SGrant Likely static struct platform_driver socrates_nand_driver = { 2174018294bSGrant Likely .driver = { 2181b578193SWolfgang Grandegger .name = "socrates_nand", 2194018294bSGrant Likely .of_match_table = socrates_nand_match, 2204018294bSGrant Likely }, 2211b578193SWolfgang Grandegger .probe = socrates_nand_probe, 2225153b88cSBill Pemberton .remove = socrates_nand_remove, 2231b578193SWolfgang Grandegger }; 2241b578193SWolfgang Grandegger 225f99640deSAxel Lin module_platform_driver(socrates_nand_driver); 2261b578193SWolfgang Grandegger 2271b578193SWolfgang Grandegger MODULE_LICENSE("GPL"); 2281b578193SWolfgang Grandegger MODULE_AUTHOR("Ilya Yanok"); 2291b578193SWolfgang Grandegger MODULE_DESCRIPTION("NAND driver for Socrates board"); 230