uboot/drivers/mtd/spi/spansion.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2009 Freescale Semiconductor, Inc.
   3 *
   4 * Author: Mingkai Hu (Mingkai.hu@freescale.com)
   5 * Based on stmicro.c by Wolfgang Denk (wd@denx.de),
   6 * TsiChung Liew (Tsi-Chung.Liew@freescale.com),
   7 * and  Jason McMullan (mcmullan@netapp.com)
   8 *
   9 * See file CREDITS for list of people who contributed to this
  10 * project.
  11 *
  12 * This program is free software; you can redistribute it and/or
  13 * modify it under the terms of the GNU General Public License as
  14 * published by the Free Software Foundation; either version 2 of
  15 * the License, or (at your option) any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  25 * MA 02111-1307 USA
  26 */
  27
  28#include <common.h>
  29#include <malloc.h>
  30#include <spi_flash.h>
  31
  32#include "spi_flash_internal.h"
  33
  34/* S25FLxx-specific commands */
  35#define CMD_S25FLXX_READ        0x03    /* Read Data Bytes */
  36#define CMD_S25FLXX_FAST_READ   0x0b    /* Read Data Bytes at Higher Speed */
  37#define CMD_S25FLXX_READID      0x90    /* Read Manufacture ID and Device ID */
  38#define CMD_S25FLXX_WREN        0x06    /* Write Enable */
  39#define CMD_S25FLXX_WRDI        0x04    /* Write Disable */
  40#define CMD_S25FLXX_RDSR        0x05    /* Read Status Register */
  41#define CMD_S25FLXX_WRSR        0x01    /* Write Status Register */
  42#define CMD_S25FLXX_PP          0x02    /* Page Program */
  43#define CMD_S25FLXX_SE          0xd8    /* Sector Erase */
  44#define CMD_S25FLXX_BE          0xc7    /* Bulk Erase */
  45#define CMD_S25FLXX_DP          0xb9    /* Deep Power-down */
  46#define CMD_S25FLXX_RES         0xab    /* Release from DP, and Read Signature */
  47
  48#define SPSN_ID_S25FL008A       0x0213
  49#define SPSN_ID_S25FL016A       0x0214
  50#define SPSN_ID_S25FL032A       0x0215
  51#define SPSN_ID_S25FL064A       0x0216
  52#define SPSN_ID_S25FL128P       0x2018
  53#define SPSN_EXT_ID_S25FL128P_256KB     0x0300
  54#define SPSN_EXT_ID_S25FL128P_64KB      0x0301
  55
  56#define SPANSION_SR_WIP         (1 << 0)        /* Write-in-Progress */
  57
  58struct spansion_spi_flash_params {
  59        u16 idcode1;
  60        u16 idcode2;
  61        u16 page_size;
  62        u16 pages_per_sector;
  63        u16 nr_sectors;
  64        const char *name;
  65};
  66
  67struct spansion_spi_flash {
  68        struct spi_flash flash;
  69        const struct spansion_spi_flash_params *params;
  70};
  71
  72static inline struct spansion_spi_flash *to_spansion_spi_flash(struct spi_flash
  73                                                             *flash)
  74{
  75        return container_of(flash, struct spansion_spi_flash, flash);
  76}
  77
  78static const struct spansion_spi_flash_params spansion_spi_flash_table[] = {
  79        {
  80                .idcode1 = SPSN_ID_S25FL008A,
  81                .idcode2 = 0,
  82                .page_size = 256,
  83                .pages_per_sector = 256,
  84                .nr_sectors = 16,
  85                .name = "S25FL008A",
  86        },
  87        {
  88                .idcode1 = SPSN_ID_S25FL016A,
  89                .idcode2 = 0,
  90                .page_size = 256,
  91                .pages_per_sector = 256,
  92                .nr_sectors = 32,
  93                .name = "S25FL016A",
  94        },
  95        {
  96                .idcode1 = SPSN_ID_S25FL032A,
  97                .idcode2 = 0,
  98                .page_size = 256,
  99                .pages_per_sector = 256,
 100                .nr_sectors = 64,
 101                .name = "S25FL032A",
 102        },
 103        {
 104                .idcode1 = SPSN_ID_S25FL064A,
 105                .idcode2 = 0,
 106                .page_size = 256,
 107                .pages_per_sector = 256,
 108                .nr_sectors = 128,
 109                .name = "S25FL064A",
 110        },
 111        {
 112                .idcode1 = SPSN_ID_S25FL128P,
 113                .idcode2 = SPSN_EXT_ID_S25FL128P_64KB,
 114                .page_size = 256,
 115                .pages_per_sector = 256,
 116                .nr_sectors = 256,
 117                .name = "S25FL128P_64K",
 118        },
 119        {
 120                .idcode1 = SPSN_ID_S25FL128P,
 121                .idcode2 = SPSN_EXT_ID_S25FL128P_256KB,
 122                .page_size = 256,
 123                .pages_per_sector = 1024,
 124                .nr_sectors = 64,
 125                .name = "S25FL128P_256K",
 126        },
 127};
 128
 129static int spansion_wait_ready(struct spi_flash *flash, unsigned long timeout)
 130{
 131        struct spi_slave *spi = flash->spi;
 132        unsigned long timebase;
 133        int ret;
 134        u8 status;
 135
 136        timebase = get_timer(0);
 137        do {
 138                ret = spi_flash_cmd(spi, CMD_S25FLXX_RDSR, &status, sizeof(status));
 139                if (ret)
 140                        return -1;
 141
 142                if ((status & SPANSION_SR_WIP) == 0)
 143                        break;
 144
 145        } while (get_timer(timebase) < timeout);
 146
 147
 148        if ((status & SPANSION_SR_WIP) == 0)
 149                return 0;
 150
 151        /* Timed out */
 152        return -1;
 153}
 154
 155static int spansion_read_fast(struct spi_flash *flash,
 156                             u32 offset, size_t len, void *buf)
 157{
 158        struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
 159        unsigned long page_addr;
 160        unsigned long page_size;
 161        u8 cmd[5];
 162
 163        page_size = spsn->params->page_size;
 164        page_addr = offset / page_size;
 165
 166        cmd[0] = CMD_READ_ARRAY_FAST;
 167        cmd[1] = page_addr >> 8;
 168        cmd[2] = page_addr;
 169        cmd[3] = offset % page_size;
 170        cmd[4] = 0x00;
 171
 172        debug
 173                ("READ: 0x%x => cmd = { 0x%02x 0x%02x%02x%02x%02x } len = 0x%x\n",
 174                 offset, cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], len);
 175
 176        return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
 177}
 178
 179static int spansion_write(struct spi_flash *flash,
 180                         u32 offset, size_t len, const void *buf)
 181{
 182        struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
 183        unsigned long page_addr;
 184        unsigned long byte_addr;
 185        unsigned long page_size;
 186        size_t chunk_len;
 187        size_t actual;
 188        int ret;
 189        u8 cmd[4];
 190
 191        page_size = spsn->params->page_size;
 192        page_addr = offset / page_size;
 193        byte_addr = offset % page_size;
 194
 195        ret = spi_claim_bus(flash->spi);
 196        if (ret) {
 197                debug("SF: Unable to claim SPI bus\n");
 198                return ret;
 199        }
 200
 201        ret = 0;
 202        for (actual = 0; actual < len; actual += chunk_len) {
 203                chunk_len = min(len - actual, page_size - byte_addr);
 204
 205                cmd[0] = CMD_S25FLXX_PP;
 206                cmd[1] = page_addr >> 8;
 207                cmd[2] = page_addr;
 208                cmd[3] = byte_addr;
 209
 210                debug
 211                    ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n",
 212                     buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
 213
 214                ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0);
 215                if (ret < 0) {
 216                        debug("SF: Enabling Write failed\n");
 217                        break;
 218                }
 219
 220                ret = spi_flash_cmd_write(flash->spi, cmd, 4,
 221                                          buf + actual, chunk_len);
 222                if (ret < 0) {
 223                        debug("SF: SPANSION Page Program failed\n");
 224                        break;
 225                }
 226
 227                ret = spansion_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
 228                if (ret < 0) {
 229                        debug("SF: SPANSION page programming timed out\n");
 230                        break;
 231                }
 232
 233                page_addr++;
 234                byte_addr = 0;
 235        }
 236
 237        debug("SF: SPANSION: Successfully programmed %u bytes @ 0x%x\n",
 238              len, offset);
 239
 240        spi_release_bus(flash->spi);
 241        return ret;
 242}
 243
 244int spansion_erase(struct spi_flash *flash, u32 offset, size_t len)
 245{
 246        struct spansion_spi_flash *spsn = to_spansion_spi_flash(flash);
 247        unsigned long sector_size;
 248        size_t actual;
 249        int ret;
 250        u8 cmd[4];
 251
 252        /*
 253         * This function currently uses sector erase only.
 254         * probably speed things up by using bulk erase
 255         * when possible.
 256         */
 257
 258        sector_size = spsn->params->page_size * spsn->params->pages_per_sector;
 259
 260        if (offset % sector_size || len % sector_size) {
 261                debug("SF: Erase offset/length not multiple of sector size\n");
 262                return -1;
 263        }
 264
 265        len /= sector_size;
 266        cmd[0] = CMD_S25FLXX_SE;
 267        cmd[2] = 0x00;
 268        cmd[3] = 0x00;
 269
 270        ret = spi_claim_bus(flash->spi);
 271        if (ret) {
 272                debug("SF: Unable to claim SPI bus\n");
 273                return ret;
 274        }
 275
 276        ret = 0;
 277        for (actual = 0; actual < len; actual++) {
 278                cmd[1] = (offset / sector_size) + actual;
 279
 280                ret = spi_flash_cmd(flash->spi, CMD_S25FLXX_WREN, NULL, 0);
 281                if (ret < 0) {
 282                        debug("SF: Enabling Write failed\n");
 283                        break;
 284                }
 285
 286                ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
 287                if (ret < 0) {
 288                        debug("SF: SPANSION page erase failed\n");
 289                        break;
 290                }
 291
 292                /* Up to 2 seconds */
 293                ret = spansion_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
 294                if (ret < 0) {
 295                        debug("SF: SPANSION page erase timed out\n");
 296                        break;
 297                }
 298        }
 299
 300        debug("SF: SPANSION: Successfully erased %u bytes @ 0x%x\n",
 301              len * sector_size, offset);
 302
 303        spi_release_bus(flash->spi);
 304        return ret;
 305}
 306
 307struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
 308{
 309        const struct spansion_spi_flash_params *params;
 310        struct spansion_spi_flash *spsn;
 311        unsigned int i;
 312        unsigned short jedec, ext_jedec;
 313
 314        jedec = idcode[1] << 8 | idcode[2];
 315        ext_jedec = idcode[3] << 8 | idcode[4];
 316
 317        for (i = 0; i < ARRAY_SIZE(spansion_spi_flash_table); i++) {
 318                params = &spansion_spi_flash_table[i];
 319                if (params->idcode1 == jedec) {
 320                        if (params->idcode2 == ext_jedec)
 321                                break;
 322                }
 323        }
 324
 325        if (i == ARRAY_SIZE(spansion_spi_flash_table)) {
 326                debug("SF: Unsupported SPANSION ID %04x %04x\n", jedec, ext_jedec);
 327                return NULL;
 328        }
 329
 330        spsn = malloc(sizeof(struct spansion_spi_flash));
 331        if (!spsn) {
 332                debug("SF: Failed to allocate memory\n");
 333                return NULL;
 334        }
 335
 336        spsn->params = params;
 337        spsn->flash.spi = spi;
 338        spsn->flash.name = params->name;
 339
 340        spsn->flash.write = spansion_write;
 341        spsn->flash.erase = spansion_erase;
 342        spsn->flash.read = spansion_read_fast;
 343        spsn->flash.size = params->page_size * params->pages_per_sector
 344            * params->nr_sectors;
 345
 346        debug("SF: Detected %s with page size %u, total %u bytes\n",
 347              params->name, params->page_size, spsn->flash.size);
 348
 349        return &spsn->flash;
 350}
 351