1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21d6432feSDavid Brownell /* 31d6432feSDavid Brownell * Atmel AT45xxx DataFlash MTD driver for lightweight SPI framework 41d6432feSDavid Brownell * 51d6432feSDavid Brownell * Largely derived from at91_dataflash.c: 61d6432feSDavid Brownell * Copyright (C) 2003-2005 SAN People (Pty) Ltd 71d6432feSDavid Brownell */ 81d6432feSDavid Brownell #include <linux/module.h> 91d6432feSDavid Brownell #include <linux/slab.h> 101d6432feSDavid Brownell #include <linux/delay.h> 111d6432feSDavid Brownell #include <linux/device.h> 12ec9ce52eSMatthias Kaehlcke #include <linux/mutex.h> 13771999b6Sakpm@linux-foundation.org #include <linux/err.h> 145b7f3a50SArtem Bityutskiy #include <linux/math64.h> 15b94e757cSShawn Guo #include <linux/of.h> 16b94e757cSShawn Guo #include <linux/of_device.h> 17771999b6Sakpm@linux-foundation.org 181d6432feSDavid Brownell #include <linux/spi/spi.h> 191d6432feSDavid Brownell #include <linux/spi/flash.h> 201d6432feSDavid Brownell 211d6432feSDavid Brownell #include <linux/mtd/mtd.h> 221d6432feSDavid Brownell #include <linux/mtd/partitions.h> 231d6432feSDavid Brownell 241d6432feSDavid Brownell /* 251d6432feSDavid Brownell * DataFlash is a kind of SPI flash. Most AT45 chips have two buffers in 261d6432feSDavid Brownell * each chip, which may be used for double buffered I/O; but this driver 271d6432feSDavid Brownell * doesn't (yet) use these for any kind of i/o overlap or prefetching. 281d6432feSDavid Brownell * 291d6432feSDavid Brownell * Sometimes DataFlash is packaged in MMC-format cards, although the 308c64038eSDavid Brownell * MMC stack can't (yet?) distinguish between MMC and DataFlash 311d6432feSDavid Brownell * protocols during enumeration. 321d6432feSDavid Brownell */ 331d6432feSDavid Brownell 341d6432feSDavid Brownell /* reads can bypass the buffers */ 351d6432feSDavid Brownell #define OP_READ_CONTINUOUS 0xE8 361d6432feSDavid Brownell #define OP_READ_PAGE 0xD2 371d6432feSDavid Brownell 381d6432feSDavid Brownell /* group B requests can run even while status reports "busy" */ 391d6432feSDavid Brownell #define OP_READ_STATUS 0xD7 /* group B */ 401d6432feSDavid Brownell 411d6432feSDavid Brownell /* move data between host and buffer */ 421d6432feSDavid Brownell #define OP_READ_BUFFER1 0xD4 /* group B */ 431d6432feSDavid Brownell #define OP_READ_BUFFER2 0xD6 /* group B */ 441d6432feSDavid Brownell #define OP_WRITE_BUFFER1 0x84 /* group B */ 451d6432feSDavid Brownell #define OP_WRITE_BUFFER2 0x87 /* group B */ 461d6432feSDavid Brownell 471d6432feSDavid Brownell /* erasing flash */ 481d6432feSDavid Brownell #define OP_ERASE_PAGE 0x81 491d6432feSDavid Brownell #define OP_ERASE_BLOCK 0x50 501d6432feSDavid Brownell 511d6432feSDavid Brownell /* move data between buffer and flash */ 521d6432feSDavid Brownell #define OP_TRANSFER_BUF1 0x53 531d6432feSDavid Brownell #define OP_TRANSFER_BUF2 0x55 541d6432feSDavid Brownell #define OP_MREAD_BUFFER1 0xD4 551d6432feSDavid Brownell #define OP_MREAD_BUFFER2 0xD6 561d6432feSDavid Brownell #define OP_MWERASE_BUFFER1 0x83 571d6432feSDavid Brownell #define OP_MWERASE_BUFFER2 0x86 581d6432feSDavid Brownell #define OP_MWRITE_BUFFER1 0x88 /* sector must be pre-erased */ 591d6432feSDavid Brownell #define OP_MWRITE_BUFFER2 0x89 /* sector must be pre-erased */ 601d6432feSDavid Brownell 611d6432feSDavid Brownell /* write to buffer, then write-erase to flash */ 621d6432feSDavid Brownell #define OP_PROGRAM_VIA_BUF1 0x82 631d6432feSDavid Brownell #define OP_PROGRAM_VIA_BUF2 0x85 641d6432feSDavid Brownell 651d6432feSDavid Brownell /* compare buffer to flash */ 661d6432feSDavid Brownell #define OP_COMPARE_BUF1 0x60 671d6432feSDavid Brownell #define OP_COMPARE_BUF2 0x61 681d6432feSDavid Brownell 691d6432feSDavid Brownell /* read flash to buffer, then write-erase to flash */ 701d6432feSDavid Brownell #define OP_REWRITE_VIA_BUF1 0x58 711d6432feSDavid Brownell #define OP_REWRITE_VIA_BUF2 0x59 721d6432feSDavid Brownell 731d6432feSDavid Brownell /* newer chips report JEDEC manufacturer and device IDs; chip 741d6432feSDavid Brownell * serial number and OTP bits; and per-sector writeprotect. 751d6432feSDavid Brownell */ 761d6432feSDavid Brownell #define OP_READ_ID 0x9F 771d6432feSDavid Brownell #define OP_READ_SECURITY 0x77 7834a82443SDavid Brownell #define OP_WRITE_SECURITY_REVC 0x9A 7934a82443SDavid Brownell #define OP_WRITE_SECURITY 0x9B /* revision D */ 801d6432feSDavid Brownell 8141c9c662SAndrey Smirnov #define CFI_MFR_ATMEL 0x1F 821d6432feSDavid Brownell 831da8869aSAndrey Smirnov #define DATAFLASH_SHIFT_EXTID 24 841da8869aSAndrey Smirnov #define DATAFLASH_SHIFT_ID 40 851da8869aSAndrey Smirnov 861d6432feSDavid Brownell struct dataflash { 87c41e43c6SAndrey Smirnov u8 command[4]; 881d6432feSDavid Brownell char name[24]; 891d6432feSDavid Brownell 901d6432feSDavid Brownell unsigned short page_offset; /* offset in flash address */ 911d6432feSDavid Brownell unsigned int page_size; /* of bytes per page */ 921d6432feSDavid Brownell 93ec9ce52eSMatthias Kaehlcke struct mutex lock; 941d6432feSDavid Brownell struct spi_device *spi; 951d6432feSDavid Brownell 961d6432feSDavid Brownell struct mtd_info mtd; 971d6432feSDavid Brownell }; 981d6432feSDavid Brownell 99b94e757cSShawn Guo #ifdef CONFIG_OF 100b94e757cSShawn Guo static const struct of_device_id dataflash_dt_ids[] = { 101b94e757cSShawn Guo { .compatible = "atmel,at45", }, 102b94e757cSShawn Guo { .compatible = "atmel,dataflash", }, 103b94e757cSShawn Guo { /* sentinel */ } 104b94e757cSShawn Guo }; 105d1d97b76SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, dataflash_dt_ids); 106b94e757cSShawn Guo #endif 107b94e757cSShawn Guo 1081d6432feSDavid Brownell /* ......................................................................... */ 1091d6432feSDavid Brownell 1101d6432feSDavid Brownell /* 1111d6432feSDavid Brownell * Return the status of the DataFlash device. 1121d6432feSDavid Brownell */ 1131d6432feSDavid Brownell static inline int dataflash_status(struct spi_device *spi) 1141d6432feSDavid Brownell { 1151d6432feSDavid Brownell /* NOTE: at45db321c over 25 MHz wants to write 1161d6432feSDavid Brownell * a dummy byte after the opcode... 1171d6432feSDavid Brownell */ 1181d6432feSDavid Brownell return spi_w8r8(spi, OP_READ_STATUS); 1191d6432feSDavid Brownell } 1201d6432feSDavid Brownell 1211d6432feSDavid Brownell /* 1221d6432feSDavid Brownell * Poll the DataFlash device until it is READY. 1231d6432feSDavid Brownell * This usually takes 5-20 msec or so; more for sector erase. 1241d6432feSDavid Brownell */ 1251d6432feSDavid Brownell static int dataflash_waitready(struct spi_device *spi) 1261d6432feSDavid Brownell { 1271d6432feSDavid Brownell int status; 1281d6432feSDavid Brownell 1291d6432feSDavid Brownell for (;;) { 1301d6432feSDavid Brownell status = dataflash_status(spi); 1311d6432feSDavid Brownell if (status < 0) { 13202f62864SAndrey Smirnov dev_dbg(&spi->dev, "status %d?\n", status); 1331d6432feSDavid Brownell status = 0; 1341d6432feSDavid Brownell } 1351d6432feSDavid Brownell 1361d6432feSDavid Brownell if (status & (1 << 7)) /* RDY/nBSY */ 1371d6432feSDavid Brownell return status; 1381d6432feSDavid Brownell 139c3c9a2c4SLuca Ellero usleep_range(3000, 4000); 1401d6432feSDavid Brownell } 1411d6432feSDavid Brownell } 1421d6432feSDavid Brownell 1431d6432feSDavid Brownell /* ......................................................................... */ 1441d6432feSDavid Brownell 1451d6432feSDavid Brownell /* 1461d6432feSDavid Brownell * Erase pages of flash. 1471d6432feSDavid Brownell */ 1481d6432feSDavid Brownell static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) 1491d6432feSDavid Brownell { 15042845d2aSKulikov Vasiliy struct dataflash *priv = mtd->priv; 1511d6432feSDavid Brownell struct spi_device *spi = priv->spi; 1527a95db03SMark Brown struct spi_transfer x = { }; 1531d6432feSDavid Brownell struct spi_message msg; 1541d6432feSDavid Brownell unsigned blocksize = priv->page_size << 3; 155c41e43c6SAndrey Smirnov u8 *command; 156c41e43c6SAndrey Smirnov u32 rem; 1571d6432feSDavid Brownell 15802f62864SAndrey Smirnov dev_dbg(&spi->dev, "erase addr=0x%llx len 0x%llx\n", 15902f62864SAndrey Smirnov (long long)instr->addr, (long long)instr->len); 1601d6432feSDavid Brownell 1615b7f3a50SArtem Bityutskiy div_u64_rem(instr->len, priv->page_size, &rem); 1625b7f3a50SArtem Bityutskiy if (rem) 1635b7f3a50SArtem Bityutskiy return -EINVAL; 1645b7f3a50SArtem Bityutskiy div_u64_rem(instr->addr, priv->page_size, &rem); 1655b7f3a50SArtem Bityutskiy if (rem) 1661d6432feSDavid Brownell return -EINVAL; 1671d6432feSDavid Brownell 1688275c642SVitaly Wool spi_message_init(&msg); 1698275c642SVitaly Wool 1708275c642SVitaly Wool x.tx_buf = command = priv->command; 1718275c642SVitaly Wool x.len = 4; 1728275c642SVitaly Wool spi_message_add_tail(&x, &msg); 1731d6432feSDavid Brownell 174ec9ce52eSMatthias Kaehlcke mutex_lock(&priv->lock); 1751d6432feSDavid Brownell while (instr->len > 0) { 1761d6432feSDavid Brownell unsigned int pageaddr; 1771d6432feSDavid Brownell int status; 1781d6432feSDavid Brownell int do_block; 1791d6432feSDavid Brownell 1801d6432feSDavid Brownell /* Calculate flash page address; use block erase (for speed) if 1811d6432feSDavid Brownell * we're at a block boundary and need to erase the whole block. 1821d6432feSDavid Brownell */ 183dbf8c11fSPeter Korsgaard pageaddr = div_u64(instr->addr, priv->page_size); 1843cb4f09fSDavid Brownell do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize; 1851d6432feSDavid Brownell pageaddr = pageaddr << priv->page_offset; 1861d6432feSDavid Brownell 1871d6432feSDavid Brownell command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE; 188c41e43c6SAndrey Smirnov command[1] = (u8)(pageaddr >> 16); 189c41e43c6SAndrey Smirnov command[2] = (u8)(pageaddr >> 8); 1901d6432feSDavid Brownell command[3] = 0; 1911d6432feSDavid Brownell 19202f62864SAndrey Smirnov dev_dbg(&spi->dev, "ERASE %s: (%x) %x %x %x [%i]\n", 1931d6432feSDavid Brownell do_block ? "block" : "page", 1941d6432feSDavid Brownell command[0], command[1], command[2], command[3], 1951d6432feSDavid Brownell pageaddr); 1961d6432feSDavid Brownell 1971d6432feSDavid Brownell status = spi_sync(spi, &msg); 1981d6432feSDavid Brownell (void) dataflash_waitready(spi); 1991d6432feSDavid Brownell 2001d6432feSDavid Brownell if (status < 0) { 20102f62864SAndrey Smirnov dev_err(&spi->dev, "erase %x, err %d\n", 20202f62864SAndrey Smirnov pageaddr, status); 2031d6432feSDavid Brownell /* REVISIT: can retry instr->retries times; or 2041d6432feSDavid Brownell * giveup and instr->fail_addr = instr->addr; 2051d6432feSDavid Brownell */ 2061d6432feSDavid Brownell continue; 2071d6432feSDavid Brownell } 2081d6432feSDavid Brownell 2091d6432feSDavid Brownell if (do_block) { 2101d6432feSDavid Brownell instr->addr += blocksize; 2111d6432feSDavid Brownell instr->len -= blocksize; 2121d6432feSDavid Brownell } else { 2131d6432feSDavid Brownell instr->addr += priv->page_size; 2141d6432feSDavid Brownell instr->len -= priv->page_size; 2151d6432feSDavid Brownell } 2161d6432feSDavid Brownell } 217ec9ce52eSMatthias Kaehlcke mutex_unlock(&priv->lock); 2181d6432feSDavid Brownell 2191d6432feSDavid Brownell return 0; 2201d6432feSDavid Brownell } 2211d6432feSDavid Brownell 2221d6432feSDavid Brownell /* 2231d6432feSDavid Brownell * Read from the DataFlash device. 2241d6432feSDavid Brownell * from : Start offset in flash device 2251d6432feSDavid Brownell * len : Amount to read 2261d6432feSDavid Brownell * retlen : About of data actually read 2271d6432feSDavid Brownell * buf : Buffer containing the data 2281d6432feSDavid Brownell */ 2291d6432feSDavid Brownell static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, 2301d6432feSDavid Brownell size_t *retlen, u_char *buf) 2311d6432feSDavid Brownell { 23242845d2aSKulikov Vasiliy struct dataflash *priv = mtd->priv; 2337a95db03SMark Brown struct spi_transfer x[2] = { }; 2341d6432feSDavid Brownell struct spi_message msg; 2351d6432feSDavid Brownell unsigned int addr; 236c41e43c6SAndrey Smirnov u8 *command; 2371d6432feSDavid Brownell int status; 2381d6432feSDavid Brownell 23902f62864SAndrey Smirnov dev_dbg(&priv->spi->dev, "read 0x%x..0x%x\n", 24002f62864SAndrey Smirnov (unsigned int)from, (unsigned int)(from + len)); 2411d6432feSDavid Brownell 2421d6432feSDavid Brownell /* Calculate flash page/byte address */ 2431d6432feSDavid Brownell addr = (((unsigned)from / priv->page_size) << priv->page_offset) 2441d6432feSDavid Brownell + ((unsigned)from % priv->page_size); 2451d6432feSDavid Brownell 2461d6432feSDavid Brownell command = priv->command; 2471d6432feSDavid Brownell 24802f62864SAndrey Smirnov dev_dbg(&priv->spi->dev, "READ: (%x) %x %x %x\n", 2491d6432feSDavid Brownell command[0], command[1], command[2], command[3]); 2501d6432feSDavid Brownell 2518275c642SVitaly Wool spi_message_init(&msg); 2528275c642SVitaly Wool 2531d6432feSDavid Brownell x[0].tx_buf = command; 2541d6432feSDavid Brownell x[0].len = 8; 2558275c642SVitaly Wool spi_message_add_tail(&x[0], &msg); 2568275c642SVitaly Wool 2571d6432feSDavid Brownell x[1].rx_buf = buf; 2581d6432feSDavid Brownell x[1].len = len; 2598275c642SVitaly Wool spi_message_add_tail(&x[1], &msg); 2601d6432feSDavid Brownell 261ec9ce52eSMatthias Kaehlcke mutex_lock(&priv->lock); 2621d6432feSDavid Brownell 2631d6432feSDavid Brownell /* Continuous read, max clock = f(car) which may be less than 2641d6432feSDavid Brownell * the peak rate available. Some chips support commands with 2651d6432feSDavid Brownell * fewer "don't care" bytes. Both buffers stay unchanged. 2661d6432feSDavid Brownell */ 2671d6432feSDavid Brownell command[0] = OP_READ_CONTINUOUS; 268c41e43c6SAndrey Smirnov command[1] = (u8)(addr >> 16); 269c41e43c6SAndrey Smirnov command[2] = (u8)(addr >> 8); 270c41e43c6SAndrey Smirnov command[3] = (u8)(addr >> 0); 2711d6432feSDavid Brownell /* plus 4 "don't care" bytes */ 2721d6432feSDavid Brownell 2731d6432feSDavid Brownell status = spi_sync(priv->spi, &msg); 274ec9ce52eSMatthias Kaehlcke mutex_unlock(&priv->lock); 2751d6432feSDavid Brownell 2761d6432feSDavid Brownell if (status >= 0) { 2771d6432feSDavid Brownell *retlen = msg.actual_length - 8; 2781d6432feSDavid Brownell status = 0; 2791d6432feSDavid Brownell } else 28002f62864SAndrey Smirnov dev_dbg(&priv->spi->dev, "read %x..%x --> %d\n", 2811d6432feSDavid Brownell (unsigned)from, (unsigned)(from + len), 2821d6432feSDavid Brownell status); 2831d6432feSDavid Brownell return status; 2841d6432feSDavid Brownell } 2851d6432feSDavid Brownell 2861d6432feSDavid Brownell /* 2871d6432feSDavid Brownell * Write to the DataFlash device. 2881d6432feSDavid Brownell * to : Start offset in flash device 2891d6432feSDavid Brownell * len : Amount to write 2901d6432feSDavid Brownell * retlen : Amount of data actually written 2911d6432feSDavid Brownell * buf : Buffer containing the data 2921d6432feSDavid Brownell */ 2931d6432feSDavid Brownell static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, 2941d6432feSDavid Brownell size_t * retlen, const u_char * buf) 2951d6432feSDavid Brownell { 29642845d2aSKulikov Vasiliy struct dataflash *priv = mtd->priv; 2971d6432feSDavid Brownell struct spi_device *spi = priv->spi; 2987a95db03SMark Brown struct spi_transfer x[2] = { }; 2991d6432feSDavid Brownell struct spi_message msg; 3001d6432feSDavid Brownell unsigned int pageaddr, addr, offset, writelen; 3011d6432feSDavid Brownell size_t remaining = len; 3021d6432feSDavid Brownell u_char *writebuf = (u_char *) buf; 3031d6432feSDavid Brownell int status = -EINVAL; 304c41e43c6SAndrey Smirnov u8 *command; 3051d6432feSDavid Brownell 30602f62864SAndrey Smirnov dev_dbg(&spi->dev, "write 0x%x..0x%x\n", 30702f62864SAndrey Smirnov (unsigned int)to, (unsigned int)(to + len)); 3081d6432feSDavid Brownell 3098275c642SVitaly Wool spi_message_init(&msg); 3108275c642SVitaly Wool 3111d6432feSDavid Brownell x[0].tx_buf = command = priv->command; 3121d6432feSDavid Brownell x[0].len = 4; 3138275c642SVitaly Wool spi_message_add_tail(&x[0], &msg); 3141d6432feSDavid Brownell 3151d6432feSDavid Brownell pageaddr = ((unsigned)to / priv->page_size); 3161d6432feSDavid Brownell offset = ((unsigned)to % priv->page_size); 3171d6432feSDavid Brownell if (offset + len > priv->page_size) 3181d6432feSDavid Brownell writelen = priv->page_size - offset; 3191d6432feSDavid Brownell else 3201d6432feSDavid Brownell writelen = len; 3211d6432feSDavid Brownell 322ec9ce52eSMatthias Kaehlcke mutex_lock(&priv->lock); 3231d6432feSDavid Brownell while (remaining > 0) { 32402f62864SAndrey Smirnov dev_dbg(&spi->dev, "write @ %i:%i len=%i\n", 3251d6432feSDavid Brownell pageaddr, offset, writelen); 3261d6432feSDavid Brownell 3271d6432feSDavid Brownell /* REVISIT: 3281d6432feSDavid Brownell * (a) each page in a sector must be rewritten at least 3291d6432feSDavid Brownell * once every 10K sibling erase/program operations. 3301d6432feSDavid Brownell * (b) for pages that are already erased, we could 3311d6432feSDavid Brownell * use WRITE+MWRITE not PROGRAM for ~30% speedup. 3321d6432feSDavid Brownell * (c) WRITE to buffer could be done while waiting for 3331d6432feSDavid Brownell * a previous MWRITE/MWERASE to complete ... 3341d6432feSDavid Brownell * (d) error handling here seems to be mostly missing. 3351d6432feSDavid Brownell * 3361d6432feSDavid Brownell * Two persistent bits per page, plus a per-sector counter, 3371d6432feSDavid Brownell * could support (a) and (b) ... we might consider using 3381d6432feSDavid Brownell * the second half of sector zero, which is just one block, 3391d6432feSDavid Brownell * to track that state. (On AT91, that sector should also 3401d6432feSDavid Brownell * support boot-from-DataFlash.) 3411d6432feSDavid Brownell */ 3421d6432feSDavid Brownell 3431d6432feSDavid Brownell addr = pageaddr << priv->page_offset; 3441d6432feSDavid Brownell 3451d6432feSDavid Brownell /* (1) Maybe transfer partial page to Buffer1 */ 3461d6432feSDavid Brownell if (writelen != priv->page_size) { 3471d6432feSDavid Brownell command[0] = OP_TRANSFER_BUF1; 3481d6432feSDavid Brownell command[1] = (addr & 0x00FF0000) >> 16; 3491d6432feSDavid Brownell command[2] = (addr & 0x0000FF00) >> 8; 3501d6432feSDavid Brownell command[3] = 0; 3511d6432feSDavid Brownell 35202f62864SAndrey Smirnov dev_dbg(&spi->dev, "TRANSFER: (%x) %x %x %x\n", 3531d6432feSDavid Brownell command[0], command[1], command[2], command[3]); 3541d6432feSDavid Brownell 3551d6432feSDavid Brownell status = spi_sync(spi, &msg); 3561d6432feSDavid Brownell if (status < 0) 35702f62864SAndrey Smirnov dev_dbg(&spi->dev, "xfer %u -> %d\n", 35802f62864SAndrey Smirnov addr, status); 3591d6432feSDavid Brownell 3601d6432feSDavid Brownell (void) dataflash_waitready(priv->spi); 3611d6432feSDavid Brownell } 3621d6432feSDavid Brownell 3631d6432feSDavid Brownell /* (2) Program full page via Buffer1 */ 3641d6432feSDavid Brownell addr += offset; 3651d6432feSDavid Brownell command[0] = OP_PROGRAM_VIA_BUF1; 3661d6432feSDavid Brownell command[1] = (addr & 0x00FF0000) >> 16; 3671d6432feSDavid Brownell command[2] = (addr & 0x0000FF00) >> 8; 3681d6432feSDavid Brownell command[3] = (addr & 0x000000FF); 3691d6432feSDavid Brownell 37002f62864SAndrey Smirnov dev_dbg(&spi->dev, "PROGRAM: (%x) %x %x %x\n", 3711d6432feSDavid Brownell command[0], command[1], command[2], command[3]); 3721d6432feSDavid Brownell 3731d6432feSDavid Brownell x[1].tx_buf = writebuf; 3741d6432feSDavid Brownell x[1].len = writelen; 3758275c642SVitaly Wool spi_message_add_tail(x + 1, &msg); 3761d6432feSDavid Brownell status = spi_sync(spi, &msg); 3778275c642SVitaly Wool spi_transfer_del(x + 1); 3781d6432feSDavid Brownell if (status < 0) 37902f62864SAndrey Smirnov dev_dbg(&spi->dev, "pgm %u/%u -> %d\n", 38002f62864SAndrey Smirnov addr, writelen, status); 3811d6432feSDavid Brownell 3821d6432feSDavid Brownell (void) dataflash_waitready(priv->spi); 3831d6432feSDavid Brownell 3848275c642SVitaly Wool 38505dd1807SRobert P. J. Day #ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY 3861d6432feSDavid Brownell 3871d6432feSDavid Brownell /* (3) Compare to Buffer1 */ 3881d6432feSDavid Brownell addr = pageaddr << priv->page_offset; 3891d6432feSDavid Brownell command[0] = OP_COMPARE_BUF1; 3901d6432feSDavid Brownell command[1] = (addr & 0x00FF0000) >> 16; 3911d6432feSDavid Brownell command[2] = (addr & 0x0000FF00) >> 8; 3921d6432feSDavid Brownell command[3] = 0; 3931d6432feSDavid Brownell 39402f62864SAndrey Smirnov dev_dbg(&spi->dev, "COMPARE: (%x) %x %x %x\n", 3951d6432feSDavid Brownell command[0], command[1], command[2], command[3]); 3961d6432feSDavid Brownell 3971d6432feSDavid Brownell status = spi_sync(spi, &msg); 3981d6432feSDavid Brownell if (status < 0) 39902f62864SAndrey Smirnov dev_dbg(&spi->dev, "compare %u -> %d\n", 40002f62864SAndrey Smirnov addr, status); 4011d6432feSDavid Brownell 4021d6432feSDavid Brownell status = dataflash_waitready(priv->spi); 4031d6432feSDavid Brownell 4041d6432feSDavid Brownell /* Check result of the compare operation */ 405cccb45d4SAndrew Victor if (status & (1 << 6)) { 40602f62864SAndrey Smirnov dev_err(&spi->dev, "compare page %u, err %d\n", 40702f62864SAndrey Smirnov pageaddr, status); 4081d6432feSDavid Brownell remaining = 0; 4091d6432feSDavid Brownell status = -EIO; 4101d6432feSDavid Brownell break; 4111d6432feSDavid Brownell } else 4121d6432feSDavid Brownell status = 0; 4131d6432feSDavid Brownell 41405dd1807SRobert P. J. Day #endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */ 4151d6432feSDavid Brownell 4161d6432feSDavid Brownell remaining = remaining - writelen; 4171d6432feSDavid Brownell pageaddr++; 4181d6432feSDavid Brownell offset = 0; 4191d6432feSDavid Brownell writebuf += writelen; 4201d6432feSDavid Brownell *retlen += writelen; 4211d6432feSDavid Brownell 4221d6432feSDavid Brownell if (remaining > priv->page_size) 4231d6432feSDavid Brownell writelen = priv->page_size; 4241d6432feSDavid Brownell else 4251d6432feSDavid Brownell writelen = remaining; 4261d6432feSDavid Brownell } 427ec9ce52eSMatthias Kaehlcke mutex_unlock(&priv->lock); 4281d6432feSDavid Brownell 4291d6432feSDavid Brownell return status; 4301d6432feSDavid Brownell } 4311d6432feSDavid Brownell 4321d6432feSDavid Brownell /* ......................................................................... */ 4331d6432feSDavid Brownell 43434a82443SDavid Brownell #ifdef CONFIG_MTD_DATAFLASH_OTP 43534a82443SDavid Brownell 4364b78fc42SChristian Riesch static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len, 4374b78fc42SChristian Riesch size_t *retlen, struct otp_info *info) 43834a82443SDavid Brownell { 43934a82443SDavid Brownell /* Report both blocks as identical: bytes 0..64, locked. 44034a82443SDavid Brownell * Unless the user block changed from all-ones, we can't 44134a82443SDavid Brownell * tell whether it's still writable; so we assume it isn't. 44234a82443SDavid Brownell */ 44334a82443SDavid Brownell info->start = 0; 44434a82443SDavid Brownell info->length = 64; 44534a82443SDavid Brownell info->locked = 1; 4464b78fc42SChristian Riesch *retlen = sizeof(*info); 4474b78fc42SChristian Riesch return 0; 44834a82443SDavid Brownell } 44934a82443SDavid Brownell 45034a82443SDavid Brownell static ssize_t otp_read(struct spi_device *spi, unsigned base, 451c41e43c6SAndrey Smirnov u8 *buf, loff_t off, size_t len) 45234a82443SDavid Brownell { 45334a82443SDavid Brownell struct spi_message m; 45434a82443SDavid Brownell size_t l; 455c41e43c6SAndrey Smirnov u8 *scratch; 45634a82443SDavid Brownell struct spi_transfer t; 45734a82443SDavid Brownell int status; 45834a82443SDavid Brownell 45934a82443SDavid Brownell if (off > 64) 46034a82443SDavid Brownell return -EINVAL; 46134a82443SDavid Brownell 46234a82443SDavid Brownell if ((off + len) > 64) 46334a82443SDavid Brownell len = 64 - off; 46434a82443SDavid Brownell 46534a82443SDavid Brownell spi_message_init(&m); 46634a82443SDavid Brownell 46734a82443SDavid Brownell l = 4 + base + off + len; 46834a82443SDavid Brownell scratch = kzalloc(l, GFP_KERNEL); 46934a82443SDavid Brownell if (!scratch) 47034a82443SDavid Brownell return -ENOMEM; 47134a82443SDavid Brownell 47234a82443SDavid Brownell /* OUT: OP_READ_SECURITY, 3 don't-care bytes, zeroes 47334a82443SDavid Brownell * IN: ignore 4 bytes, data bytes 0..N (max 127) 47434a82443SDavid Brownell */ 47534a82443SDavid Brownell scratch[0] = OP_READ_SECURITY; 47634a82443SDavid Brownell 47734a82443SDavid Brownell memset(&t, 0, sizeof t); 47834a82443SDavid Brownell t.tx_buf = scratch; 47934a82443SDavid Brownell t.rx_buf = scratch; 48034a82443SDavid Brownell t.len = l; 48134a82443SDavid Brownell spi_message_add_tail(&t, &m); 48234a82443SDavid Brownell 48334a82443SDavid Brownell dataflash_waitready(spi); 48434a82443SDavid Brownell 48534a82443SDavid Brownell status = spi_sync(spi, &m); 48634a82443SDavid Brownell if (status >= 0) { 48734a82443SDavid Brownell memcpy(buf, scratch + 4 + base + off, len); 48834a82443SDavid Brownell status = len; 48934a82443SDavid Brownell } 49034a82443SDavid Brownell 49134a82443SDavid Brownell kfree(scratch); 49234a82443SDavid Brownell return status; 49334a82443SDavid Brownell } 49434a82443SDavid Brownell 49534a82443SDavid Brownell static int dataflash_read_fact_otp(struct mtd_info *mtd, 49634a82443SDavid Brownell loff_t from, size_t len, size_t *retlen, u_char *buf) 49734a82443SDavid Brownell { 49842845d2aSKulikov Vasiliy struct dataflash *priv = mtd->priv; 49934a82443SDavid Brownell int status; 50034a82443SDavid Brownell 50134a82443SDavid Brownell /* 64 bytes, from 0..63 ... start at 64 on-chip */ 50234a82443SDavid Brownell mutex_lock(&priv->lock); 50334a82443SDavid Brownell status = otp_read(priv->spi, 64, buf, from, len); 50434a82443SDavid Brownell mutex_unlock(&priv->lock); 50534a82443SDavid Brownell 50634a82443SDavid Brownell if (status < 0) 50734a82443SDavid Brownell return status; 50834a82443SDavid Brownell *retlen = status; 50934a82443SDavid Brownell return 0; 51034a82443SDavid Brownell } 51134a82443SDavid Brownell 51234a82443SDavid Brownell static int dataflash_read_user_otp(struct mtd_info *mtd, 51334a82443SDavid Brownell loff_t from, size_t len, size_t *retlen, u_char *buf) 51434a82443SDavid Brownell { 51542845d2aSKulikov Vasiliy struct dataflash *priv = mtd->priv; 51634a82443SDavid Brownell int status; 51734a82443SDavid Brownell 51834a82443SDavid Brownell /* 64 bytes, from 0..63 ... start at 0 on-chip */ 51934a82443SDavid Brownell mutex_lock(&priv->lock); 52034a82443SDavid Brownell status = otp_read(priv->spi, 0, buf, from, len); 52134a82443SDavid Brownell mutex_unlock(&priv->lock); 52234a82443SDavid Brownell 52334a82443SDavid Brownell if (status < 0) 52434a82443SDavid Brownell return status; 52534a82443SDavid Brownell *retlen = status; 52634a82443SDavid Brownell return 0; 52734a82443SDavid Brownell } 52834a82443SDavid Brownell 52934a82443SDavid Brownell static int dataflash_write_user_otp(struct mtd_info *mtd, 53034a82443SDavid Brownell loff_t from, size_t len, size_t *retlen, u_char *buf) 53134a82443SDavid Brownell { 53234a82443SDavid Brownell struct spi_message m; 53334a82443SDavid Brownell const size_t l = 4 + 64; 534c41e43c6SAndrey Smirnov u8 *scratch; 53534a82443SDavid Brownell struct spi_transfer t; 53642845d2aSKulikov Vasiliy struct dataflash *priv = mtd->priv; 53734a82443SDavid Brownell int status; 53834a82443SDavid Brownell 5399a78bc83SChristian Riesch if (from >= 64) { 5409a78bc83SChristian Riesch /* 5419a78bc83SChristian Riesch * Attempting to write beyond the end of OTP memory, 5429a78bc83SChristian Riesch * no data can be written. 54334a82443SDavid Brownell */ 5449a78bc83SChristian Riesch *retlen = 0; 5459a78bc83SChristian Riesch return 0; 5469a78bc83SChristian Riesch } 5479a78bc83SChristian Riesch 5489a78bc83SChristian Riesch /* Truncate the write to fit into OTP memory. */ 54934a82443SDavid Brownell if ((from + len) > 64) 5509a78bc83SChristian Riesch len = 64 - from; 55134a82443SDavid Brownell 55234a82443SDavid Brownell /* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes 55334a82443SDavid Brownell * IN: ignore all 55434a82443SDavid Brownell */ 55534a82443SDavid Brownell scratch = kzalloc(l, GFP_KERNEL); 55634a82443SDavid Brownell if (!scratch) 55734a82443SDavid Brownell return -ENOMEM; 55834a82443SDavid Brownell scratch[0] = OP_WRITE_SECURITY; 55934a82443SDavid Brownell memcpy(scratch + 4 + from, buf, len); 56034a82443SDavid Brownell 56134a82443SDavid Brownell spi_message_init(&m); 56234a82443SDavid Brownell 56334a82443SDavid Brownell memset(&t, 0, sizeof t); 56434a82443SDavid Brownell t.tx_buf = scratch; 56534a82443SDavid Brownell t.len = l; 56634a82443SDavid Brownell spi_message_add_tail(&t, &m); 56734a82443SDavid Brownell 56834a82443SDavid Brownell /* Write the OTP bits, if they've not yet been written. 56934a82443SDavid Brownell * This modifies SRAM buffer1. 57034a82443SDavid Brownell */ 57134a82443SDavid Brownell mutex_lock(&priv->lock); 57234a82443SDavid Brownell dataflash_waitready(priv->spi); 57334a82443SDavid Brownell status = spi_sync(priv->spi, &m); 57434a82443SDavid Brownell mutex_unlock(&priv->lock); 57534a82443SDavid Brownell 57634a82443SDavid Brownell kfree(scratch); 57734a82443SDavid Brownell 57834a82443SDavid Brownell if (status >= 0) { 57934a82443SDavid Brownell status = 0; 58034a82443SDavid Brownell *retlen = len; 58134a82443SDavid Brownell } 58234a82443SDavid Brownell return status; 58334a82443SDavid Brownell } 58434a82443SDavid Brownell 58534a82443SDavid Brownell static char *otp_setup(struct mtd_info *device, char revision) 58634a82443SDavid Brownell { 5873c3c10bbSArtem Bityutskiy device->_get_fact_prot_info = dataflash_get_otp_info; 5883c3c10bbSArtem Bityutskiy device->_read_fact_prot_reg = dataflash_read_fact_otp; 5893c3c10bbSArtem Bityutskiy device->_get_user_prot_info = dataflash_get_otp_info; 5903c3c10bbSArtem Bityutskiy device->_read_user_prot_reg = dataflash_read_user_otp; 59134a82443SDavid Brownell 59234a82443SDavid Brownell /* rev c parts (at45db321c and at45db1281 only!) use a 59334a82443SDavid Brownell * different write procedure; not (yet?) implemented. 59434a82443SDavid Brownell */ 59534a82443SDavid Brownell if (revision > 'c') 5963c3c10bbSArtem Bityutskiy device->_write_user_prot_reg = dataflash_write_user_otp; 59734a82443SDavid Brownell 59834a82443SDavid Brownell return ", OTP"; 59934a82443SDavid Brownell } 60034a82443SDavid Brownell 60134a82443SDavid Brownell #else 60234a82443SDavid Brownell 603cf93ae02SDavid Brownell static char *otp_setup(struct mtd_info *device, char revision) 60434a82443SDavid Brownell { 60534a82443SDavid Brownell return " (OTP)"; 60634a82443SDavid Brownell } 60734a82443SDavid Brownell 60834a82443SDavid Brownell #endif 60934a82443SDavid Brownell 61034a82443SDavid Brownell /* ......................................................................... */ 61134a82443SDavid Brownell 6121d6432feSDavid Brownell /* 6131d6432feSDavid Brownell * Register DataFlash device with MTD subsystem. 6141d6432feSDavid Brownell */ 615d8929942SGreg Kroah-Hartman static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, 616d8929942SGreg Kroah-Hartman int pagesize, int pageoffset, char revision) 6171d6432feSDavid Brownell { 6181d6432feSDavid Brownell struct dataflash *priv; 6191d6432feSDavid Brownell struct mtd_info *device; 6200278fd3fSJingoo Han struct flash_platform_data *pdata = dev_get_platdata(&spi->dev); 62134a82443SDavid Brownell char *otp_tag = ""; 622d4702669SH Hartley Sweeten int err = 0; 6231d6432feSDavid Brownell 6245cbded58SRobert P. J. Day priv = kzalloc(sizeof *priv, GFP_KERNEL); 6251d6432feSDavid Brownell if (!priv) 6261d6432feSDavid Brownell return -ENOMEM; 6271d6432feSDavid Brownell 628ec9ce52eSMatthias Kaehlcke mutex_init(&priv->lock); 6291d6432feSDavid Brownell priv->spi = spi; 6301d6432feSDavid Brownell priv->page_size = pagesize; 6311d6432feSDavid Brownell priv->page_offset = pageoffset; 6321d6432feSDavid Brownell 6331d6432feSDavid Brownell /* name must be usable with cmdlinepart */ 6341d6432feSDavid Brownell sprintf(priv->name, "spi%d.%d-%s", 6351d6432feSDavid Brownell spi->master->bus_num, spi->chip_select, 6361d6432feSDavid Brownell name); 6371d6432feSDavid Brownell 6381d6432feSDavid Brownell device = &priv->mtd; 6391d6432feSDavid Brownell device->name = (pdata && pdata->name) ? pdata->name : priv->name; 6401d6432feSDavid Brownell device->size = nr_pages * pagesize; 6411d6432feSDavid Brownell device->erasesize = pagesize; 64217ffc7baSArtem B. Bityutskiy device->writesize = pagesize; 6431d6432feSDavid Brownell device->type = MTD_DATAFLASH; 6446c33cafcSHaavard Skinnemoen device->flags = MTD_WRITEABLE; 6453c3c10bbSArtem Bityutskiy device->_erase = dataflash_erase; 6463c3c10bbSArtem Bityutskiy device->_read = dataflash_read; 6473c3c10bbSArtem Bityutskiy device->_write = dataflash_write; 6481d6432feSDavid Brownell device->priv = priv; 6491d6432feSDavid Brownell 65087f39f04SDavid Brownell device->dev.parent = &spi->dev; 651004b5e60SBrian Norris mtd_set_of_node(device, spi->dev.of_node); 65287f39f04SDavid Brownell 65334a82443SDavid Brownell if (revision >= 'c') 65434a82443SDavid Brownell otp_tag = otp_setup(device, revision); 65534a82443SDavid Brownell 6565b7f3a50SArtem Bityutskiy dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n", 6575b7f3a50SArtem Bityutskiy name, (long long)((device->size + 1023) >> 10), 65834a82443SDavid Brownell pagesize, otp_tag); 6595cacbfa9SJingoo Han spi_set_drvdata(spi, priv); 6601d6432feSDavid Brownell 661004b5e60SBrian Norris err = mtd_device_register(device, 6623a8fb12aSDmitry Eremin-Solenikov pdata ? pdata->parts : NULL, 6633a8fb12aSDmitry Eremin-Solenikov pdata ? pdata->nr_parts : 0); 6641d6432feSDavid Brownell 665d4702669SH Hartley Sweeten if (!err) 666d4702669SH Hartley Sweeten return 0; 667d4702669SH Hartley Sweeten 668d4702669SH Hartley Sweeten kfree(priv); 669d4702669SH Hartley Sweeten return err; 6701d6432feSDavid Brownell } 6711d6432feSDavid Brownell 672d8929942SGreg Kroah-Hartman static inline int add_dataflash(struct spi_device *spi, char *name, 67334a82443SDavid Brownell int nr_pages, int pagesize, int pageoffset) 67434a82443SDavid Brownell { 67534a82443SDavid Brownell return add_dataflash_otp(spi, name, nr_pages, pagesize, 67634a82443SDavid Brownell pageoffset, 0); 67734a82443SDavid Brownell } 67834a82443SDavid Brownell 679e9d42227SMichael Hennerich struct flash_info { 680e9d42227SMichael Hennerich char *name; 681e9d42227SMichael Hennerich 682771999b6Sakpm@linux-foundation.org /* JEDEC id has a high byte of zero plus three data bytes: 683771999b6Sakpm@linux-foundation.org * the manufacturer id, then a two byte device id. 684e9d42227SMichael Hennerich */ 6851da8869aSAndrey Smirnov u64 jedec_id; 686e9d42227SMichael Hennerich 687771999b6Sakpm@linux-foundation.org /* The size listed here is what works with OP_ERASE_PAGE. */ 688e9d42227SMichael Hennerich unsigned nr_pages; 689c41e43c6SAndrey Smirnov u16 pagesize; 690c41e43c6SAndrey Smirnov u16 pageoffset; 691e9d42227SMichael Hennerich 692c41e43c6SAndrey Smirnov u16 flags; 6931da8869aSAndrey Smirnov #define SUP_EXTID 0x0004 /* supports extended ID data */ 694771999b6Sakpm@linux-foundation.org #define SUP_POW2PS 0x0002 /* supports 2^N byte pages */ 695771999b6Sakpm@linux-foundation.org #define IS_POW2PS 0x0001 /* uses 2^N byte pages */ 696e9d42227SMichael Hennerich }; 697e9d42227SMichael Hennerich 698042a1909SBill Pemberton static struct flash_info dataflash_data[] = { 699e9d42227SMichael Hennerich 700771999b6Sakpm@linux-foundation.org /* 701771999b6Sakpm@linux-foundation.org * NOTE: chips with SUP_POW2PS (rev D and up) need two entries, 702771999b6Sakpm@linux-foundation.org * one with IS_POW2PS and the other without. The entry with the 703771999b6Sakpm@linux-foundation.org * non-2^N byte page size can't name exact chip revisions without 704771999b6Sakpm@linux-foundation.org * losing backwards compatibility for cmdlinepart. 705771999b6Sakpm@linux-foundation.org * 706771999b6Sakpm@linux-foundation.org * These newer chips also support 128-byte security registers (with 707771999b6Sakpm@linux-foundation.org * 64 bytes one-time-programmable) and software write-protection. 708771999b6Sakpm@linux-foundation.org */ 709771999b6Sakpm@linux-foundation.org { "AT45DB011B", 0x1f2200, 512, 264, 9, SUP_POW2PS}, 710e9d42227SMichael Hennerich { "at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS}, 711e9d42227SMichael Hennerich 712771999b6Sakpm@linux-foundation.org { "AT45DB021B", 0x1f2300, 1024, 264, 9, SUP_POW2PS}, 713e9d42227SMichael Hennerich { "at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS}, 714e9d42227SMichael Hennerich 715771999b6Sakpm@linux-foundation.org { "AT45DB041x", 0x1f2400, 2048, 264, 9, SUP_POW2PS}, 716e9d42227SMichael Hennerich { "at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS}, 717e9d42227SMichael Hennerich 718771999b6Sakpm@linux-foundation.org { "AT45DB081B", 0x1f2500, 4096, 264, 9, SUP_POW2PS}, 719e9d42227SMichael Hennerich { "at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS}, 720e9d42227SMichael Hennerich 721771999b6Sakpm@linux-foundation.org { "AT45DB161x", 0x1f2600, 4096, 528, 10, SUP_POW2PS}, 722e9d42227SMichael Hennerich { "at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS}, 723e9d42227SMichael Hennerich 724771999b6Sakpm@linux-foundation.org { "AT45DB321x", 0x1f2700, 8192, 528, 10, 0}, /* rev C */ 725e9d42227SMichael Hennerich 726771999b6Sakpm@linux-foundation.org { "AT45DB321x", 0x1f2701, 8192, 528, 10, SUP_POW2PS}, 727e9d42227SMichael Hennerich { "at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS}, 728e9d42227SMichael Hennerich 729771999b6Sakpm@linux-foundation.org { "AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS}, 730771999b6Sakpm@linux-foundation.org { "at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS}, 73167e4145eSAndrey Smirnov 732cbdceb9bSGeert Uytterhoeven { "AT45DB641E", 0x1f28000100ULL, 32768, 264, 9, SUP_EXTID | SUP_POW2PS}, 733cbdceb9bSGeert Uytterhoeven { "at45db641e", 0x1f28000100ULL, 32768, 256, 8, SUP_EXTID | SUP_POW2PS | IS_POW2PS}, 734e9d42227SMichael Hennerich }; 735e9d42227SMichael Hennerich 7361da8869aSAndrey Smirnov static struct flash_info *jedec_lookup(struct spi_device *spi, 7371da8869aSAndrey Smirnov u64 jedec, bool use_extid) 738e9d42227SMichael Hennerich { 739e9d42227SMichael Hennerich struct flash_info *info; 740e9d42227SMichael Hennerich int status; 741e9d42227SMichael Hennerich 742a296a1bcSAndrey Smirnov for (info = dataflash_data; 743a296a1bcSAndrey Smirnov info < dataflash_data + ARRAY_SIZE(dataflash_data); 744a296a1bcSAndrey Smirnov info++) { 7451da8869aSAndrey Smirnov if (use_extid && !(info->flags & SUP_EXTID)) 7461da8869aSAndrey Smirnov continue; 7471da8869aSAndrey Smirnov 748e9d42227SMichael Hennerich if (info->jedec_id == jedec) { 74902f62864SAndrey Smirnov dev_dbg(&spi->dev, "OTP, sector protect%s\n", 75002f62864SAndrey Smirnov (info->flags & SUP_POW2PS) ? 75102f62864SAndrey Smirnov ", binary pagesize" : ""); 752e9d42227SMichael Hennerich if (info->flags & SUP_POW2PS) { 753e9d42227SMichael Hennerich status = dataflash_status(spi); 754771999b6Sakpm@linux-foundation.org if (status < 0) { 75502f62864SAndrey Smirnov dev_dbg(&spi->dev, "status error %d\n", 75602f62864SAndrey Smirnov status); 757771999b6Sakpm@linux-foundation.org return ERR_PTR(status); 758771999b6Sakpm@linux-foundation.org } 759771999b6Sakpm@linux-foundation.org if (status & 0x1) { 760771999b6Sakpm@linux-foundation.org if (info->flags & IS_POW2PS) 761771999b6Sakpm@linux-foundation.org return info; 762771999b6Sakpm@linux-foundation.org } else { 763771999b6Sakpm@linux-foundation.org if (!(info->flags & IS_POW2PS)) 764e9d42227SMichael Hennerich return info; 765e9d42227SMichael Hennerich } 766229cc58bSWill Newton } else 767229cc58bSWill Newton return info; 768e9d42227SMichael Hennerich } 769e9d42227SMichael Hennerich } 770e9d42227SMichael Hennerich 7711da8869aSAndrey Smirnov return ERR_PTR(-ENODEV); 7721da8869aSAndrey Smirnov } 7731da8869aSAndrey Smirnov 7741da8869aSAndrey Smirnov static struct flash_info *jedec_probe(struct spi_device *spi) 7751da8869aSAndrey Smirnov { 7761da8869aSAndrey Smirnov int ret; 7771da8869aSAndrey Smirnov u8 code = OP_READ_ID; 7781da8869aSAndrey Smirnov u64 jedec; 7791da8869aSAndrey Smirnov u8 id[sizeof(jedec)] = {0}; 7801da8869aSAndrey Smirnov const unsigned int id_size = 5; 7811da8869aSAndrey Smirnov struct flash_info *info; 7821da8869aSAndrey Smirnov 7831da8869aSAndrey Smirnov /* 7841da8869aSAndrey Smirnov * JEDEC also defines an optional "extended device information" 7851da8869aSAndrey Smirnov * string for after vendor-specific data, after the three bytes 7861da8869aSAndrey Smirnov * we use here. Supporting some chips might require using it. 7871da8869aSAndrey Smirnov * 7881da8869aSAndrey Smirnov * If the vendor ID isn't Atmel's (0x1f), assume this call failed. 7891da8869aSAndrey Smirnov * That's not an error; only rev C and newer chips handle it, and 7901da8869aSAndrey Smirnov * only Atmel sells these chips. 7911da8869aSAndrey Smirnov */ 7921da8869aSAndrey Smirnov ret = spi_write_then_read(spi, &code, 1, id, id_size); 7931da8869aSAndrey Smirnov if (ret < 0) { 7941da8869aSAndrey Smirnov dev_dbg(&spi->dev, "error %d reading JEDEC ID\n", ret); 7951da8869aSAndrey Smirnov return ERR_PTR(ret); 7961da8869aSAndrey Smirnov } 7971da8869aSAndrey Smirnov 7981da8869aSAndrey Smirnov if (id[0] != CFI_MFR_ATMEL) 7991da8869aSAndrey Smirnov return NULL; 8001da8869aSAndrey Smirnov 8011da8869aSAndrey Smirnov jedec = be64_to_cpup((__be64 *)id); 8021da8869aSAndrey Smirnov 8031da8869aSAndrey Smirnov /* 8041da8869aSAndrey Smirnov * First, try to match device using extended device 8051da8869aSAndrey Smirnov * information 8061da8869aSAndrey Smirnov */ 8071da8869aSAndrey Smirnov info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_EXTID, true); 8081da8869aSAndrey Smirnov if (!IS_ERR(info)) 8091da8869aSAndrey Smirnov return info; 8101da8869aSAndrey Smirnov /* 8111da8869aSAndrey Smirnov * If that fails, make another pass using regular ID 8121da8869aSAndrey Smirnov * information 8131da8869aSAndrey Smirnov */ 8141da8869aSAndrey Smirnov info = jedec_lookup(spi, jedec >> DATAFLASH_SHIFT_ID, false); 8151da8869aSAndrey Smirnov if (!IS_ERR(info)) 8161da8869aSAndrey Smirnov return info; 817771999b6Sakpm@linux-foundation.org /* 818771999b6Sakpm@linux-foundation.org * Treat other chips as errors ... we won't know the right page 819771999b6Sakpm@linux-foundation.org * size (it might be binary) even when we can tell which density 820771999b6Sakpm@linux-foundation.org * class is involved (legacy chip id scheme). 821771999b6Sakpm@linux-foundation.org */ 8221da8869aSAndrey Smirnov dev_warn(&spi->dev, "JEDEC id %016llx not handled\n", jedec); 823771999b6Sakpm@linux-foundation.org return ERR_PTR(-ENODEV); 824771999b6Sakpm@linux-foundation.org } 825771999b6Sakpm@linux-foundation.org 826771999b6Sakpm@linux-foundation.org /* 827771999b6Sakpm@linux-foundation.org * Detect and initialize DataFlash device, using JEDEC IDs on newer chips 828771999b6Sakpm@linux-foundation.org * or else the ID code embedded in the status bits: 829771999b6Sakpm@linux-foundation.org * 830771999b6Sakpm@linux-foundation.org * Device Density ID code #Pages PageSize Offset 831771999b6Sakpm@linux-foundation.org * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9 832771999b6Sakpm@linux-foundation.org * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9 833771999b6Sakpm@linux-foundation.org * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9 834771999b6Sakpm@linux-foundation.org * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9 835771999b6Sakpm@linux-foundation.org * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10 836771999b6Sakpm@linux-foundation.org * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10 837771999b6Sakpm@linux-foundation.org * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11 838771999b6Sakpm@linux-foundation.org * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 839771999b6Sakpm@linux-foundation.org */ 84006f25510SBill Pemberton static int dataflash_probe(struct spi_device *spi) 8411d6432feSDavid Brownell { 8421d6432feSDavid Brownell int status; 843e9d42227SMichael Hennerich struct flash_info *info; 844e9d42227SMichael Hennerich 845e9d42227SMichael Hennerich /* 846e9d42227SMichael Hennerich * Try to detect dataflash by JEDEC ID. 847e9d42227SMichael Hennerich * If it succeeds we know we have either a C or D part. 848e9d42227SMichael Hennerich * D will support power of 2 pagesize option. 84934a82443SDavid Brownell * Both support the security register, though with different 85034a82443SDavid Brownell * write procedures. 851e9d42227SMichael Hennerich */ 852e9d42227SMichael Hennerich info = jedec_probe(spi); 853771999b6Sakpm@linux-foundation.org if (IS_ERR(info)) 854771999b6Sakpm@linux-foundation.org return PTR_ERR(info); 855e9d42227SMichael Hennerich if (info != NULL) 85634a82443SDavid Brownell return add_dataflash_otp(spi, info->name, info->nr_pages, 85734a82443SDavid Brownell info->pagesize, info->pageoffset, 85834a82443SDavid Brownell (info->flags & SUP_POW2PS) ? 'd' : 'c'); 859e9d42227SMichael Hennerich 860771999b6Sakpm@linux-foundation.org /* 861771999b6Sakpm@linux-foundation.org * Older chips support only legacy commands, identifing 862771999b6Sakpm@linux-foundation.org * capacity using bits in the status byte. 863771999b6Sakpm@linux-foundation.org */ 8641d6432feSDavid Brownell status = dataflash_status(spi); 8651d6432feSDavid Brownell if (status <= 0 || status == 0xff) { 86602f62864SAndrey Smirnov dev_dbg(&spi->dev, "status error %d\n", status); 867de4fa992SDavid Brownell if (status == 0 || status == 0xff) 8681d6432feSDavid Brownell status = -ENODEV; 8691d6432feSDavid Brownell return status; 8701d6432feSDavid Brownell } 8711d6432feSDavid Brownell 8721d6432feSDavid Brownell /* if there's a device there, assume it's dataflash. 8731d6432feSDavid Brownell * board setup should have set spi->max_speed_max to 8741d6432feSDavid Brownell * match f(car) for continuous reads, mode 0 or 3. 8751d6432feSDavid Brownell */ 8761d6432feSDavid Brownell switch (status & 0x3c) { 8771d6432feSDavid Brownell case 0x0c: /* 0 0 1 1 x x */ 8781d6432feSDavid Brownell status = add_dataflash(spi, "AT45DB011B", 512, 264, 9); 8791d6432feSDavid Brownell break; 8801d6432feSDavid Brownell case 0x14: /* 0 1 0 1 x x */ 881e9d42227SMichael Hennerich status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9); 8821d6432feSDavid Brownell break; 8831d6432feSDavid Brownell case 0x1c: /* 0 1 1 1 x x */ 884771999b6Sakpm@linux-foundation.org status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9); 8851d6432feSDavid Brownell break; 8861d6432feSDavid Brownell case 0x24: /* 1 0 0 1 x x */ 8871d6432feSDavid Brownell status = add_dataflash(spi, "AT45DB081B", 4096, 264, 9); 8881d6432feSDavid Brownell break; 8891d6432feSDavid Brownell case 0x2c: /* 1 0 1 1 x x */ 890771999b6Sakpm@linux-foundation.org status = add_dataflash(spi, "AT45DB161x", 4096, 528, 10); 8911d6432feSDavid Brownell break; 8921d6432feSDavid Brownell case 0x34: /* 1 1 0 1 x x */ 8931d6432feSDavid Brownell status = add_dataflash(spi, "AT45DB321x", 8192, 528, 10); 8941d6432feSDavid Brownell break; 8951d6432feSDavid Brownell case 0x38: /* 1 1 1 x x x */ 8961d6432feSDavid Brownell case 0x3c: 8971d6432feSDavid Brownell status = add_dataflash(spi, "AT45DB642x", 8192, 1056, 11); 8981d6432feSDavid Brownell break; 8991d6432feSDavid Brownell /* obsolete AT45DB1282 not (yet?) supported */ 9001d6432feSDavid Brownell default: 901cb85b7e7SMark Brown dev_info(&spi->dev, "unsupported device (%x)\n", 9020a32a102SBrian Norris status & 0x3c); 9031d6432feSDavid Brownell status = -ENODEV; 9041d6432feSDavid Brownell } 9051d6432feSDavid Brownell 9061d6432feSDavid Brownell if (status < 0) 90702f62864SAndrey Smirnov dev_dbg(&spi->dev, "add_dataflash --> %d\n", status); 9081d6432feSDavid Brownell 9091d6432feSDavid Brownell return status; 9101d6432feSDavid Brownell } 9111d6432feSDavid Brownell 912810b7e06SBill Pemberton static int dataflash_remove(struct spi_device *spi) 9131d6432feSDavid Brownell { 9145cacbfa9SJingoo Han struct dataflash *flash = spi_get_drvdata(spi); 9151d6432feSDavid Brownell int status; 9161d6432feSDavid Brownell 91702f62864SAndrey Smirnov dev_dbg(&spi->dev, "remove\n"); 9181d6432feSDavid Brownell 919436c06daSJamie Iles status = mtd_device_unregister(&flash->mtd); 9201ba80c9eSJingoo Han if (status == 0) 9211d6432feSDavid Brownell kfree(flash); 9221d6432feSDavid Brownell return status; 9231d6432feSDavid Brownell } 9241d6432feSDavid Brownell 9251d6432feSDavid Brownell static struct spi_driver dataflash_driver = { 9261d6432feSDavid Brownell .driver = { 9271d6432feSDavid Brownell .name = "mtd_dataflash", 92819d69b86SSachin Kamat .of_match_table = of_match_ptr(dataflash_dt_ids), 9291d6432feSDavid Brownell }, 9301d6432feSDavid Brownell 9311d6432feSDavid Brownell .probe = dataflash_probe, 9325153b88cSBill Pemberton .remove = dataflash_remove, 9331d6432feSDavid Brownell 9341d6432feSDavid Brownell /* FIXME: investigate suspend and resume... */ 9351d6432feSDavid Brownell }; 9361d6432feSDavid Brownell 937c9d1b752SAxel Lin module_spi_driver(dataflash_driver); 9381d6432feSDavid Brownell 9391d6432feSDavid Brownell MODULE_LICENSE("GPL"); 9401d6432feSDavid Brownell MODULE_AUTHOR("Andrew Victor, David Brownell"); 9411d6432feSDavid Brownell MODULE_DESCRIPTION("MTD DataFlash driver"); 942e0626e38SAnton Vorontsov MODULE_ALIAS("spi:mtd_dataflash"); 943