uboot/drivers/dfu/dfu_nand.c
<<
>>
Prefs
   1/*
   2 * dfu_nand.c -- DFU for NAND routines.
   3 *
   4 * Copyright (C) 2012-2013 Texas Instruments, Inc.
   5 *
   6 * Based on dfu_mmc.c which is:
   7 * Copyright (C) 2012 Samsung Electronics
   8 * author: Lukasz Majewski <l.majewski@samsung.com>
   9 *
  10 * SPDX-License-Identifier:     GPL-2.0+
  11 */
  12
  13#include <common.h>
  14#include <malloc.h>
  15#include <errno.h>
  16#include <div64.h>
  17#include <dfu.h>
  18#include <linux/mtd/mtd.h>
  19#include <jffs2/load_kernel.h>
  20#include <nand.h>
  21
  22static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu,
  23                        u64 offset, void *buf, long *len)
  24{
  25        loff_t start, lim;
  26        size_t count, actual;
  27        int ret;
  28        struct mtd_info *mtd;
  29
  30        /* if buf == NULL return total size of the area */
  31        if (buf == NULL) {
  32                *len = dfu->data.nand.size;
  33                return 0;
  34        }
  35
  36        start = dfu->data.nand.start + offset + dfu->bad_skip;
  37        lim = dfu->data.nand.start + dfu->data.nand.size - start;
  38        count = *len;
  39
  40        if (nand_curr_device < 0 ||
  41            nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
  42            !nand_info[nand_curr_device]) {
  43                printf("%s: invalid nand device\n", __func__);
  44                return -1;
  45        }
  46
  47        mtd = nand_info[nand_curr_device];
  48
  49        if (op == DFU_OP_READ) {
  50                ret = nand_read_skip_bad(mtd, start, &count, &actual,
  51                                         lim, buf);
  52        } else {
  53                nand_erase_options_t opts;
  54
  55                memset(&opts, 0, sizeof(opts));
  56                opts.offset = start;
  57                opts.length = count;
  58                opts.spread = 1;
  59                opts.quiet = 1;
  60                opts.lim = lim;
  61                /* first erase */
  62                ret = nand_erase_opts(mtd, &opts);
  63                if (ret)
  64                        return ret;
  65                /* then write */
  66                ret = nand_write_skip_bad(mtd, start, &count, &actual,
  67                                          lim, buf, WITH_WR_VERIFY);
  68        }
  69
  70        if (ret != 0) {
  71                printf("%s: nand_%s_skip_bad call failed at %llx!\n",
  72                       __func__, op == DFU_OP_READ ? "read" : "write",
  73                       start);
  74                return ret;
  75        }
  76
  77        /*
  78         * Find out where we stopped writing data.  This can be deeper into
  79         * the NAND than we expected due to having to skip bad blocks.  So
  80         * we must take this into account for the next write, if any.
  81         */
  82        if (actual > count)
  83                dfu->bad_skip += actual - count;
  84
  85        return ret;
  86}
  87
  88static inline int nand_block_write(struct dfu_entity *dfu,
  89                u64 offset, void *buf, long *len)
  90{
  91        return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
  92}
  93
  94static inline int nand_block_read(struct dfu_entity *dfu,
  95                u64 offset, void *buf, long *len)
  96{
  97        return nand_block_op(DFU_OP_READ, dfu, offset, buf, len);
  98}
  99
 100static int dfu_write_medium_nand(struct dfu_entity *dfu,
 101                u64 offset, void *buf, long *len)
 102{
 103        int ret = -1;
 104
 105        switch (dfu->layout) {
 106        case DFU_RAW_ADDR:
 107                ret = nand_block_write(dfu, offset, buf, len);
 108                break;
 109        default:
 110                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 111                       dfu_get_layout(dfu->layout));
 112        }
 113
 114        return ret;
 115}
 116
 117long dfu_get_medium_size_nand(struct dfu_entity *dfu)
 118{
 119        return dfu->data.nand.size;
 120}
 121
 122static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf,
 123                long *len)
 124{
 125        int ret = -1;
 126
 127        switch (dfu->layout) {
 128        case DFU_RAW_ADDR:
 129                ret = nand_block_read(dfu, offset, buf, len);
 130                break;
 131        default:
 132                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 133                       dfu_get_layout(dfu->layout));
 134        }
 135
 136        return ret;
 137}
 138
 139static int dfu_flush_medium_nand(struct dfu_entity *dfu)
 140{
 141        int ret = 0;
 142        u64 off;
 143
 144        /* in case of ubi partition, erase rest of the partition */
 145        if (dfu->data.nand.ubi) {
 146                struct mtd_info *mtd;
 147                nand_erase_options_t opts;
 148
 149                if (nand_curr_device < 0 ||
 150                    nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
 151                    !nand_info[nand_curr_device]) {
 152                        printf("%s: invalid nand device\n", __func__);
 153                        return -1;
 154                }
 155
 156                mtd = nand_info[nand_curr_device];
 157
 158                memset(&opts, 0, sizeof(opts));
 159                off = dfu->offset;
 160                if ((off & (mtd->erasesize - 1)) != 0) {
 161                        /*
 162                         * last write ended with unaligned length
 163                         * sector is erased, jump to next
 164                         */
 165                        off = off & ~((mtd->erasesize - 1));
 166                        off += mtd->erasesize;
 167                }
 168                opts.offset = dfu->data.nand.start + off +
 169                                dfu->bad_skip;
 170                opts.length = dfu->data.nand.start +
 171                                dfu->data.nand.size - opts.offset;
 172                ret = nand_erase_opts(mtd, &opts);
 173                if (ret != 0)
 174                        printf("Failure erase: %d\n", ret);
 175        }
 176
 177        return ret;
 178}
 179
 180unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
 181{
 182        /*
 183         * Currently, Poll Timeout != 0 is only needed on nand
 184         * ubi partition, as the not used sectors need an erase
 185         */
 186        if (dfu->data.nand.ubi)
 187                return DFU_MANIFEST_POLL_TIMEOUT;
 188
 189        return DFU_DEFAULT_POLL_TIMEOUT;
 190}
 191
 192int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s)
 193{
 194        char *st;
 195        int ret, dev, part;
 196
 197        dfu->data.nand.ubi = 0;
 198        dfu->dev_type = DFU_DEV_NAND;
 199        st = strsep(&s, " ");
 200        if (!strcmp(st, "raw")) {
 201                dfu->layout = DFU_RAW_ADDR;
 202                dfu->data.nand.start = simple_strtoul(s, &s, 16);
 203                s++;
 204                dfu->data.nand.size = simple_strtoul(s, &s, 16);
 205        } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
 206                char mtd_id[32];
 207                struct mtd_device *mtd_dev;
 208                u8 part_num;
 209                struct part_info *pi;
 210
 211                dfu->layout = DFU_RAW_ADDR;
 212
 213                dev = simple_strtoul(s, &s, 10);
 214                s++;
 215                part = simple_strtoul(s, &s, 10);
 216
 217                sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1);
 218                printf("using id '%s'\n", mtd_id);
 219
 220                mtdparts_init();
 221
 222                ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
 223                if (ret != 0) {
 224                        printf("Could not locate '%s'\n", mtd_id);
 225                        return -1;
 226                }
 227
 228                dfu->data.nand.start = pi->offset;
 229                dfu->data.nand.size = pi->size;
 230                if (!strcmp(st, "partubi"))
 231                        dfu->data.nand.ubi = 1;
 232        } else {
 233                printf("%s: Memory layout (%s) not supported!\n", __func__, st);
 234                return -1;
 235        }
 236
 237        dfu->get_medium_size = dfu_get_medium_size_nand;
 238        dfu->read_medium = dfu_read_medium_nand;
 239        dfu->write_medium = dfu_write_medium_nand;
 240        dfu->flush_medium = dfu_flush_medium_nand;
 241        dfu->poll_timeout = dfu_polltimeout_nand;
 242
 243        /* initial state */
 244        dfu->inited = 0;
 245
 246        return 0;
 247}
 248