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