18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2008 Ilya Yanok, Emcraft Systems 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 98c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 108c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define FPGA_NAND_CMD_MASK (0x7 << 28) 168c2ecf20Sopenharmony_ci#define FPGA_NAND_CMD_COMMAND (0x0 << 28) 178c2ecf20Sopenharmony_ci#define FPGA_NAND_CMD_ADDR (0x1 << 28) 188c2ecf20Sopenharmony_ci#define FPGA_NAND_CMD_READ (0x2 << 28) 198c2ecf20Sopenharmony_ci#define FPGA_NAND_CMD_WRITE (0x3 << 28) 208c2ecf20Sopenharmony_ci#define FPGA_NAND_BUSY (0x1 << 15) 218c2ecf20Sopenharmony_ci#define FPGA_NAND_ENABLE (0x1 << 31) 228c2ecf20Sopenharmony_ci#define FPGA_NAND_DATA_SHIFT 16 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct socrates_nand_host { 258c2ecf20Sopenharmony_ci struct nand_controller controller; 268c2ecf20Sopenharmony_ci struct nand_chip nand_chip; 278c2ecf20Sopenharmony_ci void __iomem *io_base; 288c2ecf20Sopenharmony_ci struct device *dev; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * socrates_nand_write_buf - write buffer to chip 338c2ecf20Sopenharmony_ci * @this: NAND chip object 348c2ecf20Sopenharmony_ci * @buf: data buffer 358c2ecf20Sopenharmony_ci * @len: number of bytes to write 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic void socrates_nand_write_buf(struct nand_chip *this, const uint8_t *buf, 388c2ecf20Sopenharmony_ci int len) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int i; 418c2ecf20Sopenharmony_ci struct socrates_nand_host *host = nand_get_controller_data(this); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 448c2ecf20Sopenharmony_ci out_be32(host->io_base, FPGA_NAND_ENABLE | 458c2ecf20Sopenharmony_ci FPGA_NAND_CMD_WRITE | 468c2ecf20Sopenharmony_ci (buf[i] << FPGA_NAND_DATA_SHIFT)); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/** 518c2ecf20Sopenharmony_ci * socrates_nand_read_buf - read chip data into buffer 528c2ecf20Sopenharmony_ci * @this: NAND chip object 538c2ecf20Sopenharmony_ci * @buf: buffer to store date 548c2ecf20Sopenharmony_ci * @len: number of bytes to read 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistatic void socrates_nand_read_buf(struct nand_chip *this, uint8_t *buf, 578c2ecf20Sopenharmony_ci int len) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int i; 608c2ecf20Sopenharmony_ci struct socrates_nand_host *host = nand_get_controller_data(this); 618c2ecf20Sopenharmony_ci uint32_t val; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci out_be32(host->io_base, val); 668c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 678c2ecf20Sopenharmony_ci buf[i] = (in_be32(host->io_base) >> 688c2ecf20Sopenharmony_ci FPGA_NAND_DATA_SHIFT) & 0xff; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * socrates_nand_read_byte - read one byte from the chip 748c2ecf20Sopenharmony_ci * @mtd: MTD device structure 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistatic uint8_t socrates_nand_read_byte(struct nand_chip *this) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci uint8_t byte; 798c2ecf20Sopenharmony_ci socrates_nand_read_buf(this, &byte, sizeof(byte)); 808c2ecf20Sopenharmony_ci return byte; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Hardware specific access to control-lines 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_cistatic void socrates_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd, 878c2ecf20Sopenharmony_ci unsigned int ctrl) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 908c2ecf20Sopenharmony_ci uint32_t val; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (cmd == NAND_CMD_NONE) 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (ctrl & NAND_CLE) 968c2ecf20Sopenharmony_ci val = FPGA_NAND_CMD_COMMAND; 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci val = FPGA_NAND_CMD_ADDR; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (ctrl & NAND_NCE) 1018c2ecf20Sopenharmony_ci val |= FPGA_NAND_ENABLE; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci out_be32(host->io_base, val); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * Read the Device Ready pin. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistatic int socrates_nand_device_ready(struct nand_chip *nand_chip) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct socrates_nand_host *host = nand_get_controller_data(nand_chip); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (in_be32(host->io_base) & FPGA_NAND_BUSY) 1168c2ecf20Sopenharmony_ci return 0; /* busy */ 1178c2ecf20Sopenharmony_ci return 1; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int socrates_attach_chip(struct nand_chip *chip) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && 1238c2ecf20Sopenharmony_ci chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 1248c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct nand_controller_ops socrates_ops = { 1308c2ecf20Sopenharmony_ci .attach_chip = socrates_attach_chip, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * Probe for the NAND device. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic int socrates_nand_probe(struct platform_device *ofdev) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct socrates_nand_host *host; 1398c2ecf20Sopenharmony_ci struct mtd_info *mtd; 1408c2ecf20Sopenharmony_ci struct nand_chip *nand_chip; 1418c2ecf20Sopenharmony_ci int res; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Allocate memory for the device structure (and zero it) */ 1448c2ecf20Sopenharmony_ci host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); 1458c2ecf20Sopenharmony_ci if (!host) 1468c2ecf20Sopenharmony_ci return -ENOMEM; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci host->io_base = of_iomap(ofdev->dev.of_node, 0); 1498c2ecf20Sopenharmony_ci if (host->io_base == NULL) { 1508c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "ioremap failed\n"); 1518c2ecf20Sopenharmony_ci return -EIO; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci nand_chip = &host->nand_chip; 1558c2ecf20Sopenharmony_ci mtd = nand_to_mtd(nand_chip); 1568c2ecf20Sopenharmony_ci host->dev = &ofdev->dev; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci nand_controller_init(&host->controller); 1598c2ecf20Sopenharmony_ci host->controller.ops = &socrates_ops; 1608c2ecf20Sopenharmony_ci nand_chip->controller = &host->controller; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* link the private data structures */ 1638c2ecf20Sopenharmony_ci nand_set_controller_data(nand_chip, host); 1648c2ecf20Sopenharmony_ci nand_set_flash_node(nand_chip, ofdev->dev.of_node); 1658c2ecf20Sopenharmony_ci mtd->name = "socrates_nand"; 1668c2ecf20Sopenharmony_ci mtd->dev.parent = &ofdev->dev; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci nand_chip->legacy.cmd_ctrl = socrates_nand_cmd_ctrl; 1698c2ecf20Sopenharmony_ci nand_chip->legacy.read_byte = socrates_nand_read_byte; 1708c2ecf20Sopenharmony_ci nand_chip->legacy.write_buf = socrates_nand_write_buf; 1718c2ecf20Sopenharmony_ci nand_chip->legacy.read_buf = socrates_nand_read_buf; 1728c2ecf20Sopenharmony_ci nand_chip->legacy.dev_ready = socrates_nand_device_ready; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* TODO: I have no idea what real delay is. */ 1758c2ecf20Sopenharmony_ci nand_chip->legacy.chip_delay = 20; /* 20us command delay time */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * This driver assumes that the default ECC engine should be TYPE_SOFT. 1798c2ecf20Sopenharmony_ci * Set ->engine_type before registering the NAND devices in order to 1808c2ecf20Sopenharmony_ci * provide a driver specific default value. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci dev_set_drvdata(&ofdev->dev, host); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci res = nand_scan(nand_chip, 1); 1878c2ecf20Sopenharmony_ci if (res) 1888c2ecf20Sopenharmony_ci goto out; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci res = mtd_device_register(mtd, NULL, 0); 1918c2ecf20Sopenharmony_ci if (!res) 1928c2ecf20Sopenharmony_ci return res; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci nand_cleanup(nand_chip); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ciout: 1978c2ecf20Sopenharmony_ci iounmap(host->io_base); 1988c2ecf20Sopenharmony_ci return res; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* 2028c2ecf20Sopenharmony_ci * Remove a NAND device. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic int socrates_nand_remove(struct platform_device *ofdev) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); 2078c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->nand_chip; 2088c2ecf20Sopenharmony_ci int ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 2118c2ecf20Sopenharmony_ci WARN_ON(ret); 2128c2ecf20Sopenharmony_ci nand_cleanup(chip); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci iounmap(host->io_base); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic const struct of_device_id socrates_nand_match[] = 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci { 2228c2ecf20Sopenharmony_ci .compatible = "abb,socrates-nand", 2238c2ecf20Sopenharmony_ci }, 2248c2ecf20Sopenharmony_ci {}, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, socrates_nand_match); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct platform_driver socrates_nand_driver = { 2308c2ecf20Sopenharmony_ci .driver = { 2318c2ecf20Sopenharmony_ci .name = "socrates_nand", 2328c2ecf20Sopenharmony_ci .of_match_table = socrates_nand_match, 2338c2ecf20Sopenharmony_ci }, 2348c2ecf20Sopenharmony_ci .probe = socrates_nand_probe, 2358c2ecf20Sopenharmony_ci .remove = socrates_nand_remove, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cimodule_platform_driver(socrates_nand_driver); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ilya Yanok"); 2428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND driver for Socrates board"); 243