uboot/drivers/mtd/cfi_mtd.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2008 Semihalf
   3 *
   4 * Written by: Piotr Ziecik <kosmo@semihalf.com>
   5 *
   6 * SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <flash.h>
  11#include <malloc.h>
  12
  13#include <linux/errno.h>
  14#include <linux/mtd/mtd.h>
  15#include <linux/mtd/concat.h>
  16#include <mtd/cfi_flash.h>
  17
  18static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS];
  19static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16];
  20#ifdef CONFIG_MTD_CONCAT
  21static char c_mtd_name[16];
  22#endif
  23
  24static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
  25{
  26        flash_info_t *fi = mtd->priv;
  27        size_t a_start = fi->start[0] + instr->addr;
  28        size_t a_end = a_start + instr->len;
  29        int s_first = -1;
  30        int s_last = -1;
  31        int error, sect;
  32
  33        for (sect = 0; sect < fi->sector_count; sect++) {
  34                if (a_start == fi->start[sect])
  35                        s_first = sect;
  36
  37                if (sect < fi->sector_count - 1) {
  38                        if (a_end == fi->start[sect + 1]) {
  39                                s_last = sect;
  40                                break;
  41                        }
  42                } else {
  43                        s_last = sect;
  44                        break;
  45                }
  46        }
  47
  48        if (s_first >= 0 && s_first <= s_last) {
  49                instr->state = MTD_ERASING;
  50
  51                flash_set_verbose(0);
  52                error = flash_erase(fi, s_first, s_last);
  53                flash_set_verbose(1);
  54
  55                if (error) {
  56                        instr->state = MTD_ERASE_FAILED;
  57                        return -EIO;
  58                }
  59
  60                instr->state = MTD_ERASE_DONE;
  61                mtd_erase_callback(instr);
  62                return 0;
  63        }
  64
  65        return -EINVAL;
  66}
  67
  68static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
  69        size_t *retlen, u_char *buf)
  70{
  71        flash_info_t *fi = mtd->priv;
  72        u_char *f = (u_char*)(fi->start[0]) + from;
  73
  74        memcpy(buf, f, len);
  75        *retlen = len;
  76
  77        return 0;
  78}
  79
  80static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
  81        size_t *retlen, const u_char *buf)
  82{
  83        flash_info_t *fi = mtd->priv;
  84        u_long t = fi->start[0] + to;
  85        int error;
  86
  87        flash_set_verbose(0);
  88        error = write_buff(fi, (u_char*)buf, t, len);
  89        flash_set_verbose(1);
  90
  91        if (!error) {
  92                *retlen = len;
  93                return 0;
  94        }
  95
  96        return -EIO;
  97}
  98
  99static void cfi_mtd_sync(struct mtd_info *mtd)
 100{
 101        /*
 102         * This function should wait until all pending operations
 103         * finish. However this driver is fully synchronous, so
 104         * this function returns immediately
 105         */
 106}
 107
 108static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 109{
 110        flash_info_t *fi = mtd->priv;
 111
 112        flash_set_verbose(0);
 113        flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs,
 114                                        fi->start[0] + ofs + len - 1, fi);
 115        flash_set_verbose(1);
 116
 117        return 0;
 118}
 119
 120static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 121{
 122        flash_info_t *fi = mtd->priv;
 123
 124        flash_set_verbose(0);
 125        flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs,
 126                                        fi->start[0] + ofs + len - 1, fi);
 127        flash_set_verbose(1);
 128
 129        return 0;
 130}
 131
 132static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi)
 133{
 134        int sect_size = 0;
 135        int sect_size_old = 0;
 136        int sect;
 137        int regions = 0;
 138        int numblocks = 0;
 139        ulong offset;
 140        ulong base_addr;
 141
 142        /*
 143         * First detect the number of eraseregions so that we can allocate
 144         * the array of eraseregions correctly
 145         */
 146        for (sect = 0; sect < fi->sector_count; sect++) {
 147                if (sect_size_old != flash_sector_size(fi, sect))
 148                        regions++;
 149                sect_size_old = flash_sector_size(fi, sect);
 150        }
 151
 152        switch (regions) {
 153        case 0:
 154                return 1;
 155        case 1: /* flash has uniform erase size */
 156                mtd->numeraseregions = 0;
 157                mtd->erasesize = sect_size_old;
 158                return 0;
 159        }
 160
 161        mtd->numeraseregions = regions;
 162        mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions);
 163
 164        /*
 165         * Now detect the largest sector and fill the eraseregions
 166         */
 167        regions = 0;
 168        base_addr = offset = fi->start[0];
 169        sect_size_old = flash_sector_size(fi, 0);
 170        for (sect = 0; sect < fi->sector_count; sect++) {
 171                if (sect_size_old != flash_sector_size(fi, sect)) {
 172                        mtd->eraseregions[regions].offset = offset - base_addr;
 173                        mtd->eraseregions[regions].erasesize = sect_size_old;
 174                        mtd->eraseregions[regions].numblocks = numblocks;
 175                        /* Now start counting the next eraseregions */
 176                        numblocks = 0;
 177                        regions++;
 178                        offset = fi->start[sect];
 179                }
 180                numblocks++;
 181
 182                /*
 183                 * Select the largest sector size as erasesize (e.g. for UBI)
 184                 */
 185                if (flash_sector_size(fi, sect) > sect_size)
 186                        sect_size = flash_sector_size(fi, sect);
 187
 188                sect_size_old = flash_sector_size(fi, sect);
 189        }
 190
 191        /*
 192         * Set the last region
 193         */
 194        mtd->eraseregions[regions].offset = offset - base_addr;
 195        mtd->eraseregions[regions].erasesize = sect_size_old;
 196        mtd->eraseregions[regions].numblocks = numblocks;
 197
 198        mtd->erasesize = sect_size;
 199
 200        return 0;
 201}
 202
 203int cfi_mtd_init(void)
 204{
 205        struct mtd_info *mtd;
 206        flash_info_t *fi;
 207        int error, i;
 208#ifdef CONFIG_MTD_CONCAT
 209        int devices_found = 0;
 210        struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS];
 211#endif
 212
 213        for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
 214                fi = &flash_info[i];
 215                mtd = &cfi_mtd_info[i];
 216
 217                memset(mtd, 0, sizeof(struct mtd_info));
 218
 219                error = cfi_mtd_set_erasesize(mtd, fi);
 220                if (error)
 221                        continue;
 222
 223                sprintf(cfi_mtd_names[i], "nor%d", i);
 224                mtd->name               = cfi_mtd_names[i];
 225                mtd->type               = MTD_NORFLASH;
 226                mtd->flags              = MTD_CAP_NORFLASH;
 227                mtd->size               = fi->size;
 228                mtd->writesize          = 1;
 229                mtd->writebufsize       = mtd->writesize;
 230
 231                mtd->_erase             = cfi_mtd_erase;
 232                mtd->_read              = cfi_mtd_read;
 233                mtd->_write             = cfi_mtd_write;
 234                mtd->_sync              = cfi_mtd_sync;
 235                mtd->_lock              = cfi_mtd_lock;
 236                mtd->_unlock            = cfi_mtd_unlock;
 237                mtd->priv               = fi;
 238
 239                if (add_mtd_device(mtd))
 240                        return -ENOMEM;
 241
 242#ifdef CONFIG_MTD_CONCAT
 243                mtd_list[devices_found++] = mtd;
 244#endif
 245        }
 246
 247#ifdef CONFIG_MTD_CONCAT
 248        if (devices_found > 1) {
 249                /*
 250                 * We detected multiple devices. Concatenate them together.
 251                 */
 252                sprintf(c_mtd_name, "nor%d", devices_found);
 253                mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name);
 254
 255                if (mtd == NULL)
 256                        return -ENXIO;
 257
 258                if (add_mtd_device(mtd))
 259                        return -ENOMEM;
 260        }
 261#endif /* CONFIG_MTD_CONCAT */
 262
 263        return 0;
 264}
 265