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