uboot/drivers/dfu/dfu_sf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
   4 */
   5
   6#include <common.h>
   7#include <malloc.h>
   8#include <errno.h>
   9#include <div64.h>
  10#include <dfu.h>
  11#include <spi.h>
  12#include <spi_flash.h>
  13#include <jffs2/load_kernel.h>
  14#include <linux/mtd/mtd.h>
  15#include <linux/ctype.h>
  16
  17static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size)
  18{
  19        *size = dfu->data.sf.size;
  20
  21        return 0;
  22}
  23
  24static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf,
  25                              long *len)
  26{
  27        long seglen = *len;
  28        int ret;
  29
  30        if (seglen > (16 << 20))
  31                seglen = (16 << 20);
  32
  33        ret = spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset,
  34                seglen, buf);
  35        if (!ret)
  36                *len = seglen;
  37
  38        return ret;
  39}
  40
  41static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset)
  42{
  43        return (lldiv((start + offset), dfu->data.sf.dev->sector_size)) *
  44                dfu->data.sf.dev->sector_size;
  45}
  46
  47static int dfu_write_medium_sf(struct dfu_entity *dfu,
  48                               u64 offset, void *buf, long *len)
  49{
  50        int ret;
  51
  52        ret = spi_flash_erase(dfu->data.sf.dev,
  53                              find_sector(dfu, dfu->data.sf.start, offset),
  54                              dfu->data.sf.dev->sector_size);
  55        if (ret)
  56                return ret;
  57
  58        ret = spi_flash_write(dfu->data.sf.dev, dfu->data.sf.start + offset,
  59                              *len, buf);
  60        if (ret)
  61                return ret;
  62
  63        return 0;
  64}
  65
  66static int dfu_flush_medium_sf(struct dfu_entity *dfu)
  67{
  68        u64 off, length;
  69
  70        if (!CONFIG_IS_ENABLED(DFU_SF_PART) || !dfu->data.sf.ubi)
  71                return 0;
  72
  73        /* in case of ubi partition, erase rest of the partition */
  74        off = find_sector(dfu, dfu->data.sf.start, dfu->offset);
  75        /* last write ended with unaligned length jump to next */
  76        if (off != dfu->data.sf.start + dfu->offset)
  77                off += dfu->data.sf.dev->sector_size;
  78        length = dfu->data.sf.start + dfu->data.sf.size - off;
  79        if (length)
  80                return spi_flash_erase(dfu->data.sf.dev, off, length);
  81
  82        return 0;
  83}
  84
  85static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu)
  86{
  87        /*
  88         * Currently, Poll Timeout != 0 is only needed on nand
  89         * ubi partition, as sectors which are not used need
  90         * to be erased
  91         */
  92        if (CONFIG_IS_ENABLED(DFU_SF_PART) && dfu->data.sf.ubi)
  93                return DFU_MANIFEST_POLL_TIMEOUT;
  94
  95        return DFU_DEFAULT_POLL_TIMEOUT;
  96}
  97
  98static void dfu_free_entity_sf(struct dfu_entity *dfu)
  99{
 100        /*
 101         * In the DM case it is not necessary to free the SPI device.
 102         * For the non-DM case we must ensure that the the SPI device is only
 103         * freed once.
 104         */
 105        if (!CONFIG_IS_ENABLED(DM_SPI_FLASH)) {
 106                struct spi_flash *dev = dfu->data.sf.dev;
 107
 108                if (!dev)
 109                        return;
 110                dfu->data.sf.dev = NULL;
 111                list_for_each_entry(dfu, &dfu_list, list) {
 112                        if (dfu->data.sf.dev == dev)
 113                                dfu->data.sf.dev = NULL;
 114                }
 115                spi_flash_free(dev);
 116        }
 117}
 118
 119static struct spi_flash *parse_dev(char *devstr)
 120{
 121        unsigned int bus;
 122        unsigned int cs;
 123        unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
 124        unsigned int mode = CONFIG_SF_DEFAULT_MODE;
 125        char *s, *endp;
 126        struct spi_flash *dev;
 127
 128        s = strsep(&devstr, ":");
 129        if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) {
 130                printf("Invalid SPI bus %s\n", s);
 131                return NULL;
 132        }
 133
 134        s = strsep(&devstr, ":");
 135        if (!s || !*s || (cs = simple_strtoul(s, &endp, 0), *endp)) {
 136                printf("Invalid SPI chip-select %s\n", s);
 137                return NULL;
 138        }
 139
 140        s = strsep(&devstr, ":");
 141        if (s && *s) {
 142                speed = simple_strtoul(s, &endp, 0);
 143                if (*endp || !speed) {
 144                        printf("Invalid SPI speed %s\n", s);
 145                        return NULL;
 146                }
 147        }
 148
 149        s = strsep(&devstr, ":");
 150        if (s && *s) {
 151                mode = simple_strtoul(s, &endp, 0);
 152                if (*endp || mode > 3) {
 153                        printf("Invalid SPI mode %s\n", s);
 154                        return NULL;
 155                }
 156        }
 157
 158        dev = spi_flash_probe(bus, cs, speed, mode);
 159        if (!dev) {
 160                printf("Failed to create SPI flash at %u:%u:%u:%u\n",
 161                       bus, cs, speed, mode);
 162                return NULL;
 163        }
 164
 165        return dev;
 166}
 167
 168int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char **argv, int argc)
 169{
 170        char *s;
 171        char *devstr_bkup = strdup(devstr);
 172
 173        dfu->data.sf.dev = parse_dev(devstr_bkup);
 174        free(devstr_bkup);
 175        if (!dfu->data.sf.dev)
 176                return -ENODEV;
 177
 178        dfu->dev_type = DFU_DEV_SF;
 179        dfu->max_buf_size = dfu->data.sf.dev->sector_size;
 180
 181        if (argc != 3)
 182                return -EINVAL;
 183        if (!strcmp(argv[0], "raw")) {
 184                dfu->layout = DFU_RAW_ADDR;
 185                dfu->data.sf.start = hextoul(argv[1], &s);
 186                if (*s)
 187                        return -EINVAL;
 188                dfu->data.sf.size = hextoul(argv[2], &s);
 189                if (*s)
 190                        return -EINVAL;
 191        } else if (CONFIG_IS_ENABLED(DFU_SF_PART) &&
 192                   (!strcmp(argv[0], "part") || !strcmp(argv[0], "partubi"))) {
 193                char mtd_id[32];
 194                struct mtd_device *mtd_dev;
 195                u8 part_num;
 196                struct part_info *pi;
 197                int ret, dev, part;
 198
 199                dfu->layout = DFU_RAW_ADDR;
 200
 201                dev = dectoul(argv[1], &s);
 202                if (*s)
 203                        return -EINVAL;
 204                part = dectoul(argv[2], &s);
 205                if (*s)
 206                        return -EINVAL;
 207
 208                sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1);
 209                printf("using id '%s'\n", mtd_id);
 210
 211                mtdparts_init();
 212
 213                ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
 214                if (ret != 0) {
 215                        printf("Could not locate '%s'\n", mtd_id);
 216                        return -1;
 217                }
 218                dfu->data.sf.start = pi->offset;
 219                dfu->data.sf.size = pi->size;
 220                if (!strcmp(argv[0], "partubi"))
 221                        dfu->data.sf.ubi = 1;
 222        } else {
 223                printf("%s: Memory layout (%s) not supported!\n", __func__, argv[0]);
 224                spi_flash_free(dfu->data.sf.dev);
 225                return -1;
 226        }
 227
 228        dfu->get_medium_size = dfu_get_medium_size_sf;
 229        dfu->read_medium = dfu_read_medium_sf;
 230        dfu->write_medium = dfu_write_medium_sf;
 231        dfu->flush_medium = dfu_flush_medium_sf;
 232        dfu->poll_timeout = dfu_polltimeout_sf;
 233        dfu->free_entity = dfu_free_entity_sf;
 234
 235        /* initial state */
 236        dfu->inited = 0;
 237
 238        return 0;
 239}
 240