linux/drivers/mtd/chips/cfi_util.c
<<
>>
Prefs
   1/*
   2 * Common Flash Interface support:
   3 *   Generic utility functions not dependant on command set
   4 *
   5 * Copyright (C) 2002 Red Hat
   6 * Copyright (C) 2003 STMicroelectronics Limited
   7 *
   8 * This code is covered by the GPL.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include <linux/kernel.h>
  14#include <asm/io.h>
  15#include <asm/byteorder.h>
  16
  17#include <linux/errno.h>
  18#include <linux/slab.h>
  19#include <linux/delay.h>
  20#include <linux/interrupt.h>
  21#include <linux/mtd/xip.h>
  22#include <linux/mtd/mtd.h>
  23#include <linux/mtd/map.h>
  24#include <linux/mtd/cfi.h>
  25#include <linux/mtd/compatmac.h>
  26
  27int __xipram cfi_qry_present(struct map_info *map, __u32 base,
  28                             struct cfi_private *cfi)
  29{
  30        int osf = cfi->interleave * cfi->device_type;   /* scale factor */
  31        map_word val[3];
  32        map_word qry[3];
  33
  34        qry[0] = cfi_build_cmd('Q', map, cfi);
  35        qry[1] = cfi_build_cmd('R', map, cfi);
  36        qry[2] = cfi_build_cmd('Y', map, cfi);
  37
  38        val[0] = map_read(map, base + osf*0x10);
  39        val[1] = map_read(map, base + osf*0x11);
  40        val[2] = map_read(map, base + osf*0x12);
  41
  42        if (!map_word_equal(map, qry[0], val[0]))
  43                return 0;
  44
  45        if (!map_word_equal(map, qry[1], val[1]))
  46                return 0;
  47
  48        if (!map_word_equal(map, qry[2], val[2]))
  49                return 0;
  50
  51        return 1;       /* "QRY" found */
  52}
  53EXPORT_SYMBOL_GPL(cfi_qry_present);
  54
  55int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
  56                             struct cfi_private *cfi)
  57{
  58        cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
  59        cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
  60        if (cfi_qry_present(map, base, cfi))
  61                return 1;
  62        /* QRY not found probably we deal with some odd CFI chips */
  63        /* Some revisions of some old Intel chips? */
  64        cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
  65        cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
  66        cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
  67        if (cfi_qry_present(map, base, cfi))
  68                return 1;
  69        /* ST M29DW chips */
  70        cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
  71        cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
  72        if (cfi_qry_present(map, base, cfi))
  73                return 1;
  74        /* QRY not found */
  75        return 0;
  76}
  77EXPORT_SYMBOL_GPL(cfi_qry_mode_on);
  78
  79void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
  80                               struct cfi_private *cfi)
  81{
  82        cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
  83        cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
  84        /* M29W128G flashes require an additional reset command
  85           when exit qry mode */
  86        if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
  87                cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
  88}
  89EXPORT_SYMBOL_GPL(cfi_qry_mode_off);
  90
  91struct cfi_extquery *
  92__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
  93{
  94        struct cfi_private *cfi = map->fldrv_priv;
  95        __u32 base = 0; // cfi->chips[0].start;
  96        int ofs_factor = cfi->interleave * cfi->device_type;
  97        int i;
  98        struct cfi_extquery *extp = NULL;
  99
 100        printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
 101        if (!adr)
 102                goto out;
 103
 104        extp = kmalloc(size, GFP_KERNEL);
 105        if (!extp) {
 106                printk(KERN_ERR "Failed to allocate memory\n");
 107                goto out;
 108        }
 109
 110#ifdef CONFIG_MTD_XIP
 111        local_irq_disable();
 112#endif
 113
 114        /* Switch it into Query Mode */
 115        cfi_qry_mode_on(base, map, cfi);
 116        /* Read in the Extended Query Table */
 117        for (i=0; i<size; i++) {
 118                ((unsigned char *)extp)[i] =
 119                        cfi_read_query(map, base+((adr+i)*ofs_factor));
 120        }
 121
 122        /* Make sure it returns to read mode */
 123        cfi_qry_mode_off(base, map, cfi);
 124
 125#ifdef CONFIG_MTD_XIP
 126        (void) map_read(map, base);
 127        xip_iprefetch();
 128        local_irq_enable();
 129#endif
 130
 131 out:   return extp;
 132}
 133
 134EXPORT_SYMBOL(cfi_read_pri);
 135
 136void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
 137{
 138        struct map_info *map = mtd->priv;
 139        struct cfi_private *cfi = map->fldrv_priv;
 140        struct cfi_fixup *f;
 141
 142        for (f=fixups; f->fixup; f++) {
 143                if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
 144                    ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
 145                        f->fixup(mtd, f->param);
 146                }
 147        }
 148}
 149
 150EXPORT_SYMBOL(cfi_fixup);
 151
 152int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
 153                                     loff_t ofs, size_t len, void *thunk)
 154{
 155        struct map_info *map = mtd->priv;
 156        struct cfi_private *cfi = map->fldrv_priv;
 157        unsigned long adr;
 158        int chipnum, ret = 0;
 159        int i, first;
 160        struct mtd_erase_region_info *regions = mtd->eraseregions;
 161
 162        if (ofs > mtd->size)
 163                return -EINVAL;
 164
 165        if ((len + ofs) > mtd->size)
 166                return -EINVAL;
 167
 168        /* Check that both start and end of the requested erase are
 169         * aligned with the erasesize at the appropriate addresses.
 170         */
 171
 172        i = 0;
 173
 174        /* Skip all erase regions which are ended before the start of
 175           the requested erase. Actually, to save on the calculations,
 176           we skip to the first erase region which starts after the
 177           start of the requested erase, and then go back one.
 178        */
 179
 180        while (i < mtd->numeraseregions && ofs >= regions[i].offset)
 181               i++;
 182        i--;
 183
 184        /* OK, now i is pointing at the erase region in which this
 185           erase request starts. Check the start of the requested
 186           erase range is aligned with the erase size which is in
 187           effect here.
 188        */
 189
 190        if (ofs & (regions[i].erasesize-1))
 191                return -EINVAL;
 192
 193        /* Remember the erase region we start on */
 194        first = i;
 195
 196        /* Next, check that the end of the requested erase is aligned
 197         * with the erase region at that address.
 198         */
 199
 200        while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
 201                i++;
 202
 203        /* As before, drop back one to point at the region in which
 204           the address actually falls
 205        */
 206        i--;
 207
 208        if ((ofs + len) & (regions[i].erasesize-1))
 209                return -EINVAL;
 210
 211        chipnum = ofs >> cfi->chipshift;
 212        adr = ofs - (chipnum << cfi->chipshift);
 213
 214        i=first;
 215
 216        while(len) {
 217                int size = regions[i].erasesize;
 218
 219                ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
 220
 221                if (ret)
 222                        return ret;
 223
 224                adr += size;
 225                ofs += size;
 226                len -= size;
 227
 228                if (ofs == regions[i].offset + size * regions[i].numblocks)
 229                        i++;
 230
 231                if (adr >> cfi->chipshift) {
 232                        adr = 0;
 233                        chipnum++;
 234
 235                        if (chipnum >= cfi->numchips)
 236                        break;
 237                }
 238        }
 239
 240        return 0;
 241}
 242
 243EXPORT_SYMBOL(cfi_varsize_frob);
 244
 245MODULE_LICENSE("GPL");
 246