linux/drivers/staging/goldfish/goldfish_nand.c
<<
>>
Prefs
   1/*
   2 * drivers/mtd/devices/goldfish_nand.c
   3 *
   4 * Copyright (C) 2007 Google, Inc.
   5 * Copyright (C) 2012 Intel, Inc.
   6 * Copyright (C) 2013 Intel, Inc.
   7 *
   8 * This software is licensed under the terms of the GNU General Public
   9 * License version 2, as published by the Free Software Foundation, and
  10 * may be copied, distributed, and modified under those terms.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 */
  18
  19#include <linux/io.h>
  20#include <linux/device.h>
  21#include <linux/module.h>
  22#include <linux/slab.h>
  23#include <linux/ioport.h>
  24#include <linux/vmalloc.h>
  25#include <linux/init.h>
  26#include <linux/mtd/mtd.h>
  27#include <linux/platform_device.h>
  28
  29#include <asm/div64.h>
  30
  31#include "goldfish_nand_reg.h"
  32
  33struct goldfish_nand {
  34        spinlock_t              lock;
  35        unsigned char __iomem  *base;
  36        struct cmd_params       *cmd_params;
  37        size_t                  mtd_count;
  38        struct mtd_info         mtd[0];
  39};
  40
  41static u32 goldfish_nand_cmd_with_params(struct mtd_info *mtd,
  42                        enum nand_cmd cmd, u64 addr, u32 len,
  43                        void *ptr, u32 *rv)
  44{
  45        u32 cmdp;
  46        struct goldfish_nand *nand = mtd->priv;
  47        struct cmd_params *cps = nand->cmd_params;
  48        unsigned char __iomem  *base = nand->base;
  49
  50        if (cps == NULL)
  51                return -1;
  52
  53        switch (cmd) {
  54        case NAND_CMD_ERASE:
  55                cmdp = NAND_CMD_ERASE_WITH_PARAMS;
  56                break;
  57        case NAND_CMD_READ:
  58                cmdp = NAND_CMD_READ_WITH_PARAMS;
  59                break;
  60        case NAND_CMD_WRITE:
  61                cmdp = NAND_CMD_WRITE_WITH_PARAMS;
  62                break;
  63        default:
  64                return -1;
  65        }
  66        cps->dev = mtd - nand->mtd;
  67        cps->addr_high = (u32)(addr >> 32);
  68        cps->addr_low = (u32)addr;
  69        cps->transfer_size = len;
  70        cps->data = (u32)ptr;
  71        writel(cmdp, base + NAND_COMMAND);
  72        *rv = cps->result;
  73        return 0;
  74}
  75
  76static u32 goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
  77                                u64 addr, u32 len, void *ptr)
  78{
  79        struct goldfish_nand *nand = mtd->priv;
  80        u32 rv;
  81        unsigned long irq_flags;
  82        unsigned char __iomem  *base = nand->base;
  83
  84        spin_lock_irqsave(&nand->lock, irq_flags);
  85        if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv)) {
  86                writel(mtd - nand->mtd, base + NAND_DEV);
  87                writel((u32)(addr >> 32), base + NAND_ADDR_HIGH);
  88                writel((u32)addr, base + NAND_ADDR_LOW);
  89                writel(len, base + NAND_TRANSFER_SIZE);
  90                writel((u32)ptr, base + NAND_DATA);
  91                writel(cmd, base + NAND_COMMAND);
  92                rv = readl(base + NAND_RESULT);
  93        }
  94        spin_unlock_irqrestore(&nand->lock, irq_flags);
  95        return rv;
  96}
  97
  98static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
  99{
 100        loff_t ofs = instr->addr;
 101        u32 len = instr->len;
 102        u32 rem;
 103
 104        if (ofs + len > mtd->size)
 105                goto invalid_arg;
 106        rem = do_div(ofs, mtd->writesize);
 107        if (rem)
 108                goto invalid_arg;
 109        ofs *= (mtd->writesize + mtd->oobsize);
 110
 111        if (len % mtd->writesize)
 112                goto invalid_arg;
 113        len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
 114
 115        if (goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
 116                pr_err("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n",
 117                        ofs, len, mtd->size, mtd->erasesize);
 118                return -EIO;
 119        }
 120
 121        instr->state = MTD_ERASE_DONE;
 122        mtd_erase_callback(instr);
 123
 124        return 0;
 125
 126invalid_arg:
 127        pr_err("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n",
 128                ofs, len, mtd->size, mtd->erasesize);
 129        return -EINVAL;
 130}
 131
 132static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
 133                                struct mtd_oob_ops *ops)
 134{
 135        u32 rem;
 136
 137        if (ofs + ops->len > mtd->size)
 138                goto invalid_arg;
 139        if (ops->datbuf && ops->len && ops->len != mtd->writesize)
 140                goto invalid_arg;
 141        if (ops->ooblen + ops->ooboffs > mtd->oobsize)
 142                goto invalid_arg;
 143
 144        rem = do_div(ofs, mtd->writesize);
 145        if (rem)
 146                goto invalid_arg;
 147        ofs *= (mtd->writesize + mtd->oobsize);
 148
 149        if (ops->datbuf)
 150                ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
 151                                                ops->len, ops->datbuf);
 152        ofs += mtd->writesize + ops->ooboffs;
 153        if (ops->oobbuf)
 154                ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
 155                                                ops->ooblen, ops->oobbuf);
 156        return 0;
 157
 158invalid_arg:
 159        pr_err("goldfish_nand_read_oob: invalid read, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
 160                ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
 161        return -EINVAL;
 162}
 163
 164static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
 165                                struct mtd_oob_ops *ops)
 166{
 167        u32 rem;
 168
 169        if (ofs + ops->len > mtd->size)
 170                goto invalid_arg;
 171        if (ops->len && ops->len != mtd->writesize)
 172                goto invalid_arg;
 173        if (ops->ooblen + ops->ooboffs > mtd->oobsize)
 174                goto invalid_arg;
 175
 176        rem = do_div(ofs, mtd->writesize);
 177        if (rem)
 178                goto invalid_arg;
 179        ofs *= (mtd->writesize + mtd->oobsize);
 180
 181        if (ops->datbuf)
 182                ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
 183                                                ops->len, ops->datbuf);
 184        ofs += mtd->writesize + ops->ooboffs;
 185        if (ops->oobbuf)
 186                ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
 187                                                ops->ooblen, ops->oobbuf);
 188        return 0;
 189
 190invalid_arg:
 191        pr_err("goldfish_nand_write_oob: invalid write, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
 192                ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
 193        return -EINVAL;
 194}
 195
 196static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
 197                                size_t *retlen, u_char *buf)
 198{
 199        u32 rem;
 200
 201        if (from + len > mtd->size)
 202                goto invalid_arg;
 203        if (len != mtd->writesize)
 204                goto invalid_arg;
 205
 206        rem = do_div(from, mtd->writesize);
 207        if (rem)
 208                goto invalid_arg;
 209        from *= (mtd->writesize + mtd->oobsize);
 210
 211        *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
 212        return 0;
 213
 214invalid_arg:
 215        pr_err("goldfish_nand_read: invalid read, start %llx, len %zx, dev_size %llx, write_size %x\n",
 216                from, len, mtd->size, mtd->writesize);
 217        return -EINVAL;
 218}
 219
 220static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
 221                                size_t *retlen, const u_char *buf)
 222{
 223        u32 rem;
 224
 225        if (to + len > mtd->size)
 226                goto invalid_arg;
 227        if (len != mtd->writesize)
 228                goto invalid_arg;
 229
 230        rem = do_div(to, mtd->writesize);
 231        if (rem)
 232                goto invalid_arg;
 233        to *= (mtd->writesize + mtd->oobsize);
 234
 235        *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
 236        return 0;
 237
 238invalid_arg:
 239        pr_err("goldfish_nand_write: invalid write, start %llx, len %zx, dev_size %llx, write_size %x\n",
 240                to, len, mtd->size, mtd->writesize);
 241        return -EINVAL;
 242}
 243
 244static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
 245{
 246        u32 rem;
 247
 248        if (ofs >= mtd->size)
 249                goto invalid_arg;
 250
 251        rem = do_div(ofs, mtd->erasesize);
 252        if (rem)
 253                goto invalid_arg;
 254        ofs *= mtd->erasesize / mtd->writesize;
 255        ofs *= (mtd->writesize + mtd->oobsize);
 256
 257        return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
 258
 259invalid_arg:
 260        pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
 261                ofs, mtd->size, mtd->writesize);
 262        return -EINVAL;
 263}
 264
 265static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 266{
 267        u32 rem;
 268
 269        if (ofs >= mtd->size)
 270                goto invalid_arg;
 271
 272        rem = do_div(ofs, mtd->erasesize);
 273        if (rem)
 274                goto invalid_arg;
 275        ofs *= mtd->erasesize / mtd->writesize;
 276        ofs *= (mtd->writesize + mtd->oobsize);
 277
 278        if (goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
 279                return -EIO;
 280        return 0;
 281
 282invalid_arg:
 283        pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
 284                ofs, mtd->size, mtd->writesize);
 285        return -EINVAL;
 286}
 287
 288static int nand_setup_cmd_params(struct platform_device *pdev,
 289                                                struct goldfish_nand *nand)
 290{
 291        u64 paddr;
 292        unsigned char __iomem  *base = nand->base;
 293
 294        nand->cmd_params = devm_kzalloc(&pdev->dev,
 295                                        sizeof(struct cmd_params), GFP_KERNEL);
 296        if (!nand->cmd_params)
 297                return -1;
 298
 299        paddr = __pa(nand->cmd_params);
 300        writel((u32)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH);
 301        writel((u32)paddr, base + NAND_CMD_PARAMS_ADDR_LOW);
 302        return 0;
 303}
 304
 305static int goldfish_nand_init_device(struct platform_device *pdev,
 306                                        struct goldfish_nand *nand, int id)
 307{
 308        u32 name_len;
 309        u32 result;
 310        u32 flags;
 311        unsigned long irq_flags;
 312        unsigned char __iomem  *base = nand->base;
 313        struct mtd_info *mtd = &nand->mtd[id];
 314        char *name;
 315
 316        spin_lock_irqsave(&nand->lock, irq_flags);
 317        writel(id, base + NAND_DEV);
 318        flags = readl(base + NAND_DEV_FLAGS);
 319        name_len = readl(base + NAND_DEV_NAME_LEN);
 320        mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
 321        mtd->size = readl(base + NAND_DEV_SIZE_LOW);
 322        mtd->size |= (u64)readl(base + NAND_DEV_SIZE_HIGH) << 32;
 323        mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
 324        mtd->oobavail = mtd->oobsize;
 325        mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
 326                        (mtd->writesize + mtd->oobsize) * mtd->writesize;
 327        do_div(mtd->size, mtd->writesize + mtd->oobsize);
 328        mtd->size *= mtd->writesize;
 329        dev_dbg(&pdev->dev,
 330                "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
 331                       id, mtd->size, mtd->writesize,
 332                       mtd->oobsize, mtd->erasesize);
 333        spin_unlock_irqrestore(&nand->lock, irq_flags);
 334
 335        mtd->priv = nand;
 336
 337        mtd->name = name = devm_kzalloc(&pdev->dev, name_len + 1, GFP_KERNEL);
 338        if (name == NULL)
 339                return -ENOMEM;
 340
 341        result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len,
 342                                                                        name);
 343        if (result != name_len) {
 344                dev_err(&pdev->dev,
 345                        "goldfish_nand_init_device failed to get dev name %d != %d\n",
 346                               result, name_len);
 347                return -ENODEV;
 348        }
 349        ((char *) mtd->name)[name_len] = '\0';
 350
 351        /* Setup the MTD structure */
 352        mtd->type = MTD_NANDFLASH;
 353        mtd->flags = MTD_CAP_NANDFLASH;
 354        if (flags & NAND_DEV_FLAG_READ_ONLY)
 355                mtd->flags &= ~MTD_WRITEABLE;
 356        if (flags & NAND_DEV_FLAG_CMD_PARAMS_CAP)
 357                nand_setup_cmd_params(pdev, nand);
 358
 359        mtd->owner = THIS_MODULE;
 360        mtd->_erase = goldfish_nand_erase;
 361        mtd->_read = goldfish_nand_read;
 362        mtd->_write = goldfish_nand_write;
 363        mtd->_read_oob = goldfish_nand_read_oob;
 364        mtd->_write_oob = goldfish_nand_write_oob;
 365        mtd->_block_isbad = goldfish_nand_block_isbad;
 366        mtd->_block_markbad = goldfish_nand_block_markbad;
 367
 368        if (mtd_device_register(mtd, NULL, 0))
 369                return -EIO;
 370
 371        return 0;
 372}
 373
 374static int goldfish_nand_probe(struct platform_device *pdev)
 375{
 376        u32 num_dev;
 377        int i;
 378        int err;
 379        u32 num_dev_working;
 380        u32 version;
 381        struct resource *r;
 382        struct goldfish_nand *nand;
 383        unsigned char __iomem  *base;
 384
 385        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 386        if (r == NULL)
 387                return -ENODEV;
 388
 389        base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
 390        if (base == NULL)
 391                return -ENOMEM;
 392
 393        version = readl(base + NAND_VERSION);
 394        if (version != NAND_VERSION_CURRENT) {
 395                dev_err(&pdev->dev,
 396                        "goldfish_nand_init: version mismatch, got %d, expected %d\n",
 397                                version, NAND_VERSION_CURRENT);
 398                return -ENODEV;
 399        }
 400        num_dev = readl(base + NAND_NUM_DEV);
 401        if (num_dev == 0)
 402                return -ENODEV;
 403
 404        nand = devm_kzalloc(&pdev->dev, sizeof(*nand) +
 405                                sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
 406        if (nand == NULL)
 407                return -ENOMEM;
 408
 409        spin_lock_init(&nand->lock);
 410        nand->base = base;
 411        nand->mtd_count = num_dev;
 412        platform_set_drvdata(pdev, nand);
 413
 414        num_dev_working = 0;
 415        for (i = 0; i < num_dev; i++) {
 416                err = goldfish_nand_init_device(pdev, nand, i);
 417                if (err == 0)
 418                        num_dev_working++;
 419        }
 420        if (num_dev_working == 0)
 421                return -ENODEV;
 422        return 0;
 423}
 424
 425static int goldfish_nand_remove(struct platform_device *pdev)
 426{
 427        struct goldfish_nand *nand = platform_get_drvdata(pdev);
 428        int i;
 429        for (i = 0; i < nand->mtd_count; i++) {
 430                if (nand->mtd[i].name)
 431                        mtd_device_unregister(&nand->mtd[i]);
 432        }
 433        return 0;
 434}
 435
 436static struct platform_driver goldfish_nand_driver = {
 437        .probe          = goldfish_nand_probe,
 438        .remove         = goldfish_nand_remove,
 439        .driver = {
 440                .name = "goldfish_nand"
 441        }
 442};
 443
 444module_platform_driver(goldfish_nand_driver);
 445MODULE_LICENSE("GPL");
 446