1/* 2 * drivers/mtd/nand/socrates_nand.c 3 * 4 * Copyright �� 2008 Ilya Yanok, Emcraft Systems 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 */ 12 13#include <linux/slab.h> 14#include <linux/module.h> 15#include <linux/mtd/mtd.h> 16#include <linux/mtd/nand.h> 17#include <linux/mtd/partitions.h> 18#include <linux/of_address.h> 19#include <linux/of_platform.h> 20#include <linux/io.h> 21 22#define FPGA_NAND_CMD_MASK (0x7 << 28) 23#define FPGA_NAND_CMD_COMMAND (0x0 << 28) 24#define FPGA_NAND_CMD_ADDR (0x1 << 28) 25#define FPGA_NAND_CMD_READ (0x2 << 28) 26#define FPGA_NAND_CMD_WRITE (0x3 << 28) 27#define FPGA_NAND_BUSY (0x1 << 15) 28#define FPGA_NAND_ENABLE (0x1 << 31) 29#define FPGA_NAND_DATA_SHIFT 16 30 31struct socrates_nand_host { 32 struct nand_chip nand_chip; 33 struct mtd_info mtd; 34 void __iomem *io_base; 35 struct device *dev; 36}; 37 38/** 39 * socrates_nand_write_buf - write buffer to chip 40 * @mtd: MTD device structure 41 * @buf: data buffer 42 * @len: number of bytes to write 43 */ 44static void socrates_nand_write_buf(struct mtd_info *mtd, 45 const uint8_t *buf, int len) 46{ 47 int i; 48 struct nand_chip *this = mtd->priv; 49 struct socrates_nand_host *host = this->priv; 50 51 for (i = 0; i < len; i++) { 52 out_be32(host->io_base, FPGA_NAND_ENABLE | 53 FPGA_NAND_CMD_WRITE | 54 (buf[i] << FPGA_NAND_DATA_SHIFT)); 55 } 56} 57 58/** 59 * socrates_nand_read_buf - read chip data into buffer 60 * @mtd: MTD device structure 61 * @buf: buffer to store date 62 * @len: number of bytes to read 63 */ 64static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) 65{ 66 int i; 67 struct nand_chip *this = mtd->priv; 68 struct socrates_nand_host *host = this->priv; 69 uint32_t val; 70 71 val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; 72 73 out_be32(host->io_base, val); 74 for (i = 0; i < len; i++) { 75 buf[i] = (in_be32(host->io_base) >> 76 FPGA_NAND_DATA_SHIFT) & 0xff; 77 } 78} 79 80/** 81 * socrates_nand_read_byte - read one byte from the chip 82 * @mtd: MTD device structure 83 */ 84static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) 85{ 86 uint8_t byte; 87 socrates_nand_read_buf(mtd, &byte, sizeof(byte)); 88 return byte; 89} 90 91/** 92 * socrates_nand_read_word - read one word from the chip 93 * @mtd: MTD device structure 94 */ 95static uint16_t socrates_nand_read_word(struct mtd_info *mtd) 96{ 97 uint16_t word; 98 socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word)); 99 return word; 100} 101 102/* 103 * Hardware specific access to control-lines 104 */ 105static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, 106 unsigned int ctrl) 107{ 108 struct nand_chip *nand_chip = mtd->priv; 109 struct socrates_nand_host *host = nand_chip->priv; 110 uint32_t val; 111 112 if (cmd == NAND_CMD_NONE) 113 return; 114 115 if (ctrl & NAND_CLE) 116 val = FPGA_NAND_CMD_COMMAND; 117 else 118 val = FPGA_NAND_CMD_ADDR; 119 120 if (ctrl & NAND_NCE) 121 val |= FPGA_NAND_ENABLE; 122 123 val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; 124 125 out_be32(host->io_base, val); 126} 127 128/* 129 * Read the Device Ready pin. 130 */ 131static int socrates_nand_device_ready(struct mtd_info *mtd) 132{ 133 struct nand_chip *nand_chip = mtd->priv; 134 struct socrates_nand_host *host = nand_chip->priv; 135 136 if (in_be32(host->io_base) & FPGA_NAND_BUSY) 137 return 0; /* busy */ 138 return 1; 139} 140 141/* 142 * Probe for the NAND device. 143 */ 144static int socrates_nand_probe(struct platform_device *ofdev) 145{ 146 struct socrates_nand_host *host; 147 struct mtd_info *mtd; 148 struct nand_chip *nand_chip; 149 int res; 150 struct mtd_part_parser_data ppdata; 151 152 /* Allocate memory for the device structure (and zero it) */ 153 host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); 154 if (!host) 155 return -ENOMEM; 156 157 host->io_base = of_iomap(ofdev->dev.of_node, 0); 158 if (host->io_base == NULL) { 159 dev_err(&ofdev->dev, "ioremap failed\n"); 160 return -EIO; 161 } 162 163 mtd = &host->mtd; 164 nand_chip = &host->nand_chip; 165 host->dev = &ofdev->dev; 166 167 nand_chip->priv = host; /* link the private data structures */ 168 mtd->priv = nand_chip; 169 mtd->name = "socrates_nand"; 170 mtd->dev.parent = &ofdev->dev; 171 ppdata.of_node = ofdev->dev.of_node; 172 173 /*should never be accessed directly */ 174 nand_chip->IO_ADDR_R = (void *)0xdeadbeef; 175 nand_chip->IO_ADDR_W = (void *)0xdeadbeef; 176 177 nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; 178 nand_chip->read_byte = socrates_nand_read_byte; 179 nand_chip->read_word = socrates_nand_read_word; 180 nand_chip->write_buf = socrates_nand_write_buf; 181 nand_chip->read_buf = socrates_nand_read_buf; 182 nand_chip->dev_ready = socrates_nand_device_ready; 183 184 nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ 185 186 /* TODO: I have no idea what real delay is. */ 187 nand_chip->chip_delay = 20; /* 20us command delay time */ 188 189 dev_set_drvdata(&ofdev->dev, host); 190 191 /* first scan to find the device and get the page size */ 192 if (nand_scan_ident(mtd, 1, NULL)) { 193 res = -ENXIO; 194 goto out; 195 } 196 197 /* second phase scan */ 198 if (nand_scan_tail(mtd)) { 199 res = -ENXIO; 200 goto out; 201 } 202 203 res = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); 204 if (!res) 205 return res; 206 207 nand_release(mtd); 208 209out: 210 iounmap(host->io_base); 211 return res; 212} 213 214/* 215 * Remove a NAND device. 216 */ 217static int socrates_nand_remove(struct platform_device *ofdev) 218{ 219 struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); 220 struct mtd_info *mtd = &host->mtd; 221 222 nand_release(mtd); 223 224 iounmap(host->io_base); 225 226 return 0; 227} 228 229static const struct of_device_id socrates_nand_match[] = 230{ 231 { 232 .compatible = "abb,socrates-nand", 233 }, 234 {}, 235}; 236 237MODULE_DEVICE_TABLE(of, socrates_nand_match); 238 239static struct platform_driver socrates_nand_driver = { 240 .driver = { 241 .name = "socrates_nand", 242 .of_match_table = socrates_nand_match, 243 }, 244 .probe = socrates_nand_probe, 245 .remove = socrates_nand_remove, 246}; 247 248module_platform_driver(socrates_nand_driver); 249 250MODULE_LICENSE("GPL"); 251MODULE_AUTHOR("Ilya Yanok"); 252MODULE_DESCRIPTION("NAND driver for Socrates board"); 253