ndfc.c revision 6dfc6d250d0b7ebaa6423c44dcd09fcfe68deabd
1/* 2 * drivers/mtd/ndfc.c 3 * 4 * Overview: 5 * Platform independend driver for NDFC (NanD Flash Controller) 6 * integrated into EP440 cores 7 * 8 * Author: Thomas Gleixner 9 * 10 * Copyright 2006 IBM 11 * 12 * This program is free software; you can redistribute it and/or modify it 13 * under the terms of the GNU General Public License as published by the 14 * Free Software Foundation; either version 2 of the License, or (at your 15 * option) any later version. 16 * 17 */ 18#include <linux/module.h> 19#include <linux/mtd/nand.h> 20#include <linux/mtd/nand_ecc.h> 21#include <linux/mtd/partitions.h> 22#include <linux/mtd/ndfc.h> 23#include <linux/mtd/ubi.h> 24#include <linux/mtd/mtd.h> 25#include <linux/platform_device.h> 26 27#include <asm/io.h> 28#include <asm/ibm44x.h> 29 30struct ndfc_nand_mtd { 31 struct mtd_info mtd; 32 struct nand_chip chip; 33 struct platform_nand_chip *pl_chip; 34}; 35 36static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS]; 37 38struct ndfc_controller { 39 void __iomem *ndfcbase; 40 struct nand_hw_control ndfc_control; 41 atomic_t childs_active; 42}; 43 44static struct ndfc_controller ndfc_ctrl; 45 46static void ndfc_select_chip(struct mtd_info *mtd, int chip) 47{ 48 uint32_t ccr; 49 struct ndfc_controller *ndfc = &ndfc_ctrl; 50 struct nand_chip *nandchip = mtd->priv; 51 struct ndfc_nand_mtd *nandmtd = nandchip->priv; 52 struct platform_nand_chip *pchip = nandmtd->pl_chip; 53 54 ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); 55 if (chip >= 0) { 56 ccr &= ~NDFC_CCR_BS_MASK; 57 ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); 58 } else 59 ccr |= NDFC_CCR_RESET_CE; 60 writel(ccr, ndfc->ndfcbase + NDFC_CCR); 61} 62 63static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd) 64{ 65 struct ndfc_controller *ndfc = &ndfc_ctrl; 66 struct nand_chip *chip = mtd->priv; 67 68 switch (cmd) { 69 case NAND_CTL_SETCLE: 70 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_CMD; 71 break; 72 case NAND_CTL_SETALE: 73 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_ALE; 74 break; 75 default: 76 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; 77 break; 78 } 79} 80 81static int ndfc_ready(struct mtd_info *mtd) 82{ 83 struct ndfc_controller *ndfc = &ndfc_ctrl; 84 85 return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; 86} 87 88static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) 89{ 90 uint32_t ccr; 91 struct ndfc_controller *ndfc = &ndfc_ctrl; 92 93 ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); 94 ccr |= NDFC_CCR_RESET_ECC; 95 __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); 96 wmb(); 97} 98 99static int ndfc_calculate_ecc(struct mtd_info *mtd, 100 const u_char *dat, u_char *ecc_code) 101{ 102 struct ndfc_controller *ndfc = &ndfc_ctrl; 103 uint32_t ecc; 104 uint8_t *p = (uint8_t *)&ecc; 105 106 wmb(); 107 ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC); 108 ecc_code[0] = p[1]; 109 ecc_code[1] = p[2]; 110 ecc_code[2] = p[3]; 111 112 return 0; 113} 114 115/* 116 * Speedups for buffer read/write/verify 117 * 118 * NDFC allows 32bit read/write of data. So we can speed up the buffer 119 * functions. No further checking, as nand_base will always read/write 120 * page aligned. 121 */ 122static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) 123{ 124 struct ndfc_controller *ndfc = &ndfc_ctrl; 125 uint32_t *p = (uint32_t *) buf; 126 127 for(;len > 0; len -= 4) 128 *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA); 129} 130 131static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 132{ 133 struct ndfc_controller *ndfc = &ndfc_ctrl; 134 uint32_t *p = (uint32_t *) buf; 135 136 for(;len > 0; len -= 4) 137 __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA); 138} 139 140static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) 141{ 142 struct ndfc_controller *ndfc = &ndfc_ctrl; 143 uint32_t *p = (uint32_t *) buf; 144 145 for(;len > 0; len -= 4) 146 if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA)) 147 return -EFAULT; 148 return 0; 149} 150 151/* 152 * Initialize chip structure 153 */ 154static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) 155{ 156 struct ndfc_controller *ndfc = &ndfc_ctrl; 157 struct nand_chip *chip = &mtd->chip; 158 159 chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; 160 chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; 161 chip->hwcontrol = ndfc_hwcontrol; 162 chip->dev_ready = ndfc_ready; 163 chip->select_chip = ndfc_select_chip; 164 chip->chip_delay = 50; 165 chip->priv = mtd; 166 chip->options = mtd->pl_chip->options; 167 chip->controller = &ndfc->ndfc_control; 168 chip->read_buf = ndfc_read_buf; 169 chip->write_buf = ndfc_write_buf; 170 chip->verify_buf = ndfc_verify_buf; 171 chip->ecc.correct = nand_correct_data; 172 chip->ecc.hwctl = ndfc_enable_hwecc; 173 chip->ecc.calculate = ndfc_calculate_ecc; 174 chip->ecc.mode = NAND_ECC_HW; 175 chip->ecc.size = 256; 176 chip->ecc.bytes = 3; 177 chip->autooob = mtd->pl_chip->autooob; 178 mtd->mtd.priv = chip; 179 mtd->mtd.owner = THIS_MODULE; 180} 181 182static int ndfc_chip_probe(struct platform_device *pdev) 183{ 184 int rc; 185 struct platform_nand_chip *nc = pdev->dev.platform_data; 186 struct ndfc_chip_settings *settings = nc->priv; 187 struct ndfc_controller *ndfc = &ndfc_ctrl; 188 struct ndfc_nand_mtd *nandmtd; 189 190 if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS) 191 return -EINVAL; 192 193 /* Set the bank settings */ 194 __raw_writel(settings->bank_settings, 195 ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2)); 196 197 nandmtd = &ndfc_mtd[pdev->id]; 198 if (nandmtd->pl_chip) 199 return -EBUSY; 200 201 nandmtd->pl_chip = nc; 202 ndfc_chip_init(nandmtd); 203 204 /* Scan for chips */ 205 if (nand_scan(&nandmtd->mtd, nc->nr_chips)) { 206 nandmtd->pl_chip = NULL; 207 return -ENODEV; 208 } 209 210#ifdef CONFIG_MTD_PARTITIONS 211 printk("Number of partitions %d\n", nc->nr_partitions); 212 if (nc->nr_partitions) { 213 struct mtd_info *mtd_ubi; 214 nc->partitions[NAND_PARTS_CONTENT_IDX].mtdp = &mtd_ubi; 215 216 add_mtd_device(&nandmtd->mtd); /* for testing */ 217 add_mtd_partitions(&nandmtd->mtd, 218 nc->partitions, 219 nc->nr_partitions); 220 221 add_mtd_device(mtd_ubi); 222 223 } else 224#else 225 add_mtd_device(&nandmtd->mtd); 226#endif 227 228 atomic_inc(&ndfc->childs_active); 229 return 0; 230} 231 232static int ndfc_chip_remove(struct platform_device *pdev) 233{ 234 return 0; 235} 236 237static int ndfc_nand_probe(struct platform_device *pdev) 238{ 239 struct platform_nand_ctrl *nc = pdev->dev.platform_data; 240 struct ndfc_controller_settings *settings = nc->priv; 241 struct resource *res = pdev->resource; 242 struct ndfc_controller *ndfc = &ndfc_ctrl; 243 unsigned long long phys = NDFC_PHYSADDR_OFFS | res->start; 244 245 ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); 246 if (!ndfc->ndfcbase) { 247 printk(KERN_ERR "NDFC: ioremap failed\n"); 248 return -EIO; 249 } 250 251 __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR); 252 253 spin_lock_init(&ndfc->ndfc_control.lock); 254 init_waitqueue_head(&ndfc->ndfc_control.wq); 255 256 platform_set_drvdata(pdev, ndfc); 257 258 printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n", 259 __raw_readl(ndfc->ndfcbase + NDFC_REVID)); 260 261 return 0; 262} 263 264static int ndfc_nand_remove(struct platform_device *pdev) 265{ 266 struct ndfc_controller *ndfc = platform_get_drvdata(pdev); 267 268 if (atomic_read(&ndfc->childs_active)) 269 return -EBUSY; 270 271 if (ndfc) { 272 platform_set_drvdata(pdev, NULL); 273 iounmap(ndfc_ctrl.ndfcbase); 274 ndfc_ctrl.ndfcbase = NULL; 275 } 276 return 0; 277} 278 279/* driver device registration */ 280 281static struct platform_driver ndfc_chip_driver = { 282 .probe = ndfc_chip_probe, 283 .remove = ndfc_chip_remove, 284 .driver = { 285 .name = "ndfc-chip", 286 .owner = THIS_MODULE, 287 }, 288}; 289 290static struct platform_driver ndfc_nand_driver = { 291 .probe = ndfc_nand_probe, 292 .remove = ndfc_nand_remove, 293 .driver = { 294 .name = "ndfc-nand", 295 .owner = THIS_MODULE, 296 }, 297}; 298 299static int __init ndfc_nand_init(void) 300{ 301 int ret = platform_driver_register(&ndfc_nand_driver); 302 303 if (!ret) 304 ret = platform_driver_register(&ndfc_chip_driver); 305 return ret; 306} 307 308static void __exit ndfc_nand_exit(void) 309{ 310 platform_driver_unregister(&ndfc_chip_driver); 311 platform_driver_unregister(&ndfc_nand_driver); 312} 313 314module_init(ndfc_nand_init); 315module_exit(ndfc_nand_exit); 316 317MODULE_LICENSE("GPL"); 318MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); 319MODULE_DESCRIPTION("Platform driver for NDFC"); 320