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