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