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