uboot/drivers/mtd/spi/stmicro.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2000-2002
   3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   4 *
   5 * Copyright 2008, Network Appliance Inc.
   6 * Jason McMullan <mcmullan@netapp.com>
   7 *
   8 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
   9 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
  10 *
  11 * See file CREDITS for list of people who contributed to this
  12 * project.
  13 *
  14 * This program is free software; you can redistribute it and/or
  15 * modify it under the terms of the GNU General Public License as
  16 * published by the Free Software Foundation; either version 2 of
  17 * the License, or (at your option) any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 * GNU General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License
  25 * along with this program; if not, write to the Free Software
  26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  27 * MA 02111-1307 USA
  28 */
  29
  30#include <common.h>
  31#include <malloc.h>
  32#include <spi_flash.h>
  33
  34#include "spi_flash_internal.h"
  35
  36/* M25Pxx-specific commands */
  37#define CMD_M25PXX_WREN         0x06    /* Write Enable */
  38#define CMD_M25PXX_WRDI         0x04    /* Write Disable */
  39#define CMD_M25PXX_RDSR         0x05    /* Read Status Register */
  40#define CMD_M25PXX_WRSR         0x01    /* Write Status Register */
  41#define CMD_M25PXX_READ         0x03    /* Read Data Bytes */
  42#define CMD_M25PXX_FAST_READ    0x0b    /* Read Data Bytes at Higher Speed */
  43#define CMD_M25PXX_PP           0x02    /* Page Program */
  44#define CMD_M25PXX_SE           0xd8    /* Sector Erase */
  45#define CMD_M25PXX_BE           0xc7    /* Bulk Erase */
  46#define CMD_M25PXX_DP           0xb9    /* Deep Power-down */
  47#define CMD_M25PXX_RES          0xab    /* Release from DP, and Read Signature */
  48
  49#define STM_ID_M25P10           0x11
  50#define STM_ID_M25P16           0x15
  51#define STM_ID_M25P20           0x12
  52#define STM_ID_M25P32           0x16
  53#define STM_ID_M25P40           0x13
  54#define STM_ID_M25P64           0x17
  55#define STM_ID_M25P80           0x14
  56#define STM_ID_M25P128          0x18
  57
  58#define STMICRO_SR_WIP          (1 << 0)        /* Write-in-Progress */
  59
  60struct stmicro_spi_flash_params {
  61        u8 idcode1;
  62        u16 page_size;
  63        u16 pages_per_sector;
  64        u16 nr_sectors;
  65        const char *name;
  66};
  67
  68/* spi_flash needs to be first so upper layers can free() it */
  69struct stmicro_spi_flash {
  70        struct spi_flash flash;
  71        const struct stmicro_spi_flash_params *params;
  72};
  73
  74static inline struct stmicro_spi_flash *to_stmicro_spi_flash(struct spi_flash
  75                                                             *flash)
  76{
  77        return container_of(flash, struct stmicro_spi_flash, flash);
  78}
  79
  80static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = {
  81        {
  82                .idcode1 = STM_ID_M25P10,
  83                .page_size = 256,
  84                .pages_per_sector = 128,
  85                .nr_sectors = 4,
  86                .name = "M25P10",
  87        },
  88        {
  89                .idcode1 = STM_ID_M25P16,
  90                .page_size = 256,
  91                .pages_per_sector = 256,
  92                .nr_sectors = 32,
  93                .name = "M25P16",
  94        },
  95        {
  96                .idcode1 = STM_ID_M25P20,
  97                .page_size = 256,
  98                .pages_per_sector = 256,
  99                .nr_sectors = 4,
 100                .name = "M25P20",
 101        },
 102        {
 103                .idcode1 = STM_ID_M25P32,
 104                .page_size = 256,
 105                .pages_per_sector = 256,
 106                .nr_sectors = 64,
 107                .name = "M25P32",
 108        },
 109        {
 110                .idcode1 = STM_ID_M25P40,
 111                .page_size = 256,
 112                .pages_per_sector = 256,
 113                .nr_sectors = 8,
 114                .name = "M25P40",
 115        },
 116        {
 117                .idcode1 = STM_ID_M25P64,
 118                .page_size = 256,
 119                .pages_per_sector = 256,
 120                .nr_sectors = 128,
 121                .name = "M25P64",
 122        },
 123        {
 124                .idcode1 = STM_ID_M25P80,
 125                .page_size = 256,
 126                .pages_per_sector = 256,
 127                .nr_sectors = 16,
 128                .name = "M25P80",
 129        },
 130        {
 131                .idcode1 = STM_ID_M25P128,
 132                .page_size = 256,
 133                .pages_per_sector = 1024,
 134                .nr_sectors = 64,
 135                .name = "M25P128",
 136        },
 137};
 138
 139static int stmicro_wait_ready(struct spi_flash *flash, unsigned long timeout)
 140{
 141        struct spi_slave *spi = flash->spi;
 142        unsigned long timebase;
 143        int ret;
 144        u8 cmd = CMD_M25PXX_RDSR;
 145        u8 status;
 146
 147        ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN);
 148        if (ret) {
 149                debug("SF: Failed to send command %02x: %d\n", cmd, ret);
 150                return ret;
 151        }
 152
 153        timebase = get_timer(0);
 154        do {
 155                ret = spi_xfer(spi, 8, NULL, &status, 0);
 156                if (ret)
 157                        return -1;
 158
 159                if ((status & STMICRO_SR_WIP) == 0)
 160                        break;
 161
 162        } while (get_timer(timebase) < timeout);
 163
 164        spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END);
 165
 166        if ((status & STMICRO_SR_WIP) == 0)
 167                return 0;
 168
 169        /* Timed out */
 170        return -1;
 171}
 172
 173static int stmicro_read_fast(struct spi_flash *flash,
 174                             u32 offset, size_t len, void *buf)
 175{
 176        struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
 177        unsigned long page_addr;
 178        unsigned long page_size;
 179        u8 cmd[5];
 180
 181        page_size = stm->params->page_size;
 182        page_addr = offset / page_size;
 183
 184        cmd[0] = CMD_READ_ARRAY_FAST;
 185        cmd[1] = page_addr >> 8;
 186        cmd[2] = page_addr;
 187        cmd[3] = offset % page_size;
 188        cmd[4] = 0x00;
 189
 190        return spi_flash_read_common(flash, cmd, sizeof(cmd), buf, len);
 191}
 192
 193static int stmicro_write(struct spi_flash *flash,
 194                         u32 offset, size_t len, const void *buf)
 195{
 196        struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
 197        unsigned long page_addr;
 198        unsigned long byte_addr;
 199        unsigned long page_size;
 200        size_t chunk_len;
 201        size_t actual;
 202        int ret;
 203        u8 cmd[4];
 204
 205        page_size = stm->params->page_size;
 206        page_addr = offset / page_size;
 207        byte_addr = offset % page_size;
 208
 209        ret = spi_claim_bus(flash->spi);
 210        if (ret) {
 211                debug("SF: Unable to claim SPI bus\n");
 212                return ret;
 213        }
 214
 215        ret = 0;
 216        for (actual = 0; actual < len; actual += chunk_len) {
 217                chunk_len = min(len - actual, page_size - byte_addr);
 218
 219                cmd[0] = CMD_M25PXX_PP;
 220                cmd[1] = page_addr >> 8;
 221                cmd[2] = page_addr;
 222                cmd[3] = byte_addr;
 223
 224                debug
 225                    ("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %d\n",
 226                     buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len);
 227
 228                ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
 229                if (ret < 0) {
 230                        debug("SF: Enabling Write failed\n");
 231                        break;
 232                }
 233
 234                ret = spi_flash_cmd_write(flash->spi, cmd, 4,
 235                                          buf + actual, chunk_len);
 236                if (ret < 0) {
 237                        debug("SF: STMicro Page Program failed\n");
 238                        break;
 239                }
 240
 241                ret = stmicro_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
 242                if (ret < 0) {
 243                        debug("SF: STMicro page programming timed out\n");
 244                        break;
 245                }
 246
 247                page_addr++;
 248                byte_addr = 0;
 249        }
 250
 251        debug("SF: STMicro: Successfully programmed %u bytes @ 0x%x\n",
 252              len, offset);
 253
 254        spi_release_bus(flash->spi);
 255        return ret;
 256}
 257
 258int stmicro_erase(struct spi_flash *flash, u32 offset, size_t len)
 259{
 260        struct stmicro_spi_flash *stm = to_stmicro_spi_flash(flash);
 261        unsigned long sector_size;
 262        size_t actual;
 263        int ret;
 264        u8 cmd[4];
 265
 266        /*
 267         * This function currently uses sector erase only.
 268         * probably speed things up by using bulk erase
 269         * when possible.
 270         */
 271
 272        sector_size = stm->params->page_size * stm->params->pages_per_sector;
 273
 274        if (offset % sector_size || len % sector_size) {
 275                debug("SF: Erase offset/length not multiple of sector size\n");
 276                return -1;
 277        }
 278
 279        len /= sector_size;
 280        cmd[0] = CMD_M25PXX_SE;
 281        cmd[2] = 0x00;
 282        cmd[3] = 0x00;
 283
 284        ret = spi_claim_bus(flash->spi);
 285        if (ret) {
 286                debug("SF: Unable to claim SPI bus\n");
 287                return ret;
 288        }
 289
 290        ret = 0;
 291        for (actual = 0; actual < len; actual++) {
 292                cmd[1] = offset >> 16;
 293                offset += sector_size;
 294
 295                ret = spi_flash_cmd(flash->spi, CMD_M25PXX_WREN, NULL, 0);
 296                if (ret < 0) {
 297                        debug("SF: Enabling Write failed\n");
 298                        break;
 299                }
 300
 301                ret = spi_flash_cmd_write(flash->spi, cmd, 4, NULL, 0);
 302                if (ret < 0) {
 303                        debug("SF: STMicro page erase failed\n");
 304                        break;
 305                }
 306
 307                ret = stmicro_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
 308                if (ret < 0) {
 309                        debug("SF: STMicro page erase timed out\n");
 310                        break;
 311                }
 312        }
 313
 314        debug("SF: STMicro: Successfully erased %u bytes @ 0x%x\n",
 315              len * sector_size, offset);
 316
 317        spi_release_bus(flash->spi);
 318        return ret;
 319}
 320
 321struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
 322{
 323        const struct stmicro_spi_flash_params *params;
 324        struct stmicro_spi_flash *stm;
 325        unsigned int i;
 326
 327        if (idcode[0] == 0xff) {
 328                i = spi_flash_cmd(spi, CMD_M25PXX_RES,
 329                                  idcode, 4);
 330                if (i)
 331                        return NULL;
 332                if ((idcode[3] & 0xf0) == 0x10) {
 333                        idcode[0] = 0x20;
 334                        idcode[1] = 0x20;
 335                        idcode[2] = idcode[3] + 1;
 336                } else
 337                        return NULL;
 338        }
 339
 340        for (i = 0; i < ARRAY_SIZE(stmicro_spi_flash_table); i++) {
 341                params = &stmicro_spi_flash_table[i];
 342                if (params->idcode1 == idcode[2]) {
 343                        break;
 344                }
 345        }
 346
 347        if (i == ARRAY_SIZE(stmicro_spi_flash_table)) {
 348                debug("SF: Unsupported STMicro ID %02x\n", idcode[1]);
 349                return NULL;
 350        }
 351
 352        stm = malloc(sizeof(struct stmicro_spi_flash));
 353        if (!stm) {
 354                debug("SF: Failed to allocate memory\n");
 355                return NULL;
 356        }
 357
 358        stm->params = params;
 359        stm->flash.spi = spi;
 360        stm->flash.name = params->name;
 361
 362        stm->flash.write = stmicro_write;
 363        stm->flash.erase = stmicro_erase;
 364        stm->flash.read = stmicro_read_fast;
 365        stm->flash.size = params->page_size * params->pages_per_sector
 366            * params->nr_sectors;
 367
 368        printf("SF: Detected %s with page size %u, total ",
 369               params->name, params->page_size);
 370        print_size(stm->flash.size, "\n");
 371
 372        return &stm->flash;
 373}
 374