ndfc.c revision 76c23c32e3b3ad48e07e07897075ab19ae1ef117
1/* 2 * drivers/mtd/ndfc.c 3 * 4 * Overview: 5 * Platform independent driver for NDFC (NanD Flash Controller) 6 * integrated into EP440 cores 7 * 8 * Ported to an OF platform driver by Sean MacLennan 9 * 10 * The NDFC supports multiple chips, but this driver only supports a 11 * single chip since I do not have access to any boards with 12 * multiple chips. 13 * 14 * Author: Thomas Gleixner 15 * 16 * Copyright 2006 IBM 17 * Copyright 2008 PIKA Technologies 18 * Sean MacLennan <smaclennan@pikatech.com> 19 * 20 * This program is free software; you can redistribute it and/or modify it 21 * under the terms of the GNU General Public License as published by the 22 * Free Software Foundation; either version 2 of the License, or (at your 23 * option) any later version. 24 * 25 */ 26#include <linux/module.h> 27#include <linux/mtd/nand.h> 28#include <linux/mtd/nand_ecc.h> 29#include <linux/mtd/partitions.h> 30#include <linux/mtd/ndfc.h> 31#include <linux/mtd/mtd.h> 32#include <linux/of_platform.h> 33#include <asm/io.h> 34 35 36struct ndfc_controller { 37 struct of_device *ofdev; 38 void __iomem *ndfcbase; 39 struct mtd_info mtd; 40 struct nand_chip chip; 41 int chip_select; 42 struct nand_hw_control ndfc_control; 43#ifdef CONFIG_MTD_PARTITIONS 44 struct mtd_partition *parts; 45#endif 46}; 47 48static struct ndfc_controller ndfc_ctrl; 49 50static void ndfc_select_chip(struct mtd_info *mtd, int chip) 51{ 52 uint32_t ccr; 53 struct ndfc_controller *ndfc = &ndfc_ctrl; 54 55 ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); 56 if (chip >= 0) { 57 ccr &= ~NDFC_CCR_BS_MASK; 58 ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); 59 } else 60 ccr |= NDFC_CCR_RESET_CE; 61 out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); 62} 63 64static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) 65{ 66 struct ndfc_controller *ndfc = &ndfc_ctrl; 67 68 if (cmd == NAND_CMD_NONE) 69 return; 70 71 if (ctrl & NAND_CLE) 72 writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD); 73 else 74 writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE); 75} 76 77static int ndfc_ready(struct mtd_info *mtd) 78{ 79 struct ndfc_controller *ndfc = &ndfc_ctrl; 80 81 return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; 82} 83 84static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) 85{ 86 uint32_t ccr; 87 struct ndfc_controller *ndfc = &ndfc_ctrl; 88 89 ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); 90 ccr |= NDFC_CCR_RESET_ECC; 91 out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); 92 wmb(); 93} 94 95static int ndfc_calculate_ecc(struct mtd_info *mtd, 96 const u_char *dat, u_char *ecc_code) 97{ 98 struct ndfc_controller *ndfc = &ndfc_ctrl; 99 uint32_t ecc; 100 uint8_t *p = (uint8_t *)&ecc; 101 102 wmb(); 103 ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); 104 /* The NDFC uses Smart Media (SMC) bytes order */ 105 ecc_code[0] = p[1]; 106 ecc_code[1] = p[2]; 107 ecc_code[2] = p[3]; 108 109 return 0; 110} 111 112/* 113 * Speedups for buffer read/write/verify 114 * 115 * NDFC allows 32bit read/write of data. So we can speed up the buffer 116 * functions. No further checking, as nand_base will always read/write 117 * page aligned. 118 */ 119static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) 120{ 121 struct ndfc_controller *ndfc = &ndfc_ctrl; 122 uint32_t *p = (uint32_t *) buf; 123 124 for(;len > 0; len -= 4) 125 *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); 126} 127 128static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 129{ 130 struct ndfc_controller *ndfc = &ndfc_ctrl; 131 uint32_t *p = (uint32_t *) buf; 132 133 for(;len > 0; len -= 4) 134 out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); 135} 136 137static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 138{ 139 struct ndfc_controller *ndfc = &ndfc_ctrl; 140 uint32_t *p = (uint32_t *) buf; 141 142 for(;len > 0; len -= 4) 143 if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA)) 144 return -EFAULT; 145 return 0; 146} 147 148/* 149 * Initialize chip structure 150 */ 151static int ndfc_chip_init(struct ndfc_controller *ndfc, 152 struct device_node *node) 153{ 154#ifdef CONFIG_MTD_PARTITIONS 155#ifdef CONFIG_MTD_CMDLINE_PARTS 156 static const char *part_types[] = { "cmdlinepart", NULL }; 157#else 158 static const char *part_types[] = { NULL }; 159#endif 160#endif 161 struct device_node *flash_np; 162 struct nand_chip *chip = &ndfc->chip; 163 int ret; 164 165 chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; 166 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; 167 chip->cmd_ctrl = ndfc_hwcontrol; 168 chip->dev_ready = ndfc_ready; 169 chip->select_chip = ndfc_select_chip; 170 chip->chip_delay = 50; 171 chip->controller = &ndfc->ndfc_control; 172 chip->read_buf = ndfc_read_buf; 173 chip->write_buf = ndfc_write_buf; 174 chip->verify_buf = ndfc_verify_buf; 175 chip->ecc.correct = nand_correct_data; 176 chip->ecc.hwctl = ndfc_enable_hwecc; 177 chip->ecc.calculate = ndfc_calculate_ecc; 178 chip->ecc.mode = NAND_ECC_HW; 179 chip->ecc.size = 256; 180 chip->ecc.bytes = 3; 181 182 ndfc->mtd.priv = chip; 183 ndfc->mtd.owner = THIS_MODULE; 184 185 flash_np = of_get_next_child(node, NULL); 186 if (!flash_np) 187 return -ENODEV; 188 189 ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", 190 dev_name(&ndfc->ofdev->dev), flash_np->name); 191 if (!ndfc->mtd.name) { 192 ret = -ENOMEM; 193 goto err; 194 } 195 196 ret = nand_scan(&ndfc->mtd, 1); 197 if (ret) 198 goto err; 199 200#ifdef CONFIG_MTD_PARTITIONS 201 ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0); 202 if (ret < 0) 203 goto err; 204 205#ifdef CONFIG_MTD_OF_PARTS 206 if (ret == 0) { 207 ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np, 208 &ndfc->parts); 209 if (ret < 0) 210 goto err; 211 } 212#endif 213 214 if (ret > 0) 215 ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret); 216 else 217#endif 218 ret = add_mtd_device(&ndfc->mtd); 219 220err: 221 of_node_put(flash_np); 222 if (ret) 223 kfree(ndfc->mtd.name); 224 return ret; 225} 226 227static int __devinit ndfc_probe(struct of_device *ofdev, 228 const struct of_device_id *match) 229{ 230 struct ndfc_controller *ndfc = &ndfc_ctrl; 231 const u32 *reg; 232 u32 ccr; 233 int err, len; 234 235 spin_lock_init(&ndfc->ndfc_control.lock); 236 init_waitqueue_head(&ndfc->ndfc_control.wq); 237 ndfc->ofdev = ofdev; 238 dev_set_drvdata(&ofdev->dev, ndfc); 239 240 /* Read the reg property to get the chip select */ 241 reg = of_get_property(ofdev->node, "reg", &len); 242 if (reg == NULL || len != 12) { 243 dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); 244 return -ENOENT; 245 } 246 ndfc->chip_select = reg[0]; 247 248 ndfc->ndfcbase = of_iomap(ofdev->node, 0); 249 if (!ndfc->ndfcbase) { 250 dev_err(&ofdev->dev, "failed to get memory\n"); 251 return -EIO; 252 } 253 254 ccr = NDFC_CCR_BS(ndfc->chip_select); 255 256 /* It is ok if ccr does not exist - just default to 0 */ 257 reg = of_get_property(ofdev->node, "ccr", NULL); 258 if (reg) 259 ccr |= *reg; 260 261 out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); 262 263 /* Set the bank settings if given */ 264 reg = of_get_property(ofdev->node, "bank-settings", NULL); 265 if (reg) { 266 int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); 267 out_be32(ndfc->ndfcbase + offset, *reg); 268 } 269 270 err = ndfc_chip_init(ndfc, ofdev->node); 271 if (err) { 272 iounmap(ndfc->ndfcbase); 273 return err; 274 } 275 276 return 0; 277} 278 279static int __devexit ndfc_remove(struct of_device *ofdev) 280{ 281 struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); 282 283 nand_release(&ndfc->mtd); 284 285 return 0; 286} 287 288static const struct of_device_id ndfc_match[] = { 289 { .compatible = "ibm,ndfc", }, 290 {} 291}; 292MODULE_DEVICE_TABLE(of, ndfc_match); 293 294static struct of_platform_driver ndfc_driver = { 295 .driver = { 296 .name = "ndfc", 297 }, 298 .match_table = ndfc_match, 299 .probe = ndfc_probe, 300 .remove = __devexit_p(ndfc_remove), 301}; 302 303static int __init ndfc_nand_init(void) 304{ 305 return of_register_platform_driver(&ndfc_driver); 306} 307 308static void __exit ndfc_nand_exit(void) 309{ 310 of_unregister_platform_driver(&ndfc_driver); 311} 312 313module_init(ndfc_nand_init); 314module_exit(ndfc_nand_exit); 315 316MODULE_LICENSE("GPL"); 317MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); 318MODULE_DESCRIPTION("OF Platform driver for NDFC"); 319