Commit d66d3519 authored by Brian Norris's avatar Brian Norris

Merge branch 'spinor'

Addition of the spi-nor framework, plus updates to the ST SPI FSM
driver.
parents 6189cccb dc002f99
* Freescale Quad Serial Peripheral Interface(QuadSPI)
Required properties:
- compatible : Should be "fsl,vf610-qspi"
- reg : the first contains the register location and length,
the second contains the memory mapping address and length
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
- interrupts : Should contain the interrupt for the device
- clocks : The clocks needed by the QuadSPI controller
- clock-names : the name of the clocks
Optional properties:
- fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B.
Each bus can be connected with two NOR flashes.
Most of the time, each bus only has one NOR flash
connected, this is the default case.
But if there are two NOR flashes connected to the
bus, you should enable this property.
(Please check the board's schematic.)
Example:
qspi0: quadspi@40044000 {
compatible = "fsl,vf610-qspi";
reg = <0x40044000 0x1000>, <0x20000000 0x10000000>;
reg-names = "QuadSPI", "QuadSPI-memory";
interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_QSPI0_EN>,
<&clks VF610_CLK_QSPI0>;
clock-names = "qspi_en", "qspi";
flash0: s25fl128s@0 {
....
};
};
SPI NOR framework
============================================
Part I - Why do we need this framework?
---------------------------------------
SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus
controller operates agnostic of the specific device attached. However, some
controllers (such as Freescale's QuadSPI controller) cannot easily handle
arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
In particular, Freescale's QuadSPI controller must know the NOR commands to
find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
opcodes, addresses, or data payloads; a SPI controller simply knows to send or
receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under
which the controller driver is aware of the opcodes, addressing, and other
details of the SPI NOR protocol.
Part II - How does the framework work?
--------------------------------------
This framework just adds a new layer between the MTD and the SPI bus driver.
With this new layer, the SPI NOR controller driver does not depend on the
m25p80 code anymore.
Before this framework, the layer is like:
MTD
------------------------
m25p80
------------------------
SPI bus driver
------------------------
SPI NOR chip
After this framework, the layer is like:
MTD
------------------------
SPI NOR framework
------------------------
m25p80
------------------------
SPI bus driver
------------------------
SPI NOR chip
With the SPI NOR controller driver (Freescale QuadSPI), it looks like:
MTD
------------------------
SPI NOR framework
------------------------
fsl-quadSPI
------------------------
SPI NOR chip
Part III - How can drivers use the framework?
---------------------------------------------
The main API is spi_nor_scan(). Before you call the hook, a driver should
initialize the necessary fields for spi_nor{}. Please see
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to fsl-quadspi.c
when you want to write a new driver for a SPI NOR controller.
...@@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig" ...@@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig"
source "drivers/mtd/lpddr/Kconfig" source "drivers/mtd/lpddr/Kconfig"
source "drivers/mtd/spi-nor/Kconfig"
source "drivers/mtd/ubi/Kconfig" source "drivers/mtd/ubi/Kconfig"
endif # MTD endif # MTD
...@@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o ...@@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/ obj-$(CONFIG_MTD_UBI) += ubi/
...@@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP ...@@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP
config MTD_M25P80 config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI_MASTER depends on SPI_MASTER && MTD_SPI_NOR
help help
This enables access to most modern SPI flash chips, used for This enables access to most modern SPI flash chips, used for
program and data storage. Series supported include Atmel AT26DF, program and data storage. Series supported include Atmel AT26DF,
...@@ -212,7 +212,7 @@ config MTD_DOCG3 ...@@ -212,7 +212,7 @@ config MTD_DOCG3
config MTD_ST_SPI_FSM config MTD_ST_SPI_FSM
tristate "ST Microelectronics SPI FSM Serial Flash Controller" tristate "ST Microelectronics SPI FSM Serial Flash Controller"
depends on ARM || SH depends on ARCH_STI
help help
This provides an MTD device driver for the ST Microelectronics This provides an MTD device driver for the ST Microelectronics
SPI Fast Sequence Mode (FSM) Serial Flash Controller and support SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
......
...@@ -19,485 +19,98 @@ ...@@ -19,485 +19,98 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/math64.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/mod_devicetable.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/of_platform.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/flash.h> #include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
/* Flash opcodes. */
#define OPCODE_WREN 0x06 /* Write enable */
#define OPCODE_RDSR 0x05 /* Read status register */
#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */
#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
#define OPCODE_RDCR 0x35 /* Read configuration register */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */
#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */
#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */
#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */
/* Used for SST flashes only. */
#define OPCODE_BP 0x02 /* Byte program */
#define OPCODE_WRDI 0x04 /* Write disable */
#define OPCODE_AAI_WP 0xad /* Auto address increment word program */
/* Used for Macronix and Winbond flashes. */
#define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */
#define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */
/* Used for Spansion flashes only. */
#define OPCODE_BRWR 0x17 /* Bank register write */
/* Status Register bits. */
#define SR_WIP 1 /* Write in progress */
#define SR_WEL 2 /* Write enable latch */
/* meaning of other SR_* bits may differ between vendors */
#define SR_BP0 4 /* Block protect 0 */
#define SR_BP1 8 /* Block protect 1 */
#define SR_BP2 0x10 /* Block protect 2 */
#define SR_SRWD 0x80 /* SR write protect */
#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
/* Configuration Register bits. */
#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
/* Define max times to check status register before we give up. */
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
#define MAX_CMD_SIZE 6 #define MAX_CMD_SIZE 6
#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16)
/****************************************************************************/
enum read_type {
M25P80_NORMAL = 0,
M25P80_FAST,
M25P80_DUAL,
M25P80_QUAD,
};
struct m25p { struct m25p {
struct spi_device *spi; struct spi_device *spi;
struct mutex lock; struct spi_nor spi_nor;
struct mtd_info mtd; struct mtd_info mtd;
u16 page_size; u8 command[MAX_CMD_SIZE];
u16 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 program_opcode;
u8 *command;
enum read_type flash_read;
}; };
static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
return container_of(mtd, struct m25p, mtd);
}
/****************************************************************************/
/*
* Internal helper functions
*/
/*
* Read the status register, returning its value in the location
* Return the status register value.
* Returns negative if error occurred.
*/
static int read_sr(struct m25p *flash)
{
ssize_t retval;
u8 code = OPCODE_RDSR;
u8 val;
retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
if (retval < 0) {
dev_err(&flash->spi->dev, "error %d reading SR\n",
(int) retval);
return retval;
}
return val;
}
/*
* Read configuration register, returning its value in the
* location. Return the configuration register value.
* Returns negative if error occured.
*/
static int read_cr(struct m25p *flash)
{
u8 code = OPCODE_RDCR;
int ret;
u8 val;
ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
if (ret < 0) {
dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
return ret;
}
return val;
}
/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
static int write_sr(struct m25p *flash, u8 val)
{
flash->command[0] = OPCODE_WRSR;
flash->command[1] = val;
return spi_write(flash->spi, flash->command, 2);
}
/*
* Set write enable latch with Write Enable command.
* Returns negative if error occurred.
*/
static inline int write_enable(struct m25p *flash)
{
u8 code = OPCODE_WREN;
return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
}
/*
* Send write disble instruction to the chip.
*/
static inline int write_disable(struct m25p *flash)
{
u8 code = OPCODE_WRDI;
return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
}
/*
* Enable/disable 4-byte addressing mode.
*/
static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
{
int status;
bool need_wren = false;
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_ST: /* Micron, actually */
/* Some Micron need WREN command; all will accept it */
need_wren = true;
case CFI_MFR_MACRONIX:
case 0xEF /* winbond */:
if (need_wren)
write_enable(flash);
flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
status = spi_write(flash->spi, flash->command, 1);
if (need_wren)
write_disable(flash);
return status;
default:
/* Spansion style */
flash->command[0] = OPCODE_BRWR;
flash->command[1] = enable << 7;
return spi_write(flash->spi, flash->command, 2);
}
}
/*
* Service routine to read status register until ready, or timeout occurs.
* Returns non-zero if error.
*/
static int wait_till_ready(struct m25p *flash)
{
unsigned long deadline;
int sr;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
if ((sr = read_sr(flash)) < 0)
break;
else if (!(sr & SR_WIP))
return 0;
cond_resched();
} while (!time_after_eq(jiffies, deadline));
return 1;
}
/*
* Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the
* second byte will be written to the configuration register.
* Return negative if error occured.
*/
static int write_sr_cr(struct m25p *flash, u16 val)
{
flash->command[0] = OPCODE_WRSR;
flash->command[1] = val & 0xff;
flash->command[2] = (val >> 8);
return spi_write(flash->spi, flash->command, 3);
}
static int macronix_quad_enable(struct m25p *flash)
{
int ret, val;
u8 cmd[2];
cmd[0] = OPCODE_WRSR;
val = read_sr(flash);
cmd[1] = val | SR_QUAD_EN_MX;
write_enable(flash);
spi_write(flash->spi, &cmd, 2);
if (wait_till_ready(flash))
return 1;
ret = read_sr(flash);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
dev_err(&flash->spi->dev, "Macronix Quad bit not set\n");
return -EINVAL;
}
return 0;
}
static int spansion_quad_enable(struct m25p *flash)
{ {
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
int ret; int ret;
int quad_en = CR_QUAD_EN_SPAN << 8;
write_enable(flash); ret = spi_write_then_read(spi, &code, 1, val, len);
if (ret < 0)
dev_err(&spi->dev, "error %d reading %x\n", ret, code);
ret = write_sr_cr(flash, quad_en); return ret;
if (ret < 0) {
dev_err(&flash->spi->dev,
"error while writing configuration register\n");
return -EINVAL;
}
/* read back and check it */
ret = read_cr(flash);
if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
dev_err(&flash->spi->dev, "Spansion Quad bit not set\n");
return -EINVAL;
}
return 0;
}
static int set_quad_mode(struct m25p *flash, u32 jedec_id)
{
int status;
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_MACRONIX:
status = macronix_quad_enable(flash);
if (status) {
dev_err(&flash->spi->dev,
"Macronix quad-read not enabled\n");
return -EINVAL;
}
return status;
default:
status = spansion_quad_enable(flash);
if (status) {
dev_err(&flash->spi->dev,
"Spansion quad-read not enabled\n");
return -EINVAL;
}
return status;
}
}
/*
* Erase the whole flash memory
*
* Returns 0 if successful, non-zero otherwise.
*/
static int erase_chip(struct m25p *flash)
{
pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__,
(long long)(flash->mtd.size >> 10));
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
return 1;
/* Send write enable, then erase commands. */
write_enable(flash);
/* Set up command buffer. */
flash->command[0] = OPCODE_CHIP_ERASE;
spi_write(flash->spi, flash->command, 1);
return 0;
} }
static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
{ {
/* opcode is in cmd[0] */ /* opcode is in cmd[0] */
cmd[1] = addr >> (flash->addr_width * 8 - 8); cmd[1] = addr >> (nor->addr_width * 8 - 8);
cmd[2] = addr >> (flash->addr_width * 8 - 16); cmd[2] = addr >> (nor->addr_width * 8 - 16);
cmd[3] = addr >> (flash->addr_width * 8 - 24); cmd[3] = addr >> (nor->addr_width * 8 - 24);
cmd[4] = addr >> (flash->addr_width * 8 - 32); cmd[4] = addr >> (nor->addr_width * 8 - 32);
} }
static int m25p_cmdsz(struct m25p *flash) static int m25p_cmdsz(struct spi_nor *nor)
{ {
return 1 + flash->addr_width; return 1 + nor->addr_width;
} }
/* static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
* Erase one sector of flash memory at offset ``offset'' which is any int wr_en)
* address within the sector which should be erased.
*
* Returns 0 if successful, non-zero otherwise.
*/
static int erase_sector(struct m25p *flash, u32 offset)
{ {
pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev), struct m25p *flash = nor->priv;
__func__, flash->mtd.erasesize / 1024, offset); struct spi_device *spi = flash->spi;
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
return 1;
/* Send write enable, then erase commands. */
write_enable(flash);
/* Set up command buffer. */ flash->command[0] = opcode;
flash->command[0] = flash->erase_opcode; if (buf)
m25p_addr2cmd(flash, offset, flash->command); memcpy(&flash->command[1], buf, len);
spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
return 0; return spi_write(spi, flash->command, len + 1);
} }
/****************************************************************************/ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
/*
* MTD implementation
*/
/*
* Erase an address range on the flash chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
struct m25p *flash = mtd_to_m25p(mtd); struct m25p *flash = nor->priv;
u32 addr,len; struct spi_device *spi = flash->spi;
uint32_t rem; struct spi_transfer t[2] = {};
struct spi_message m;
pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev), int cmd_sz = m25p_cmdsz(nor);
__func__, (long long)instr->addr,
(long long)instr->len);
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
addr = instr->addr;
len = instr->len;
mutex_lock(&flash->lock);
/* whole-chip erase? */
if (len == flash->mtd.size) {
if (erase_chip(flash)) {
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return -EIO;
}
/* REVISIT in some cases we could speed up erasing large regions spi_message_init(&m);
* by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up
* to use "small sector erase", but that's not always optimal.
*/
/* "sector"-at-a-time erase */ if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
} else { cmd_sz = 1;
while (len) {
if (erase_sector(flash, addr)) {
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return -EIO;
}
addr += mtd->erasesize; flash->command[0] = nor->program_opcode;
len -= mtd->erasesize; m25p_addr2cmd(nor, to, flash->command);
}
}
mutex_unlock(&flash->lock); t[0].tx_buf = flash->command;
t[0].len = cmd_sz;
spi_message_add_tail(&t[0], &m);
instr->state = MTD_ERASE_DONE; t[1].tx_buf = buf;
mtd_erase_callback(instr); t[1].len = len;
spi_message_add_tail(&t[1], &m);
return 0; spi_sync(spi, &m);
}
/* *retlen += m.actual_length - cmd_sz;
* Dummy Cycle calculation for different type of read.
* It can be used to support more commands with
* different dummy cycle requirements.
*/
static inline int m25p80_dummy_cycles_read(struct m25p *flash)
{
switch (flash->flash_read) {
case M25P80_FAST:
case M25P80_DUAL:
case M25P80_QUAD:
return 1;
case M25P80_NORMAL:
return 0;
default:
dev_err(&flash->spi->dev, "No valid read type supported\n");
return -1;
}
} }
static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
{ {
switch (flash->flash_read) { switch (nor->flash_read) {
case M25P80_DUAL: case SPI_NOR_DUAL:
return 2; return 2;
case M25P80_QUAD: case SPI_NOR_QUAD:
return 4; return 4;
default: default:
return 0; return 0;
...@@ -505,590 +118,72 @@ static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) ...@@ -505,590 +118,72 @@ static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
} }
/* /*
* Read an address range from the flash chip. The address range * Read an address range from the nor chip. The address range
* may be any size provided it is within the physical boundaries. * may be any size provided it is within the physical boundaries.
*/ */
static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
struct m25p *flash = mtd_to_m25p(mtd); struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
struct spi_transfer t[2]; struct spi_transfer t[2];
struct spi_message m; struct spi_message m;
uint8_t opcode; int dummy = nor->read_dummy;
int dummy; int ret;
pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), /* Wait till previous write/erase is done. */
__func__, (u32)from, len); ret = nor->wait_till_ready(nor);
if (ret)
return ret;
spi_message_init(&m); spi_message_init(&m);
memset(t, 0, (sizeof t)); memset(t, 0, (sizeof t));
dummy = m25p80_dummy_cycles_read(flash); flash->command[0] = nor->read_opcode;
if (dummy < 0) { m25p_addr2cmd(nor, from, flash->command);
dev_err(&flash->spi->dev, "No valid read command supported\n");
return -EINVAL;
}
t[0].tx_buf = flash->command; t[0].tx_buf = flash->command;
t[0].len = m25p_cmdsz(flash) + dummy; t[0].len = m25p_cmdsz(nor) + dummy;
spi_message_add_tail(&t[0], &m); spi_message_add_tail(&t[0], &m);
t[1].rx_buf = buf; t[1].rx_buf = buf;
t[1].rx_nbits = m25p80_rx_nbits(flash); t[1].rx_nbits = m25p80_rx_nbits(nor);
t[1].len = len; t[1].len = len;
spi_message_add_tail(&t[1], &m); spi_message_add_tail(&t[1], &m);
mutex_lock(&flash->lock); spi_sync(spi, &m);
/* Wait till previous write/erase is done. */
if (wait_till_ready(flash)) {
/* REVISIT status return?? */
mutex_unlock(&flash->lock);
return 1;
}
/* Set up the write data buffer. */
opcode = flash->read_opcode;
flash->command[0] = opcode;
m25p_addr2cmd(flash, from, flash->command);
spi_sync(flash->spi, &m);
*retlen = m.actual_length - m25p_cmdsz(flash) - dummy;
mutex_unlock(&flash->lock);
return 0;
}
/*
* Write an address range to the flash chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
* it is within the physical boundaries.
*/
static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct m25p *flash = mtd_to_m25p(mtd);
u32 page_offset, page_size;
struct spi_transfer t[2];
struct spi_message m;
pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
__func__, (u32)to, len);
spi_message_init(&m);
memset(t, 0, (sizeof t));
t[0].tx_buf = flash->command;
t[0].len = m25p_cmdsz(flash);
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
spi_message_add_tail(&t[1], &m);
mutex_lock(&flash->lock);
/* Wait until finished previous write command. */
if (wait_till_ready(flash)) {
mutex_unlock(&flash->lock);
return 1;
}
write_enable(flash);
/* Set up the opcode in the write buffer. */
flash->command[0] = flash->program_opcode;
m25p_addr2cmd(flash, to, flash->command);
page_offset = to & (flash->page_size - 1);
/* do all the bytes fit onto one page? */
if (page_offset + len <= flash->page_size) {
t[1].len = len;
spi_sync(flash->spi, &m);
*retlen = m.actual_length - m25p_cmdsz(flash);
} else {
u32 i;
/* the size of data remaining on the first page */
page_size = flash->page_size - page_offset;
t[1].len = page_size;
spi_sync(flash->spi, &m);
*retlen = m.actual_length - m25p_cmdsz(flash);
/* write everything in flash->page_size chunks */
for (i = page_size; i < len; i += page_size) {
page_size = len - i;
if (page_size > flash->page_size)
page_size = flash->page_size;
/* write the next page to flash */
m25p_addr2cmd(flash, to + i, flash->command);
t[1].tx_buf = buf + i;
t[1].len = page_size;
wait_till_ready(flash);
write_enable(flash);
spi_sync(flash->spi, &m);
*retlen += m.actual_length - m25p_cmdsz(flash);
}
}
mutex_unlock(&flash->lock);
*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
return 0; return 0;
} }
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, static int m25p80_erase(struct spi_nor *nor, loff_t offset)
size_t *retlen, const u_char *buf)
{ {
struct m25p *flash = mtd_to_m25p(mtd); struct m25p *flash = nor->priv;
struct spi_transfer t[2]; int ret;
struct spi_message m;
size_t actual;
int cmd_sz, ret;
pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
__func__, (u32)to, len);
spi_message_init(&m);
memset(t, 0, (sizeof t));
t[0].tx_buf = flash->command;
t[0].len = m25p_cmdsz(flash);
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
spi_message_add_tail(&t[1], &m);
mutex_lock(&flash->lock); dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
flash->mtd.erasesize / 1024, (u32)offset);
/* Wait until finished previous write command. */ /* Wait until finished previous write command. */
ret = wait_till_ready(flash); ret = nor->wait_till_ready(nor);
if (ret)
goto time_out;
write_enable(flash);
actual = to % 2;
/* Start write from odd address. */
if (actual) {
flash->command[0] = OPCODE_BP;
m25p_addr2cmd(flash, to, flash->command);
/* write one byte. */
t[1].len = 1;
spi_sync(flash->spi, &m);
ret = wait_till_ready(flash);
if (ret)
goto time_out;
*retlen += m.actual_length - m25p_cmdsz(flash);
}
to += actual;
flash->command[0] = OPCODE_AAI_WP;
m25p_addr2cmd(flash, to, flash->command);
/* Write out most of the data here. */
cmd_sz = m25p_cmdsz(flash);
for (; actual < len - 1; actual += 2) {
t[0].len = cmd_sz;
/* write two bytes. */
t[1].len = 2;
t[1].tx_buf = buf + actual;
spi_sync(flash->spi, &m);
ret = wait_till_ready(flash);
if (ret)
goto time_out;
*retlen += m.actual_length - cmd_sz;
cmd_sz = 1;
to += 2;
}
write_disable(flash);
ret = wait_till_ready(flash);
if (ret) if (ret)
goto time_out; return ret;
/* Write out trailing byte if it exists. */
if (actual != len) {
write_enable(flash);
flash->command[0] = OPCODE_BP;
m25p_addr2cmd(flash, to, flash->command);
t[0].len = m25p_cmdsz(flash);
t[1].len = 1;
t[1].tx_buf = buf + actual;
spi_sync(flash->spi, &m); /* Send write enable, then erase commands. */
ret = wait_till_ready(flash); ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
if (ret) if (ret)
goto time_out;
*retlen += m.actual_length - m25p_cmdsz(flash);
write_disable(flash);
}
time_out:
mutex_unlock(&flash->lock);
return ret; return ret;
}
static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct m25p *flash = mtd_to_m25p(mtd);
uint32_t offset = ofs;
uint8_t status_old, status_new;
int res = 0;
mutex_lock(&flash->lock);
/* Wait until finished previous command */
if (wait_till_ready(flash)) {
res = 1;
goto err;
}
status_old = read_sr(flash);
if (offset < flash->mtd.size-(flash->mtd.size/2))
status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
else if (offset < flash->mtd.size-(flash->mtd.size/4))
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
else if (offset < flash->mtd.size-(flash->mtd.size/8))
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
else if (offset < flash->mtd.size-(flash->mtd.size/16))
status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
else if (offset < flash->mtd.size-(flash->mtd.size/32))
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
else if (offset < flash->mtd.size-(flash->mtd.size/64))
status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
else
status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
/* Only modify protection if it will not unlock other areas */
if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) >
(status_old&(SR_BP2|SR_BP1|SR_BP0))) {
write_enable(flash);
if (write_sr(flash, status_new) < 0) {
res = 1;
goto err;
}
}
err: mutex_unlock(&flash->lock);
return res;
}
static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct m25p *flash = mtd_to_m25p(mtd);
uint32_t offset = ofs;
uint8_t status_old, status_new;
int res = 0;
mutex_lock(&flash->lock);
/* Wait until finished previous command */
if (wait_till_ready(flash)) {
res = 1;
goto err;
}
status_old = read_sr(flash);
if (offset+len > flash->mtd.size-(flash->mtd.size/64))
status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0);
else if (offset+len > flash->mtd.size-(flash->mtd.size/32))
status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
else if (offset+len > flash->mtd.size-(flash->mtd.size/16))
status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
else if (offset+len > flash->mtd.size-(flash->mtd.size/8))
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
else if (offset+len > flash->mtd.size-(flash->mtd.size/4))
status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
else if (offset+len > flash->mtd.size-(flash->mtd.size/2))
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
else
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
/* Only modify protection if it will not lock other areas */
if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) <
(status_old&(SR_BP2|SR_BP1|SR_BP0))) {
write_enable(flash);
if (write_sr(flash, status_new) < 0) {
res = 1;
goto err;
}
}
err: mutex_unlock(&flash->lock);
return res;
}
/****************************************************************************/
/*
* SPI device driver setup and teardown
*/
struct flash_info {
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
* a high byte of zero plus three data bytes: the manufacturer id,
* then a two byte device id.
*/
u32 jedec_id;
u16 ext_id;
/* The size listed here is what works with OPCODE_SE, which isn't
* necessarily called a "sector" by the vendor.
*/
unsigned sector_size;
u16 n_sectors;
u16 page_size;
u16 addr_width;
u16 flags;
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
#define M25P_NO_ERASE 0x02 /* No erase command needed */
#define SST_WRITE 0x04 /* use SST byte programming */
#define M25P_NO_FR 0x08 /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */
#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */
#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.jedec_id = (_jedec_id), \
.ext_id = (_ext_id), \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags), \
})
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = (_page_size), \
.addr_width = (_addr_width), \
.flags = (_flags), \
})
/* NOTE: double check command sets and memory organization when you add
* more flash chips. This current list focusses on newer chips, which
* have been converging on command sets which including JEDEC ID.
*/
static const struct spi_device_id m25p_ids[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
{ "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
/* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
/* ESMT */
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
/* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) },
/* GigaDevice */
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
/* Intel/Numonyx -- xxxs33b */
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
/* Macronix */
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
{ "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) },
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) },
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) },
/* Micron */
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
/* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
{ "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) },
{ "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) },
/* Spansion -- single (large) sector size only, at least /* Set up command buffer. */
* for the chips listed here (without boot sectors). flash->command[0] = nor->erase_opcode;
*/ m25p_addr2cmd(nor, offset, flash->command);
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) },
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) },
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
{ "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) },
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
/* ST Microelectronics -- newer production may have feature updates */
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
{ "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) },
{ "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) },
{ "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) },
{ "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) },
{ "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) },
{ "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
{ "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
{ "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) },
{ "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) },
{ "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) },
{ "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) },
{ "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) },
{ "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) },
{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) },
{ "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) },
{ "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) },
{ "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) },
{ "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) },
{ "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
{ "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) },
{ "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) },
{ "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) },
{ "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) },
{ "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) },
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
{ "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) },
{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) },
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
static const struct spi_device_id *jedec_probe(struct spi_device *spi)
{
int tmp;
u8 code = OPCODE_RDID;
u8 id[5];
u32 jedec;
u16 ext_jedec;
struct flash_info *info;
/* JEDEC also defines an optional "extended device information"
* string for after vendor-specific data, after the three bytes
* we use here. Supporting some chips might require using it.
*/
tmp = spi_write_then_read(spi, &code, 1, id, 5);
if (tmp < 0) {
pr_debug("%s: error %d reading JEDEC ID\n",
dev_name(&spi->dev), tmp);
return ERR_PTR(tmp);
}
jedec = id[0];
jedec = jedec << 8;
jedec |= id[1];
jedec = jedec << 8;
jedec |= id[2];
ext_jedec = id[3] << 8 | id[4]; spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { return 0;
info = (void *)m25p_ids[tmp].driver_data;
if (info->jedec_id == jedec) {
if (info->ext_id == 0 || info->ext_id == ext_jedec)
return &m25p_ids[tmp];
}
}
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
return ERR_PTR(-ENODEV);
} }
/* /*
* board specific setup should have ensured the SPI clock used here * board specific setup should have ensured the SPI clock used here
* matches what the READ command supports, at least until this driver * matches what the READ command supports, at least until this driver
...@@ -1096,231 +191,43 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi) ...@@ -1096,231 +191,43 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
*/ */
static int m25p_probe(struct spi_device *spi) static int m25p_probe(struct spi_device *spi)
{ {
const struct spi_device_id *id = spi_get_device_id(spi); struct mtd_part_parser_data ppdata;
struct flash_platform_data *data; struct flash_platform_data *data;
struct m25p *flash; struct m25p *flash;
struct flash_info *info; struct spi_nor *nor;
unsigned i; enum read_mode mode = SPI_NOR_NORMAL;
struct mtd_part_parser_data ppdata;
struct device_node *np = spi->dev.of_node;
int ret; int ret;
/* Platform data helps sort out which chip type we have, as
* well as how this board partitions it. If we don't have
* a chip ID, try the JEDEC id commands; they'll work for most
* newer chips, even if we don't recognize the particular chip.
*/
data = dev_get_platdata(&spi->dev);
if (data && data->type) {
const struct spi_device_id *plat_id;
for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
plat_id = &m25p_ids[i];
if (strcmp(data->type, plat_id->name))
continue;
break;
}
if (i < ARRAY_SIZE(m25p_ids) - 1)
id = plat_id;
else
dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
}
info = (void *)id->driver_data;
if (info->jedec_id) {
const struct spi_device_id *jid;
jid = jedec_probe(spi);
if (IS_ERR(jid)) {
return PTR_ERR(jid);
} else if (jid != id) {
/*
* JEDEC knows better, so overwrite platform ID. We
* can't trust partitions any longer, but we'll let
* mtd apply them anyway, since some partitions may be
* marked read-only, and we don't want to lose that
* information, even if it's not 100% accurate.
*/
dev_warn(&spi->dev, "found %s, expected %s\n",
jid->name, id->name);
id = jid;
info = (void *)jid->driver_data;
}
}
flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
if (!flash) if (!flash)
return -ENOMEM; return -ENOMEM;
flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL); nor = &flash->spi_nor;
if (!flash->command)
return -ENOMEM;
flash->spi = spi;
mutex_init(&flash->lock);
spi_set_drvdata(spi, flash);
/*
* Atmel, SST and Intel/Numonyx serial flash tend to power
* up with the software protection bits set
*/
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
write_enable(flash);
write_sr(flash, 0);
}
if (data && data->name)
flash->mtd.name = data->name;
else
flash->mtd.name = dev_name(&spi->dev);
flash->mtd.type = MTD_NORFLASH;
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.size = info->sector_size * info->n_sectors;
flash->mtd._erase = m25p80_erase;
flash->mtd._read = m25p80_read;
/* flash protection support for STmicro chips */ /* install the hooks */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { nor->read = m25p80_read;
flash->mtd._lock = m25p80_lock; nor->write = m25p80_write;
flash->mtd._unlock = m25p80_unlock; nor->erase = m25p80_erase;
} nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
/* sst flash chips use AAI word program */
if (info->flags & SST_WRITE)
flash->mtd._write = sst_write;
else
flash->mtd._write = m25p80_write;
/* prefer "small sector" erase if possible */ nor->dev = &spi->dev;
if (info->flags & SECT_4K) { nor->mtd = &flash->mtd;
flash->erase_opcode = OPCODE_BE_4K; nor->priv = flash;
flash->mtd.erasesize = 4096;
} else if (info->flags & SECT_4K_PMC) {
flash->erase_opcode = OPCODE_BE_4K_PMC;
flash->mtd.erasesize = 4096;
} else {
flash->erase_opcode = OPCODE_SE;
flash->mtd.erasesize = info->sector_size;
}
if (info->flags & M25P_NO_ERASE) spi_set_drvdata(spi, flash);
flash->mtd.flags |= MTD_NO_ERASE; flash->mtd.priv = nor;
flash->spi = spi;
ppdata.of_node = spi->dev.of_node;
flash->mtd.dev.parent = &spi->dev;
flash->page_size = info->page_size;
flash->mtd.writebufsize = flash->page_size;
if (np) {
/* If we were instantiated by DT, use it */
if (of_property_read_bool(np, "m25p,fast-read"))
flash->flash_read = M25P80_FAST;
else
flash->flash_read = M25P80_NORMAL;
} else {
/* If we weren't instantiated by DT, default to fast-read */
flash->flash_read = M25P80_FAST;
}
/* Some devices cannot do fast-read, no matter what DT tells us */
if (info->flags & M25P_NO_FR)
flash->flash_read = M25P80_NORMAL;
/* Quad/Dual-read mode takes precedence over fast/normal */ if (spi->mode & SPI_RX_QUAD)
if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) { mode = SPI_NOR_QUAD;
ret = set_quad_mode(flash, info->jedec_id); ret = spi_nor_scan(nor, spi_get_device_id(spi), mode);
if (ret) { if (ret)
dev_err(&flash->spi->dev, "quad mode not supported\n");
return ret; return ret;
}
flash->flash_read = M25P80_QUAD;
} else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) {
flash->flash_read = M25P80_DUAL;
}
/* Default commands */
switch (flash->flash_read) {
case M25P80_QUAD:
flash->read_opcode = OPCODE_QUAD_READ;
break;
case M25P80_DUAL:
flash->read_opcode = OPCODE_DUAL_READ;
break;
case M25P80_FAST:
flash->read_opcode = OPCODE_FAST_READ;
break;
case M25P80_NORMAL:
flash->read_opcode = OPCODE_NORM_READ;
break;
default:
dev_err(&flash->spi->dev, "No Read opcode defined\n");
return -EINVAL;
}
flash->program_opcode = OPCODE_PP;
if (info->addr_width)
flash->addr_width = info->addr_width;
else if (flash->mtd.size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
flash->addr_width = 4;
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
/* Dedicated 4-byte command set */
switch (flash->flash_read) {
case M25P80_QUAD:
flash->read_opcode = OPCODE_QUAD_READ_4B;
break;
case M25P80_DUAL:
flash->read_opcode = OPCODE_DUAL_READ_4B;
break;
case M25P80_FAST:
flash->read_opcode = OPCODE_FAST_READ_4B;
break;
case M25P80_NORMAL:
flash->read_opcode = OPCODE_NORM_READ_4B;
break;
}
flash->program_opcode = OPCODE_PP_4B;
/* No small sector erase for 4-byte command set */
flash->erase_opcode = OPCODE_SE_4B;
flash->mtd.erasesize = info->sector_size;
} else
set_4byte(flash, info->jedec_id, 1);
} else {
flash->addr_width = 3;
}
dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
(long long)flash->mtd.size >> 10);
pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) "
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
flash->mtd.name,
(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
flash->mtd.erasesize, flash->mtd.erasesize / 1024,
flash->mtd.numeraseregions);
if (flash->mtd.numeraseregions)
for (i = 0; i < flash->mtd.numeraseregions; i++)
pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, "
".erasesize = 0x%.8x (%uKiB), "
".numblocks = %d }\n",
i, (long long)flash->mtd.eraseregions[i].offset,
flash->mtd.eraseregions[i].erasesize,
flash->mtd.eraseregions[i].erasesize / 1024,
flash->mtd.eraseregions[i].numblocks);
data = dev_get_platdata(&spi->dev);
ppdata.of_node = spi->dev.of_node;
/* partitions should match sector boundaries; and it may be good to
* use readonly partitions for writeprotected sectors (BP2..BP0).
*/
return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
data ? data->parts : NULL, data ? data->parts : NULL,
data ? data->nr_parts : 0); data ? data->nr_parts : 0);
...@@ -1341,7 +248,7 @@ static struct spi_driver m25p80_driver = { ...@@ -1341,7 +248,7 @@ static struct spi_driver m25p80_driver = {
.name = "m25p80", .name = "m25p80",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.id_table = m25p_ids, .id_table = spi_nor_ids,
.probe = m25p_probe, .probe = m25p_probe,
.remove = m25p_remove, .remove = m25p_remove,
......
...@@ -13,43 +13,23 @@ ...@@ -13,43 +13,23 @@
#define _MTD_SERIAL_FLASH_CMDS_H #define _MTD_SERIAL_FLASH_CMDS_H
/* Generic Flash Commands/OPCODEs */ /* Generic Flash Commands/OPCODEs */
#define FLASH_CMD_WREN 0x06 #define SPINOR_OP_RDSR2 0x35
#define FLASH_CMD_WRDI 0x04 #define SPINOR_OP_WRVCR 0x81
#define FLASH_CMD_RDID 0x9f #define SPINOR_OP_RDVCR 0x85
#define FLASH_CMD_RDSR 0x05
#define FLASH_CMD_RDSR2 0x35
#define FLASH_CMD_WRSR 0x01
#define FLASH_CMD_SE_4K 0x20
#define FLASH_CMD_SE_32K 0x52
#define FLASH_CMD_SE 0xd8
#define FLASH_CMD_CHIPERASE 0xc7
#define FLASH_CMD_WRVCR 0x81
#define FLASH_CMD_RDVCR 0x85
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
#define FLASH_CMD_READ 0x03 /* READ */ #define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */ #define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */ #define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ #define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ #define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
/* READ commands with 32-bit addressing */ /* READ commands with 32-bit addressing */
#define FLASH_CMD_READ4 0x13 #define SPINOR_OP_READ4_1_2_2 0xbc
#define FLASH_CMD_READ4_FAST 0x0c #define SPINOR_OP_READ4_1_4_4 0xec
#define FLASH_CMD_READ4_1_1_2 0x3c
#define FLASH_CMD_READ4_1_2_2 0xbc
#define FLASH_CMD_READ4_1_1_4 0x6c
#define FLASH_CMD_READ4_1_4_4 0xec
/* Configuration flags */ /* Configuration flags */
#define FLASH_FLAG_SINGLE 0x000000ff #define FLASH_FLAG_SINGLE 0x000000ff
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -201,44 +202,6 @@ ...@@ -201,44 +202,6 @@
#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */ #define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */
/* Flash Commands */
#define FLASH_CMD_WREN 0x06
#define FLASH_CMD_WRDI 0x04
#define FLASH_CMD_RDID 0x9f
#define FLASH_CMD_RDSR 0x05
#define FLASH_CMD_RDSR2 0x35
#define FLASH_CMD_WRSR 0x01
#define FLASH_CMD_SE_4K 0x20
#define FLASH_CMD_SE_32K 0x52
#define FLASH_CMD_SE 0xd8
#define FLASH_CMD_CHIPERASE 0xc7
#define FLASH_CMD_WRVCR 0x81
#define FLASH_CMD_RDVCR 0x85
#define FLASH_CMD_READ 0x03 /* READ */
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
#define FLASH_CMD_READ4 0x13
#define FLASH_CMD_READ4_FAST 0x0c
#define FLASH_CMD_READ4_1_1_2 0x3c
#define FLASH_CMD_READ4_1_2_2 0xbc
#define FLASH_CMD_READ4_1_1_4 0x6c
#define FLASH_CMD_READ4_1_4_4 0xec
/* S25FLxxxS commands */ /* S25FLxxxS commands */
#define S25FL_CMD_WRITE4_1_1_4 0x34 #define S25FL_CMD_WRITE4_1_1_4 0x34
#define S25FL_CMD_SE4 0xdc #define S25FL_CMD_SE4 0xdc
...@@ -246,7 +209,7 @@ ...@@ -246,7 +209,7 @@
#define S25FL_CMD_DYBWR 0xe1 #define S25FL_CMD_DYBWR 0xe1
#define S25FL_CMD_DYBRD 0xe0 #define S25FL_CMD_DYBRD 0xe0
#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with #define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with
* 'FLASH_CMD_WRITE_1_4_4' * 'SPINOR_OP_WRITE_1_4_4'
* as found on N25Qxxx devices! */ * as found on N25Qxxx devices! */
/* Status register */ /* Status register */
...@@ -261,6 +224,12 @@ ...@@ -261,6 +224,12 @@
#define S25FL_STATUS_E_ERR 0x20 #define S25FL_STATUS_E_ERR 0x20
#define S25FL_STATUS_P_ERR 0x40 #define S25FL_STATUS_P_ERR 0x40
#define N25Q_CMD_WRVCR 0x81
#define N25Q_CMD_RDVCR 0x85
#define N25Q_CMD_RDVECR 0x65
#define N25Q_CMD_RDNVCR 0xb5
#define N25Q_CMD_WRNVCR 0xb1
#define FLASH_PAGESIZE 256 /* In Bytes */ #define FLASH_PAGESIZE 256 /* In Bytes */
#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */ #define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */ #define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
...@@ -270,7 +239,6 @@ ...@@ -270,7 +239,6 @@
*/ */
#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001 #define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002 #define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004
#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008 #define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010 #define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
...@@ -329,7 +297,7 @@ struct flash_info { ...@@ -329,7 +297,7 @@ struct flash_info {
u32 jedec_id; u32 jedec_id;
u16 ext_id; u16 ext_id;
/* /*
* The size listed here is what works with FLASH_CMD_SE, which isn't * The size listed here is what works with SPINOR_OP_SE, which isn't
* necessarily called a "sector" by the vendor. * necessarily called a "sector" by the vendor.
*/ */
unsigned sector_size; unsigned sector_size;
...@@ -369,17 +337,26 @@ static struct flash_info flash_types[] = { ...@@ -369,17 +337,26 @@ static struct flash_info flash_types[] = {
{ "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL }, { "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
{ "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL }, { "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL },
/* Macronix MX25xxx
* - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices
* where operating frequency must be reduced.
*/
#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \ #define MX25_FLAG (FLASH_FLAG_READ_WRITE | \
FLASH_FLAG_READ_FAST | \ FLASH_FLAG_READ_FAST | \
FLASH_FLAG_READ_1_1_2 | \ FLASH_FLAG_READ_1_1_2 | \
FLASH_FLAG_READ_1_2_2 | \ FLASH_FLAG_READ_1_2_2 | \
FLASH_FLAG_READ_1_1_4 | \ FLASH_FLAG_READ_1_1_4 | \
FLASH_FLAG_READ_1_4_4 | \
FLASH_FLAG_SE_4K | \ FLASH_FLAG_SE_4K | \
FLASH_FLAG_SE_32K) FLASH_FLAG_SE_32K)
{ "mx25l3255e", 0xc29e16, 0, 64 * 1024, 64,
(MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86,
stfsm_mx25_config},
{ "mx25l25635e", 0xc22019, 0, 64*1024, 512, { "mx25l25635e", 0xc22019, 0, 64*1024, 512,
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
stfsm_mx25_config }, stfsm_mx25_config },
{ "mx25l25655e", 0xc22619, 0, 64*1024, 512,
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
stfsm_mx25_config},
#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \ #define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \
FLASH_FLAG_READ_FAST | \ FLASH_FLAG_READ_FAST | \
...@@ -407,6 +384,8 @@ static struct flash_info flash_types[] = { ...@@ -407,6 +384,8 @@ static struct flash_info flash_types[] = {
FLASH_FLAG_READ_1_4_4 | \ FLASH_FLAG_READ_1_4_4 | \
FLASH_FLAG_WRITE_1_1_4 | \ FLASH_FLAG_WRITE_1_1_4 | \
FLASH_FLAG_READ_FAST) FLASH_FLAG_READ_FAST)
{ "s25fl032p", 0x010215, 0x4d00, 64 * 1024, 64, S25FLXXXP_FLAG, 80,
stfsm_s25fl_config},
{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80, { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80,
stfsm_s25fl_config }, stfsm_s25fl_config },
{ "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80, { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80,
...@@ -473,22 +452,22 @@ static struct flash_info flash_types[] = { ...@@ -473,22 +452,22 @@ static struct flash_info flash_types[] = {
/* Default READ configurations, in order of preference */ /* Default READ configurations, in order of preference */
static struct seq_rw_config default_read_configs[] = { static struct seq_rw_config default_read_configs[] = {
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4}, {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0}, {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0}, {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8}, {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
/* Default WRITE configurations */ /* Default WRITE configurations */
static struct seq_rw_config default_write_configs[] = { static struct seq_rw_config default_write_configs[] = {
{FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0}, {FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
{FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0}, {FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
{FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0}, {FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
{FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0}, {FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
{FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_WRITE, 1, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
...@@ -511,12 +490,12 @@ static struct seq_rw_config default_write_configs[] = { ...@@ -511,12 +490,12 @@ static struct seq_rw_config default_write_configs[] = {
* cycles. * cycles.
*/ */
static struct seq_rw_config n25q_read3_configs[] = { static struct seq_rw_config n25q_read3_configs[] = {
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8}, {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
...@@ -526,12 +505,12 @@ static struct seq_rw_config n25q_read3_configs[] = { ...@@ -526,12 +505,12 @@ static struct seq_rw_config n25q_read3_configs[] = {
* - 'FAST' variants configured for 8 dummy cycles (see note above.) * - 'FAST' variants configured for 8 dummy cycles (see note above.)
*/ */
static struct seq_rw_config n25q_read4_configs[] = { static struct seq_rw_config n25q_read4_configs[] = {
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
...@@ -544,7 +523,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) ...@@ -544,7 +523,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
{ {
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) | SEQ_OPC_OPCODE(SPINOR_OP_EN4B) |
SEQ_OPC_CSDEASSERT); SEQ_OPC_CSDEASSERT);
seq->seq[0] = STFSM_INST_CMD1; seq->seq[0] = STFSM_INST_CMD1;
...@@ -572,12 +551,12 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) ...@@ -572,12 +551,12 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
* entering a state that is incompatible with the SPIBoot Controller. * entering a state that is incompatible with the SPIBoot Controller.
*/ */
static struct seq_rw_config stfsm_s25fl_read4_configs[] = { static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4}, {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0}, {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
...@@ -590,13 +569,13 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = { ...@@ -590,13 +569,13 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
/* /*
* [W25Qxxx] Configuration * [W25Qxxx] Configuration
*/ */
#define W25Q_STATUS_QE (0x1 << 9) #define W25Q_STATUS_QE (0x1 << 1)
static struct stfsm_seq stfsm_seq_read_jedec = { static struct stfsm_seq stfsm_seq_read_jedec = {
.data_size = TRANSFER_SIZE(8), .data_size = TRANSFER_SIZE(8),
.seq_opc[0] = (SEQ_OPC_PADS_1 | .seq_opc[0] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_RDID)), SEQ_OPC_OPCODE(SPINOR_OP_RDID)),
.seq = { .seq = {
STFSM_INST_CMD1, STFSM_INST_CMD1,
STFSM_INST_DATA_READ, STFSM_INST_DATA_READ,
...@@ -612,7 +591,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = { ...@@ -612,7 +591,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = {
.data_size = TRANSFER_SIZE(4), .data_size = TRANSFER_SIZE(4),
.seq_opc[0] = (SEQ_OPC_PADS_1 | .seq_opc[0] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_RDSR)), SEQ_OPC_OPCODE(SPINOR_OP_RDSR)),
.seq = { .seq = {
STFSM_INST_CMD1, STFSM_INST_CMD1,
STFSM_INST_DATA_READ, STFSM_INST_DATA_READ,
...@@ -628,10 +607,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = { ...@@ -628,10 +607,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
/* 'addr_cfg' configured during initialisation */ /* 'addr_cfg' configured during initialisation */
.seq_opc = { .seq_opc = {
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_SE)), SEQ_OPC_OPCODE(SPINOR_OP_SE)),
}, },
.seq = { .seq = {
STFSM_INST_CMD1, STFSM_INST_CMD1,
...@@ -649,10 +628,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = { ...@@ -649,10 +628,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
static struct stfsm_seq stfsm_seq_erase_chip = { static struct stfsm_seq stfsm_seq_erase_chip = {
.seq_opc = { .seq_opc = {
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT), SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT),
}, },
.seq = { .seq = {
STFSM_INST_CMD1, STFSM_INST_CMD1,
...@@ -669,26 +648,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = { ...@@ -669,26 +648,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = {
static struct stfsm_seq stfsm_seq_write_status = { static struct stfsm_seq stfsm_seq_write_status = {
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
.seq = {
STFSM_INST_CMD1,
STFSM_INST_CMD2,
STFSM_INST_STA_WR1,
STFSM_INST_STOP,
},
.seq_cfg = (SEQ_CFG_PADS_1 |
SEQ_CFG_READNOTWRITE |
SEQ_CFG_CSDEASSERT |
SEQ_CFG_STARTSEQ),
};
static struct stfsm_seq stfsm_seq_wrvcr = {
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)), SEQ_OPC_OPCODE(SPINOR_OP_WRSR)),
.seq = { .seq = {
STFSM_INST_CMD1, STFSM_INST_CMD1,
STFSM_INST_CMD2, STFSM_INST_CMD2,
...@@ -704,9 +666,9 @@ static struct stfsm_seq stfsm_seq_wrvcr = { ...@@ -704,9 +666,9 @@ static struct stfsm_seq stfsm_seq_wrvcr = {
static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
{ {
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR)); SEQ_OPC_OPCODE(SPINOR_OP_EN4B));
seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
SEQ_OPC_CSDEASSERT); SEQ_OPC_CSDEASSERT);
seq->seq[0] = STFSM_INST_CMD2; seq->seq[0] = STFSM_INST_CMD2;
...@@ -793,7 +755,7 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) ...@@ -793,7 +755,7 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size); dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
while (remaining) { while (remaining) {
for (;;) { for (;;) {
...@@ -817,7 +779,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, ...@@ -817,7 +779,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size); dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
...@@ -827,7 +789,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, ...@@ -827,7 +789,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
{ {
struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr; struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR; uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
...@@ -851,7 +813,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) ...@@ -851,7 +813,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
/* Use RDRS1 */ /* Use RDRS1 */
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_RDSR)); SEQ_OPC_OPCODE(SPINOR_OP_RDSR));
/* Load read_status sequence */ /* Load read_status sequence */
stfsm_load_seq(fsm, seq); stfsm_load_seq(fsm, seq);
...@@ -889,60 +851,57 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) ...@@ -889,60 +851,57 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
} }
static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd, static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
uint8_t *status) uint8_t *data, int bytes)
{ {
struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
uint32_t tmp; uint32_t tmp;
uint8_t *t = (uint8_t *)&tmp;
int i;
dev_dbg(fsm->dev, "reading STA[%s]\n", dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n",
(cmd == FLASH_CMD_RDSR) ? "1" : "2"); cmd, bytes);
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | BUG_ON(bytes != 1 && bytes != 2);
SEQ_OPC_CYCLES(8) |
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(cmd)), SEQ_OPC_OPCODE(cmd)),
stfsm_load_seq(fsm, seq); stfsm_load_seq(fsm, seq);
stfsm_read_fifo(fsm, &tmp, 4); stfsm_read_fifo(fsm, &tmp, 4);
*status = (uint8_t)(tmp >> 24); for (i = 0; i < bytes; i++)
data[i] = t[i];
stfsm_wait_seq(fsm); stfsm_wait_seq(fsm);
return 0; return 0;
} }
static int stfsm_write_status(struct stfsm *fsm, uint16_t status, static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd,
int sta_bytes) uint16_t data, int bytes, int wait_busy)
{ {
struct stfsm_seq *seq = &stfsm_seq_write_status; struct stfsm_seq *seq = &stfsm_seq_write_status;
dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n", dev_dbg(fsm->dev,
(sta_bytes == 1) ? "1" : "1+2", status); "write 'status' register [0x%02x], %d byte(s), 0x%04x\n"
" %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no");
seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT; BUG_ON(bytes != 1 && bytes != 2);
seq->seq[2] = (sta_bytes == 1) ?
STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
stfsm_load_seq(fsm, seq); seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(cmd));
stfsm_wait_seq(fsm);
return 0;
};
static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
{
struct stfsm_seq *seq = &stfsm_seq_wrvcr;
dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT); seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT;
seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
stfsm_load_seq(fsm, seq); stfsm_load_seq(fsm, seq);
stfsm_wait_seq(fsm); stfsm_wait_seq(fsm);
if (wait_busy)
stfsm_wait_busy(fsm);
return 0; return 0;
} }
...@@ -1027,7 +986,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm, ...@@ -1027,7 +986,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm,
if (cfg->write) if (cfg->write)
seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
SEQ_OPC_CSDEASSERT); SEQ_OPC_CSDEASSERT);
/* Address configuration (24 or 32-bit addresses) */ /* Address configuration (24 or 32-bit addresses) */
...@@ -1149,31 +1108,36 @@ static int stfsm_mx25_config(struct stfsm *fsm) ...@@ -1149,31 +1108,36 @@ static int stfsm_mx25_config(struct stfsm *fsm)
stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
soc_reset = stfsm_can_handle_soc_reset(fsm); soc_reset = stfsm_can_handle_soc_reset(fsm);
if (soc_reset || !fsm->booted_from_spi) { if (soc_reset || !fsm->booted_from_spi)
/* If we can handle SoC resets, we enable 32-bit address /* If we can handle SoC resets, we enable 32-bit address
* mode pervasively */ * mode pervasively */
stfsm_enter_32bit_addr(fsm, 1); stfsm_enter_32bit_addr(fsm, 1);
} else { else
/* Else, enable/disable 32-bit addressing before/after /* Else, enable/disable 32-bit addressing before/after
* each operation */ * each operation */
fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR | fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
CFG_WRITE_TOGGLE_32BIT_ADDR | CFG_WRITE_TOGGLE_32BIT_ADDR |
CFG_ERASESEC_TOGGLE_32BIT_ADDR); CFG_ERASESEC_TOGGLE_32BIT_ADDR);
/* It seems a small delay is required after exiting
* 32-bit mode following a write operation. The issue
* is under investigation.
*/
fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
}
} }
/* For QUAD mode, set 'QE' STATUS bit */ /* Check status of 'QE' bit, update if required. */
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1);
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
if (data_pads == 4) { if (data_pads == 4) {
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta); if (!(sta & MX25_STATUS_QE)) {
/* Set 'QE' */
sta |= MX25_STATUS_QE; sta |= MX25_STATUS_QE;
stfsm_write_status(fsm, sta, 1);
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
}
} else {
if (sta & MX25_STATUS_QE) {
/* Clear 'QE' */
sta &= ~MX25_STATUS_QE;
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
}
} }
return 0; return 0;
...@@ -1239,7 +1203,7 @@ static int stfsm_n25q_config(struct stfsm *fsm) ...@@ -1239,7 +1203,7 @@ static int stfsm_n25q_config(struct stfsm *fsm)
*/ */
vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED | vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
N25Q_VCR_WRAP_CONT); N25Q_VCR_WRAP_CONT);
stfsm_wrvcr(fsm, vcr); stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0);
return 0; return 0;
} }
...@@ -1297,7 +1261,7 @@ static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby) ...@@ -1297,7 +1261,7 @@ static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
{ {
struct stfsm_seq seq = { struct stfsm_seq seq = {
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
SEQ_OPC_CSDEASSERT), SEQ_OPC_CSDEASSERT),
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)), SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
...@@ -1337,7 +1301,7 @@ static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm) ...@@ -1337,7 +1301,7 @@ static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
SEQ_OPC_CSDEASSERT), SEQ_OPC_CSDEASSERT),
.seq_opc[1] = (SEQ_OPC_PADS_1 | .seq_opc[1] = (SEQ_OPC_PADS_1 |
SEQ_OPC_CYCLES(8) | SEQ_OPC_CYCLES(8) |
SEQ_OPC_OPCODE(FLASH_CMD_WRDI) | SEQ_OPC_OPCODE(SPINOR_OP_WRDI) |
SEQ_OPC_CSDEASSERT), SEQ_OPC_CSDEASSERT),
.seq = { .seq = {
STFSM_INST_CMD1, STFSM_INST_CMD1,
...@@ -1367,6 +1331,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm) ...@@ -1367,6 +1331,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
uint32_t offs; uint32_t offs;
uint16_t sta_wr; uint16_t sta_wr;
uint8_t sr1, cr1, dyb; uint8_t sr1, cr1, dyb;
int update_sr = 0;
int ret; int ret;
if (flags & FLASH_FLAG_32BIT_ADDR) { if (flags & FLASH_FLAG_32BIT_ADDR) {
...@@ -1414,34 +1379,28 @@ static int stfsm_s25fl_config(struct stfsm *fsm) ...@@ -1414,34 +1379,28 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
} }
} }
/* Check status of 'QE' bit */ /* Check status of 'QE' bit, update if required. */
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
if (data_pads == 4) { if (data_pads == 4) {
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) { if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
/* Set 'QE' */ /* Set 'QE' */
cr1 |= STFSM_S25FL_CONFIG_QE; cr1 |= STFSM_S25FL_CONFIG_QE;
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1); update_sr = 1;
sta_wr = ((uint16_t)cr1 << 8) | sr1;
stfsm_write_status(fsm, sta_wr, 2);
stfsm_wait_busy(fsm);
} }
} else { } else {
if ((cr1 & STFSM_S25FL_CONFIG_QE)) { if (cr1 & STFSM_S25FL_CONFIG_QE) {
/* Clear 'QE' */ /* Clear 'QE' */
cr1 &= ~STFSM_S25FL_CONFIG_QE; cr1 &= ~STFSM_S25FL_CONFIG_QE;
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1); update_sr = 1;
sta_wr = ((uint16_t)cr1 << 8) | sr1;
stfsm_write_status(fsm, sta_wr, 2);
stfsm_wait_busy(fsm);
} }
}
if (update_sr) {
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
sta_wr = ((uint16_t)cr1 << 8) | sr1;
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1);
} }
/* /*
...@@ -1456,27 +1415,36 @@ static int stfsm_s25fl_config(struct stfsm *fsm) ...@@ -1456,27 +1415,36 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
static int stfsm_w25q_config(struct stfsm *fsm) static int stfsm_w25q_config(struct stfsm *fsm)
{ {
uint32_t data_pads; uint32_t data_pads;
uint16_t sta_wr; uint8_t sr1, sr2;
uint8_t sta1, sta2; uint16_t sr_wr;
int update_sr = 0;
int ret; int ret;
ret = stfsm_prepare_rwe_seqs_default(fsm); ret = stfsm_prepare_rwe_seqs_default(fsm);
if (ret) if (ret)
return ret; return ret;
/* If using QUAD mode, set QE STATUS bit */ /* Check status of 'QE' bit, update if required. */
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
if (data_pads == 4) { if (data_pads == 4) {
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1); if (!(sr2 & W25Q_STATUS_QE)) {
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2); /* Set 'QE' */
sr2 |= W25Q_STATUS_QE;
sta_wr = ((uint16_t)sta2 << 8) | sta1; update_sr = 1;
}
sta_wr |= W25Q_STATUS_QE; } else {
if (sr2 & W25Q_STATUS_QE) {
stfsm_write_status(fsm, sta_wr, 2); /* Clear 'QE' */
sr2 &= ~W25Q_STATUS_QE;
stfsm_wait_busy(fsm); update_sr = 1;
}
}
if (update_sr) {
/* Write status register */
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
sr_wr = ((uint16_t)sr2 << 8) | sr1;
stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1);
} }
return 0; return 0;
...@@ -1506,7 +1474,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, ...@@ -1506,7 +1474,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
read_mask = (data_pads << 2) - 1; read_mask = (data_pads << 2) - 1;
/* Handle non-aligned buf */ /* Handle non-aligned buf */
p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf; p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
/* Handle non-aligned size */ /* Handle non-aligned size */
size_ub = (size + read_mask) & ~read_mask; size_ub = (size + read_mask) & ~read_mask;
...@@ -1528,7 +1496,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, ...@@ -1528,7 +1496,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
} }
/* Handle non-aligned buf */ /* Handle non-aligned buf */
if ((uint32_t)buf & 0x3) if ((uintptr_t)buf & 0x3)
memcpy(buf, page_buf, size); memcpy(buf, page_buf, size);
/* Wait for sequence to finish */ /* Wait for sequence to finish */
...@@ -1570,7 +1538,7 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, ...@@ -1570,7 +1538,7 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
write_mask = (data_pads << 2) - 1; write_mask = (data_pads << 2) - 1;
/* Handle non-aligned buf */ /* Handle non-aligned buf */
if ((uint32_t)buf & 0x3) { if ((uintptr_t)buf & 0x3) {
memcpy(page_buf, buf, size); memcpy(page_buf, buf, size);
p = (uint8_t *)page_buf; p = (uint8_t *)page_buf;
} else { } else {
...@@ -1628,11 +1596,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, ...@@ -1628,11 +1596,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
stfsm_s25fl_clear_status_reg(fsm); stfsm_s25fl_clear_status_reg(fsm);
/* Exit 32-bit address mode, if required */ /* Exit 32-bit address mode, if required */
if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) { if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
stfsm_enter_32bit_addr(fsm, 0); stfsm_enter_32bit_addr(fsm, 0);
if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
udelay(1);
}
return 0; return 0;
} }
...@@ -1736,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -1736,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
while (len) { while (len) {
/* Write up to page boundary */ /* Write up to page boundary */
bytes = min(FLASH_PAGESIZE - page_offs, len); bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len);
ret = stfsm_write(fsm, b, bytes, to); ret = stfsm_write(fsm, b, bytes, to);
if (ret) if (ret)
...@@ -1935,6 +1900,13 @@ static int stfsm_init(struct stfsm *fsm) ...@@ -1935,6 +1900,13 @@ static int stfsm_init(struct stfsm *fsm)
fsm->base + SPI_CONFIGDATA); fsm->base + SPI_CONFIGDATA);
writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG); writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
/*
* Set the FSM 'WAIT' delay to the minimum workable value. Note, for
* our purposes, the WAIT instruction is used purely to achieve
* "sequence validity" rather than actually implement a delay.
*/
writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME);
/* Clear FIFO, just in case */ /* Clear FIFO, just in case */
stfsm_clear_fifo(fsm); stfsm_clear_fifo(fsm);
......
menuconfig MTD_SPI_NOR
tristate "SPI-NOR device support"
depends on MTD
help
This is the framework for the SPI NOR which can be used by the SPI
device drivers and the SPI-NOR device driver.
if MTD_SPI_NOR
config SPI_FSL_QUADSPI
tristate "Freescale Quad SPI controller"
depends on ARCH_MXC
help
This enables support for the Quad SPI controller in master mode.
We only connect the NOR to this controller now.
endif # MTD_SPI_NOR
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
/*
* Freescale QuadSPI driver.
*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/completion.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
/* The registers */
#define QUADSPI_MCR 0x00
#define QUADSPI_MCR_RESERVED_SHIFT 16
#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT)
#define QUADSPI_MCR_MDIS_SHIFT 14
#define QUADSPI_MCR_MDIS_MASK (1 << QUADSPI_MCR_MDIS_SHIFT)
#define QUADSPI_MCR_CLR_TXF_SHIFT 11
#define QUADSPI_MCR_CLR_TXF_MASK (1 << QUADSPI_MCR_CLR_TXF_SHIFT)
#define QUADSPI_MCR_CLR_RXF_SHIFT 10
#define QUADSPI_MCR_CLR_RXF_MASK (1 << QUADSPI_MCR_CLR_RXF_SHIFT)
#define QUADSPI_MCR_DDR_EN_SHIFT 7
#define QUADSPI_MCR_DDR_EN_MASK (1 << QUADSPI_MCR_DDR_EN_SHIFT)
#define QUADSPI_MCR_END_CFG_SHIFT 2
#define QUADSPI_MCR_END_CFG_MASK (3 << QUADSPI_MCR_END_CFG_SHIFT)
#define QUADSPI_MCR_SWRSTHD_SHIFT 1
#define QUADSPI_MCR_SWRSTHD_MASK (1 << QUADSPI_MCR_SWRSTHD_SHIFT)
#define QUADSPI_MCR_SWRSTSD_SHIFT 0
#define QUADSPI_MCR_SWRSTSD_MASK (1 << QUADSPI_MCR_SWRSTSD_SHIFT)
#define QUADSPI_IPCR 0x08
#define QUADSPI_IPCR_SEQID_SHIFT 24
#define QUADSPI_IPCR_SEQID_MASK (0xF << QUADSPI_IPCR_SEQID_SHIFT)
#define QUADSPI_BUF0CR 0x10
#define QUADSPI_BUF1CR 0x14
#define QUADSPI_BUF2CR 0x18
#define QUADSPI_BUFXCR_INVALID_MSTRID 0xe
#define QUADSPI_BUF3CR 0x1c
#define QUADSPI_BUF3CR_ALLMST_SHIFT 31
#define QUADSPI_BUF3CR_ALLMST (1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
#define QUADSPI_BFGENCR 0x20
#define QUADSPI_BFGENCR_PAR_EN_SHIFT 16
#define QUADSPI_BFGENCR_PAR_EN_MASK (1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT))
#define QUADSPI_BFGENCR_SEQID_SHIFT 12
#define QUADSPI_BFGENCR_SEQID_MASK (0xF << QUADSPI_BFGENCR_SEQID_SHIFT)
#define QUADSPI_BUF0IND 0x30
#define QUADSPI_BUF1IND 0x34
#define QUADSPI_BUF2IND 0x38
#define QUADSPI_SFAR 0x100
#define QUADSPI_SMPR 0x108
#define QUADSPI_SMPR_DDRSMP_SHIFT 16
#define QUADSPI_SMPR_DDRSMP_MASK (7 << QUADSPI_SMPR_DDRSMP_SHIFT)
#define QUADSPI_SMPR_FSDLY_SHIFT 6
#define QUADSPI_SMPR_FSDLY_MASK (1 << QUADSPI_SMPR_FSDLY_SHIFT)
#define QUADSPI_SMPR_FSPHS_SHIFT 5
#define QUADSPI_SMPR_FSPHS_MASK (1 << QUADSPI_SMPR_FSPHS_SHIFT)
#define QUADSPI_SMPR_HSENA_SHIFT 0
#define QUADSPI_SMPR_HSENA_MASK (1 << QUADSPI_SMPR_HSENA_SHIFT)
#define QUADSPI_RBSR 0x10c
#define QUADSPI_RBSR_RDBFL_SHIFT 8
#define QUADSPI_RBSR_RDBFL_MASK (0x3F << QUADSPI_RBSR_RDBFL_SHIFT)
#define QUADSPI_RBCT 0x110
#define QUADSPI_RBCT_WMRK_MASK 0x1F
#define QUADSPI_RBCT_RXBRD_SHIFT 8
#define QUADSPI_RBCT_RXBRD_USEIPS (0x1 << QUADSPI_RBCT_RXBRD_SHIFT)
#define QUADSPI_TBSR 0x150
#define QUADSPI_TBDR 0x154
#define QUADSPI_SR 0x15c
#define QUADSPI_SR_IP_ACC_SHIFT 1
#define QUADSPI_SR_IP_ACC_MASK (0x1 << QUADSPI_SR_IP_ACC_SHIFT)
#define QUADSPI_SR_AHB_ACC_SHIFT 2
#define QUADSPI_SR_AHB_ACC_MASK (0x1 << QUADSPI_SR_AHB_ACC_SHIFT)
#define QUADSPI_FR 0x160
#define QUADSPI_FR_TFF_MASK 0x1
#define QUADSPI_SFA1AD 0x180
#define QUADSPI_SFA2AD 0x184
#define QUADSPI_SFB1AD 0x188
#define QUADSPI_SFB2AD 0x18c
#define QUADSPI_RBDR 0x200
#define QUADSPI_LUTKEY 0x300
#define QUADSPI_LUTKEY_VALUE 0x5AF05AF0
#define QUADSPI_LCKCR 0x304
#define QUADSPI_LCKER_LOCK 0x1
#define QUADSPI_LCKER_UNLOCK 0x2
#define QUADSPI_RSER 0x164
#define QUADSPI_RSER_TFIE (0x1 << 0)
#define QUADSPI_LUT_BASE 0x310
/*
* The definition of the LUT register shows below:
*
* ---------------------------------------------------
* | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
* ---------------------------------------------------
*/
#define OPRND0_SHIFT 0
#define PAD0_SHIFT 8
#define INSTR0_SHIFT 10
#define OPRND1_SHIFT 16
/* Instruction set for the LUT register. */
#define LUT_STOP 0
#define LUT_CMD 1
#define LUT_ADDR 2
#define LUT_DUMMY 3
#define LUT_MODE 4
#define LUT_MODE2 5
#define LUT_MODE4 6
#define LUT_READ 7
#define LUT_WRITE 8
#define LUT_JMP_ON_CS 9
#define LUT_ADDR_DDR 10
#define LUT_MODE_DDR 11
#define LUT_MODE2_DDR 12
#define LUT_MODE4_DDR 13
#define LUT_READ_DDR 14
#define LUT_WRITE_DDR 15
#define LUT_DATA_LEARN 16
/*
* The PAD definitions for LUT register.
*
* The pad stands for the lines number of IO[0:3].
* For example, the Quad read need four IO lines, so you should
* set LUT_PAD4 which means we use four IO lines.
*/
#define LUT_PAD1 0
#define LUT_PAD2 1
#define LUT_PAD4 2
/* Oprands for the LUT register. */
#define ADDR24BIT 0x18
#define ADDR32BIT 0x20
/* Macros for constructing the LUT register. */
#define LUT0(ins, pad, opr) \
(((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \
((LUT_##ins) << INSTR0_SHIFT))
#define LUT1(ins, pad, opr) (LUT0(ins, pad, opr) << OPRND1_SHIFT)
/* other macros for LUT register. */
#define QUADSPI_LUT(x) (QUADSPI_LUT_BASE + (x) * 4)
#define QUADSPI_LUT_NUM 64
/* SEQID -- we can have 16 seqids at most. */
#define SEQID_QUAD_READ 0
#define SEQID_WREN 1
#define SEQID_WRDI 2
#define SEQID_RDSR 3
#define SEQID_SE 4
#define SEQID_CHIP_ERASE 5
#define SEQID_PP 6
#define SEQID_RDID 7
#define SEQID_WRSR 8
#define SEQID_RDCR 9
#define SEQID_EN4B 10
#define SEQID_BRWR 11
enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID,
FSL_QUADSPI_IMX6SX,
};
struct fsl_qspi_devtype_data {
enum fsl_qspi_devtype devtype;
int rxfifo;
int txfifo;
};
static struct fsl_qspi_devtype_data vybrid_data = {
.devtype = FSL_QUADSPI_VYBRID,
.rxfifo = 128,
.txfifo = 64
};
static struct fsl_qspi_devtype_data imx6sx_data = {
.devtype = FSL_QUADSPI_IMX6SX,
.rxfifo = 128,
.txfifo = 512
};
#define FSL_QSPI_MAX_CHIP 4
struct fsl_qspi {
struct mtd_info mtd[FSL_QSPI_MAX_CHIP];
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
void __iomem *iobase;
void __iomem *ahb_base; /* Used when read from AHB bus */
u32 memmap_phy;
struct clk *clk, *clk_en;
struct device *dev;
struct completion c;
struct fsl_qspi_devtype_data *devtype_data;
u32 nor_size;
u32 nor_num;
u32 clk_rate;
unsigned int chip_base_addr; /* We may support two chips. */
};
static inline int is_vybrid_qspi(struct fsl_qspi *q)
{
return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
}
static inline int is_imx6sx_qspi(struct fsl_qspi *q)
{
return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX;
}
/*
* An IC bug makes us to re-arrange the 32-bit data.
* The following chips, such as IMX6SLX, have fixed this bug.
*/
static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
{
return is_vybrid_qspi(q) ? __swab32(a) : a;
}
static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q)
{
writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
}
static inline void fsl_qspi_lock_lut(struct fsl_qspi *q)
{
writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
}
static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
{
struct fsl_qspi *q = dev_id;
u32 reg;
/* clear interrupt */
reg = readl(q->iobase + QUADSPI_FR);
writel(reg, q->iobase + QUADSPI_FR);
if (reg & QUADSPI_FR_TFF_MASK)
complete(&q->c);
dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x:0x%.8x\n", q->chip_base_addr, reg);
return IRQ_HANDLED;
}
static void fsl_qspi_init_lut(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
int rxfifo = q->devtype_data->rxfifo;
u32 lut_base;
u8 cmd, addrlen, dummy;
int i;
fsl_qspi_unlock_lut(q);
/* Clear all the LUT table */
for (i = 0; i < QUADSPI_LUT_NUM; i++)
writel(0, base + QUADSPI_LUT_BASE + i * 4);
/* Quad Read */
lut_base = SEQID_QUAD_READ * 4;
if (q->nor_size <= SZ_16M) {
cmd = SPINOR_OP_READ_1_1_4;
addrlen = ADDR24BIT;
dummy = 8;
} else {
/* use the 4-byte address */
cmd = SPINOR_OP_READ_1_1_4;
addrlen = ADDR32BIT;
dummy = 8;
}
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo),
base + QUADSPI_LUT(lut_base + 1));
/* Write enable */
lut_base = SEQID_WREN * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base));
/* Page Program */
lut_base = SEQID_PP * 4;
if (q->nor_size <= SZ_16M) {
cmd = SPINOR_OP_PP;
addrlen = ADDR24BIT;
} else {
/* use the 4-byte address */
cmd = SPINOR_OP_PP;
addrlen = ADDR32BIT;
}
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
/* Read Status */
lut_base = SEQID_RDSR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(READ, PAD1, 0x1),
base + QUADSPI_LUT(lut_base));
/* Erase a sector */
lut_base = SEQID_SE * 4;
if (q->nor_size <= SZ_16M) {
cmd = SPINOR_OP_SE;
addrlen = ADDR24BIT;
} else {
/* use the 4-byte address */
cmd = SPINOR_OP_SE;
addrlen = ADDR32BIT;
}
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
/* Erase the whole chip */
lut_base = SEQID_CHIP_ERASE * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE),
base + QUADSPI_LUT(lut_base));
/* READ ID */
lut_base = SEQID_RDID * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(READ, PAD1, 0x8),
base + QUADSPI_LUT(lut_base));
/* Write Register */
lut_base = SEQID_WRSR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(WRITE, PAD1, 0x2),
base + QUADSPI_LUT(lut_base));
/* Read Configuration Register */
lut_base = SEQID_RDCR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(READ, PAD1, 0x1),
base + QUADSPI_LUT(lut_base));
/* Write disable */
lut_base = SEQID_WRDI * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + QUADSPI_LUT(lut_base));
/* Enter 4 Byte Mode (Micron) */
lut_base = SEQID_EN4B * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + QUADSPI_LUT(lut_base));
/* Enter 4 Byte Mode (Spansion) */
lut_base = SEQID_BRWR * 4;
writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + QUADSPI_LUT(lut_base));
fsl_qspi_lock_lut(q);
}
/* Get the SEQID for the command */
static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
{
switch (cmd) {
case SPINOR_OP_READ_1_1_4:
return SEQID_QUAD_READ;
case SPINOR_OP_WREN:
return SEQID_WREN;
case SPINOR_OP_WRDI:
return SEQID_WRDI;
case SPINOR_OP_RDSR:
return SEQID_RDSR;
case SPINOR_OP_SE:
return SEQID_SE;
case SPINOR_OP_CHIP_ERASE:
return SEQID_CHIP_ERASE;
case SPINOR_OP_PP:
return SEQID_PP;
case SPINOR_OP_RDID:
return SEQID_RDID;
case SPINOR_OP_WRSR:
return SEQID_WRSR;
case SPINOR_OP_RDCR:
return SEQID_RDCR;
case SPINOR_OP_EN4B:
return SEQID_EN4B;
case SPINOR_OP_BRWR:
return SEQID_BRWR;
default:
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
break;
}
return -EINVAL;
}
static int
fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
{
void __iomem *base = q->iobase;
int seqid;
u32 reg, reg2;
int err;
init_completion(&q->c);
dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n",
q->chip_base_addr, addr, len, cmd);
/* save the reg */
reg = readl(base + QUADSPI_MCR);
writel(q->memmap_phy + q->chip_base_addr + addr, base + QUADSPI_SFAR);
writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
base + QUADSPI_RBCT);
writel(reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR);
do {
reg2 = readl(base + QUADSPI_SR);
if (reg2 & (QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK)) {
udelay(1);
dev_dbg(q->dev, "The controller is busy, 0x%x\n", reg2);
continue;
}
break;
} while (1);
/* trigger the LUT now */
seqid = fsl_qspi_get_seqid(q, cmd);
writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);
/* Wait for the interrupt. */
err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000));
if (!err) {
dev_err(q->dev,
"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
cmd, addr, readl(base + QUADSPI_FR),
readl(base + QUADSPI_SR));
err = -ETIMEDOUT;
} else {
err = 0;
}
/* restore the MCR */
writel(reg, base + QUADSPI_MCR);
return err;
}
/* Read out the data from the QUADSPI_RBDR buffer registers. */
static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u8 *rxbuf)
{
u32 tmp;
int i = 0;
while (len > 0) {
tmp = readl(q->iobase + QUADSPI_RBDR + i * 4);
tmp = fsl_qspi_endian_xchg(q, tmp);
dev_dbg(q->dev, "chip addr:0x%.8x, rcv:0x%.8x\n",
q->chip_base_addr, tmp);
if (len >= 4) {
*((u32 *)rxbuf) = tmp;
rxbuf += 4;
} else {
memcpy(rxbuf, &tmp, len);
break;
}
len -= 4;
i++;
}
}
/*
* If we have changed the content of the flash by writing or erasing,
* we need to invalidate the AHB buffer. If we do not do so, we may read out
* the wrong data. The spec tells us reset the AHB domain and Serial Flash
* domain at the same time.
*/
static inline void fsl_qspi_invalid(struct fsl_qspi *q)
{
u32 reg;
reg = readl(q->iobase + QUADSPI_MCR);
reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
writel(reg, q->iobase + QUADSPI_MCR);
/*
* The minimum delay : 1 AHB + 2 SFCK clocks.
* Delay 1 us is enough.
*/
udelay(1);
reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
writel(reg, q->iobase + QUADSPI_MCR);
}
static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
u8 opcode, unsigned int to, u32 *txbuf,
unsigned count, size_t *retlen)
{
int ret, i, j;
u32 tmp;
dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n",
q->chip_base_addr, to, count);
/* clear the TX FIFO. */
tmp = readl(q->iobase + QUADSPI_MCR);
writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
/* fill the TX data to the FIFO */
for (j = 0, i = ((count + 3) / 4); j < i; j++) {
tmp = fsl_qspi_endian_xchg(q, *txbuf);
writel(tmp, q->iobase + QUADSPI_TBDR);
txbuf++;
}
/* Trigger it */
ret = fsl_qspi_runcmd(q, opcode, to, count);
if (ret == 0 && retlen)
*retlen += count;
return ret;
}
static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
{
int nor_size = q->nor_size;
void __iomem *base = q->iobase;
writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD);
}
/*
* There are two different ways to read out the data from the flash:
* the "IP Command Read" and the "AHB Command Read".
*
* The IC guy suggests we use the "AHB Command Read" which is faster
* then the "IP Command Read". (What's more is that there is a bug in
* the "IP Command Read" in the Vybrid.)
*
* After we set up the registers for the "AHB Command Read", we can use
* the memcpy to read the data directly. A "missed" access to the buffer
* causes the controller to clear the buffer, and use the sequence pointed
* by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
*/
static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
int seqid;
/* AHB configuration for access buffer 0/1/2 .*/
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR);
/* We only use the buffer3 */
writel(0, base + QUADSPI_BUF0IND);
writel(0, base + QUADSPI_BUF1IND);
writel(0, base + QUADSPI_BUF2IND);
/* Set the default lut sequence for AHB Read. */
seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode);
writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
q->iobase + QUADSPI_BFGENCR);
}
/* We use this function to do some basic init for spi_nor_scan(). */
static int fsl_qspi_nor_setup(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
u32 reg;
int ret;
/* the default frequency, we will change it in the future.*/
ret = clk_set_rate(q->clk, 66000000);
if (ret)
return ret;
/* Init the LUT table. */
fsl_qspi_init_lut(q);
/* Disable the module */
writel(QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK,
base + QUADSPI_MCR);
reg = readl(base + QUADSPI_SMPR);
writel(reg & ~(QUADSPI_SMPR_FSDLY_MASK
| QUADSPI_SMPR_FSPHS_MASK
| QUADSPI_SMPR_HSENA_MASK
| QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR);
/* Enable the module */
writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
base + QUADSPI_MCR);
/* enable the interrupt */
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
return 0;
}
static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
{
unsigned long rate = q->clk_rate;
int ret;
if (is_imx6sx_qspi(q))
rate *= 4;
ret = clk_set_rate(q->clk, rate);
if (ret)
return ret;
/* Init the LUT table again. */
fsl_qspi_init_lut(q);
/* Init for AHB read */
fsl_qspi_init_abh_read(q);
return 0;
}
static struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
static void fsl_qspi_set_base_addr(struct fsl_qspi *q, struct spi_nor *nor)
{
q->chip_base_addr = q->nor_size * (nor - q->nor);
}
static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
int ret;
struct fsl_qspi *q = nor->priv;
ret = fsl_qspi_runcmd(q, opcode, 0, len);
if (ret)
return ret;
fsl_qspi_read_data(q, len, buf);
return 0;
}
static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
int write_enable)
{
struct fsl_qspi *q = nor->priv;
int ret;
if (!buf) {
ret = fsl_qspi_runcmd(q, opcode, 0, 1);
if (ret)
return ret;
if (opcode == SPINOR_OP_CHIP_ERASE)
fsl_qspi_invalid(q);
} else if (len > 0) {
ret = fsl_qspi_nor_write(q, nor, opcode, 0,
(u32 *)buf, len, NULL);
} else {
dev_err(q->dev, "invalid cmd %d\n", opcode);
ret = -EINVAL;
}
return ret;
}
static void fsl_qspi_write(struct spi_nor *nor, loff_t to,
size_t len, size_t *retlen, const u_char *buf)
{
struct fsl_qspi *q = nor->priv;
fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
(u32 *)buf, len, retlen);
/* invalid the data in the AHB buffer. */
fsl_qspi_invalid(q);
}
static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode;
int ret;
dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
/* Wait until the previous command is finished. */
ret = nor->wait_till_ready(nor);
if (ret)
return ret;
/* Read out the data directly from the AHB buffer.*/
memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
*retlen += len;
return 0;
}
static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
{
struct fsl_qspi *q = nor->priv;
int ret;
dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
/* Wait until finished previous write command. */
ret = nor->wait_till_ready(nor);
if (ret)
return ret;
/* Send write enable, then erase commands. */
ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
if (ret)
return ret;
ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
if (ret)
return ret;
fsl_qspi_invalid(q);
return 0;
}
static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct fsl_qspi *q = nor->priv;
int ret;
ret = clk_enable(q->clk_en);
if (ret)
return ret;
ret = clk_enable(q->clk);
if (ret) {
clk_disable(q->clk_en);
return ret;
}
fsl_qspi_set_base_addr(q, nor);
return 0;
}
static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct fsl_qspi *q = nor->priv;
clk_disable(q->clk);
clk_disable(q->clk_en);
}
static int fsl_qspi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mtd_part_parser_data ppdata;
struct device *dev = &pdev->dev;
struct fsl_qspi *q;
struct resource *res;
struct spi_nor *nor;
struct mtd_info *mtd;
int ret, i = 0;
bool has_second_chip = false;
const struct of_device_id *of_id =
of_match_device(fsl_qspi_dt_ids, &pdev->dev);
q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
if (!q)
return -ENOMEM;
q->nor_num = of_get_child_count(dev->of_node);
if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP)
return -ENODEV;
/* find the resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
q->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(q->iobase)) {
ret = PTR_ERR(q->iobase);
goto map_failed;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"QuadSPI-memory");
q->ahb_base = devm_ioremap_resource(dev, res);
if (IS_ERR(q->ahb_base)) {
ret = PTR_ERR(q->ahb_base);
goto map_failed;
}
q->memmap_phy = res->start;
/* find the clocks */
q->clk_en = devm_clk_get(dev, "qspi_en");
if (IS_ERR(q->clk_en)) {
ret = PTR_ERR(q->clk_en);
goto map_failed;
}
q->clk = devm_clk_get(dev, "qspi");
if (IS_ERR(q->clk)) {
ret = PTR_ERR(q->clk);
goto map_failed;
}
ret = clk_prepare_enable(q->clk_en);
if (ret) {
dev_err(dev, "can not enable the qspi_en clock\n");
goto map_failed;
}
ret = clk_prepare_enable(q->clk);
if (ret) {
clk_disable_unprepare(q->clk_en);
dev_err(dev, "can not enable the qspi clock\n");
goto map_failed;
}
/* find the irq */
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "failed to get the irq\n");
goto irq_failed;
}
ret = devm_request_irq(dev, ret,
fsl_qspi_irq_handler, 0, pdev->name, q);
if (ret) {
dev_err(dev, "failed to request irq.\n");
goto irq_failed;
}
q->dev = dev;
q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
platform_set_drvdata(pdev, q);
ret = fsl_qspi_nor_setup(q);
if (ret)
goto irq_failed;
if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
has_second_chip = true;
/* iterate the subnodes. */
for_each_available_child_of_node(dev->of_node, np) {
const struct spi_device_id *id;
char modalias[40];
/* skip the holes */
if (!has_second_chip)
i *= 2;
nor = &q->nor[i];
mtd = &q->mtd[i];
nor->mtd = mtd;
nor->dev = dev;
nor->priv = q;
mtd->priv = nor;
/* fill the hooks */
nor->read_reg = fsl_qspi_read_reg;
nor->write_reg = fsl_qspi_write_reg;
nor->read = fsl_qspi_read;
nor->write = fsl_qspi_write;
nor->erase = fsl_qspi_erase;
nor->prepare = fsl_qspi_prep;
nor->unprepare = fsl_qspi_unprep;
if (of_modalias_node(np, modalias, sizeof(modalias)) < 0)
goto map_failed;
id = spi_nor_match_id(modalias);
if (!id)
goto map_failed;
ret = of_property_read_u32(np, "spi-max-frequency",
&q->clk_rate);
if (ret < 0)
goto map_failed;
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
ret = spi_nor_scan(nor, id, SPI_NOR_QUAD);
if (ret)
goto map_failed;
ppdata.of_node = np;
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (ret)
goto map_failed;
/* Set the correct NOR size now. */
if (q->nor_size == 0) {
q->nor_size = mtd->size;
/* Map the SPI NOR to accessiable address */
fsl_qspi_set_map_addr(q);
}
/*
* The TX FIFO is 64 bytes in the Vybrid, but the Page Program
* may writes 265 bytes per time. The write is working in the
* unit of the TX FIFO, not in the unit of the SPI NOR's page
* size.
*
* So shrink the spi_nor->page_size if it is larger then the
* TX FIFO.
*/
if (nor->page_size > q->devtype_data->txfifo)
nor->page_size = q->devtype_data->txfifo;
i++;
}
/* finish the rest init. */
ret = fsl_qspi_nor_setup_last(q);
if (ret)
goto last_init_failed;
clk_disable(q->clk);
clk_disable(q->clk_en);
dev_info(dev, "QuadSPI SPI NOR flash driver\n");
return 0;
last_init_failed:
for (i = 0; i < q->nor_num; i++)
mtd_device_unregister(&q->mtd[i]);
irq_failed:
clk_disable_unprepare(q->clk);
clk_disable_unprepare(q->clk_en);
map_failed:
dev_err(dev, "Freescale QuadSPI probe failed\n");
return ret;
}
static int fsl_qspi_remove(struct platform_device *pdev)
{
struct fsl_qspi *q = platform_get_drvdata(pdev);
int i;
for (i = 0; i < q->nor_num; i++)
mtd_device_unregister(&q->mtd[i]);
/* disable the hardware */
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
writel(0x0, q->iobase + QUADSPI_RSER);
clk_unprepare(q->clk);
clk_unprepare(q->clk_en);
return 0;
}
static struct platform_driver fsl_qspi_driver = {
.driver = {
.name = "fsl-quadspi",
.bus = &platform_bus_type,
.owner = THIS_MODULE,
.of_match_table = fsl_qspi_dt_ids,
},
.probe = fsl_qspi_probe,
.remove = fsl_qspi_remove,
};
module_platform_driver(fsl_qspi_driver);
MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver");
MODULE_AUTHOR("Freescale Semiconductor Inc.");
MODULE_LICENSE("GPL v2");
/*
* Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with
* influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c
*
* Copyright (C) 2005, Intec Automation Inc.
* Copyright (C) 2014, Freescale Semiconductor, Inc.
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/math64.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
/* Define max times to check status register before we give up. */
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16)
/*
* Read the status register, returning its value in the location
* Return the status register value.
* Returns negative if error occurred.
*/
static int read_sr(struct spi_nor *nor)
{
int ret;
u8 val;
ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);
if (ret < 0) {
pr_err("error %d reading SR\n", (int) ret);
return ret;
}
return val;
}
/*
* Read configuration register, returning its value in the
* location. Return the configuration register value.
* Returns negative if error occured.
*/
static int read_cr(struct spi_nor *nor)
{
int ret;
u8 val;
ret = nor->read_reg(nor, SPINOR_OP_RDCR, &val, 1);
if (ret < 0) {
dev_err(nor->dev, "error %d reading CR\n", ret);
return ret;
}
return val;
}
/*
* Dummy Cycle calculation for different type of read.
* It can be used to support more commands with
* different dummy cycle requirements.
*/
static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
{
switch (nor->flash_read) {
case SPI_NOR_FAST:
case SPI_NOR_DUAL:
case SPI_NOR_QUAD:
return 1;
case SPI_NOR_NORMAL:
return 0;
}
return 0;
}
/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
static inline int write_sr(struct spi_nor *nor, u8 val)
{
nor->cmd_buf[0] = val;
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
}
/*
* Set write enable latch with Write Enable command.
* Returns negative if error occurred.
*/
static inline int write_enable(struct spi_nor *nor)
{
return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
}
/*
* Send write disble instruction to the chip.
*/
static inline int write_disable(struct spi_nor *nor)
{
return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
}
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
{
return mtd->priv;
}
/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
{
int status;
bool need_wren = false;
u8 cmd;
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_ST: /* Micron, actually */
/* Some Micron need WREN command; all will accept it */
need_wren = true;
case CFI_MFR_MACRONIX:
case 0xEF /* winbond */:
if (need_wren)
write_enable(nor);
cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
status = nor->write_reg(nor, cmd, NULL, 0, 0);
if (need_wren)
write_disable(nor);
return status;
default:
/* Spansion style */
nor->cmd_buf[0] = enable << 7;
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
}
}
static int spi_nor_wait_till_ready(struct spi_nor *nor)
{
unsigned long deadline;
int sr;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
cond_resched();
sr = read_sr(nor);
if (sr < 0)
break;
else if (!(sr & SR_WIP))
return 0;
} while (!time_after_eq(jiffies, deadline));
return -ETIMEDOUT;
}
/*
* Service routine to read status register until ready, or timeout occurs.
* Returns non-zero if error.
*/
static int wait_till_ready(struct spi_nor *nor)
{
return nor->wait_till_ready(nor);
}
/*
* Erase the whole flash memory
*
* Returns 0 if successful, non-zero otherwise.
*/
static int erase_chip(struct spi_nor *nor)
{
int ret;
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
/* Wait until finished previous write command. */
ret = wait_till_ready(nor);
if (ret)
return ret;
/* Send write enable, then erase commands. */
write_enable(nor);
return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
}
static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{
int ret = 0;
mutex_lock(&nor->lock);
if (nor->prepare) {
ret = nor->prepare(nor, ops);
if (ret) {
dev_err(nor->dev, "failed in the preparation.\n");
mutex_unlock(&nor->lock);
return ret;
}
}
return ret;
}
static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
if (nor->unprepare)
nor->unprepare(nor, ops);
mutex_unlock(&nor->lock);
}
/*
* Erase an address range on the nor chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
u32 addr, len;
uint32_t rem;
int ret;
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
(long long)instr->len);
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
addr = instr->addr;
len = instr->len;
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_ERASE);
if (ret)
return ret;
/* whole-chip erase? */
if (len == mtd->size) {
if (erase_chip(nor)) {
ret = -EIO;
goto erase_err;
}
/* REVISIT in some cases we could speed up erasing large regions
* by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
* to use "small sector erase", but that's not always optimal.
*/
/* "sector"-at-a-time erase */
} else {
while (len) {
if (nor->erase(nor, addr)) {
ret = -EIO;
goto erase_err;
}
addr += mtd->erasesize;
len -= mtd->erasesize;
}
}
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return ret;
erase_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
instr->state = MTD_ERASE_FAILED;
return ret;
}
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
uint32_t offset = ofs;
uint8_t status_old, status_new;
int ret = 0;
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
if (ret)
return ret;
/* Wait until finished previous command */
ret = wait_till_ready(nor);
if (ret)
goto err;
status_old = read_sr(nor);
if (offset < mtd->size - (mtd->size / 2))
status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
else if (offset < mtd->size - (mtd->size / 4))
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
else if (offset < mtd->size - (mtd->size / 8))
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
else if (offset < mtd->size - (mtd->size / 16))
status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
else if (offset < mtd->size - (mtd->size / 32))
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
else if (offset < mtd->size - (mtd->size / 64))
status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
else
status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
/* Only modify protection if it will not unlock other areas */
if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) >
(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
write_enable(nor);
ret = write_sr(nor, status_new);
if (ret)
goto err;
}
err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
return ret;
}
static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
uint32_t offset = ofs;
uint8_t status_old, status_new;
int ret = 0;
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
if (ret)
return ret;
/* Wait until finished previous command */
ret = wait_till_ready(nor);
if (ret)
goto err;
status_old = read_sr(nor);
if (offset+len > mtd->size - (mtd->size / 64))
status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0);
else if (offset+len > mtd->size - (mtd->size / 32))
status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
else if (offset+len > mtd->size - (mtd->size / 16))
status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
else if (offset+len > mtd->size - (mtd->size / 8))
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
else if (offset+len > mtd->size - (mtd->size / 4))
status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
else if (offset+len > mtd->size - (mtd->size / 2))
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
else
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
/* Only modify protection if it will not lock other areas */
if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) <
(status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
write_enable(nor);
ret = write_sr(nor, status_new);
if (ret)
goto err;
}
err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
return ret;
}
struct flash_info {
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
* a high byte of zero plus three data bytes: the manufacturer id,
* then a two byte device id.
*/
u32 jedec_id;
u16 ext_id;
/* The size listed here is what works with SPINOR_OP_SE, which isn't
* necessarily called a "sector" by the vendor.
*/
unsigned sector_size;
u16 n_sectors;
u16 page_size;
u16 addr_width;
u16 flags;
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
#define SST_WRITE 0x04 /* use SST byte programming */
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.jedec_id = (_jedec_id), \
.ext_id = (_ext_id), \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags), \
})
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = (_page_size), \
.addr_width = (_addr_width), \
.flags = (_flags), \
})
/* NOTE: double check command sets and memory organization when you add
* more nor chips. This current list focusses on newer chips, which
* have been converging on command sets which including JEDEC ID.
*/
const struct spi_device_id spi_nor_ids[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
{ "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
/* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
/* ESMT */
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
/* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
/* GigaDevice */
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
/* Intel/Numonyx -- xxxs33b */
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
/* Macronix */
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
{ "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) },
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) },
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
/* Micron */
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
/* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
{ "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) },
{ "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) },
/* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
*/
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) },
{ "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
{ "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) },
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
/* ST Microelectronics -- newer production may have feature updates */
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
{ "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) },
{ "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) },
{ "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) },
{ "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) },
{ "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) },
{ "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
{ "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
{ "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) },
{ "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) },
{ "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) },
{ "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) },
{ "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) },
{ "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) },
{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) },
{ "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) },
{ "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) },
{ "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) },
{ "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) },
{ "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
{ "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) },
{ "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) },
{ "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) },
{ "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) },
{ "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) },
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
{ "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) },
{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ },
};
EXPORT_SYMBOL_GPL(spi_nor_ids);
static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
{
int tmp;
u8 id[5];
u32 jedec;
u16 ext_jedec;
struct flash_info *info;
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5);
if (tmp < 0) {
dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp);
}
jedec = id[0];
jedec = jedec << 8;
jedec |= id[1];
jedec = jedec << 8;
jedec |= id[2];
ext_jedec = id[3] << 8 | id[4];
for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
info = (void *)spi_nor_ids[tmp].driver_data;
if (info->jedec_id == jedec) {
if (info->ext_id == 0 || info->ext_id == ext_jedec)
return &spi_nor_ids[tmp];
}
}
dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
return ERR_PTR(-ENODEV);
}
static const struct spi_device_id *jedec_probe(struct spi_nor *nor)
{
return nor->read_id(nor);
}
static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
int ret;
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);
if (ret)
return ret;
ret = nor->read(nor, from, len, retlen, buf);
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
return ret;
}
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
size_t actual;
int ret;
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
if (ret)
return ret;
/* Wait until finished previous write command. */
ret = wait_till_ready(nor);
if (ret)
goto time_out;
write_enable(nor);
nor->sst_write_second = false;
actual = to % 2;
/* Start write from odd address. */
if (actual) {
nor->program_opcode = SPINOR_OP_BP;
/* write one byte. */
nor->write(nor, to, 1, retlen, buf);
ret = wait_till_ready(nor);
if (ret)
goto time_out;
}
to += actual;
/* Write out most of the data here. */
for (; actual < len - 1; actual += 2) {
nor->program_opcode = SPINOR_OP_AAI_WP;
/* write two bytes. */
nor->write(nor, to, 2, retlen, buf + actual);
ret = wait_till_ready(nor);
if (ret)
goto time_out;
to += 2;
nor->sst_write_second = true;
}
nor->sst_write_second = false;
write_disable(nor);
ret = wait_till_ready(nor);
if (ret)
goto time_out;
/* Write out trailing byte if it exists. */
if (actual != len) {
write_enable(nor);
nor->program_opcode = SPINOR_OP_BP;
nor->write(nor, to, 1, retlen, buf + actual);
ret = wait_till_ready(nor);
if (ret)
goto time_out;
write_disable(nor);
}
time_out:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
return ret;
}
/*
* Write an address range to the nor chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
* it is within the physical boundaries.
*/
static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
u32 page_offset, page_size, i;
int ret;
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
if (ret)
return ret;
/* Wait until finished previous write command. */
ret = wait_till_ready(nor);
if (ret)
goto write_err;
write_enable(nor);
page_offset = to & (nor->page_size - 1);
/* do all the bytes fit onto one page? */
if (page_offset + len <= nor->page_size) {
nor->write(nor, to, len, retlen, buf);
} else {
/* the size of data remaining on the first page */
page_size = nor->page_size - page_offset;
nor->write(nor, to, page_size, retlen, buf);
/* write everything in nor->page_size chunks */
for (i = page_size; i < len; i += page_size) {
page_size = len - i;
if (page_size > nor->page_size)
page_size = nor->page_size;
wait_till_ready(nor);
write_enable(nor);
nor->write(nor, to + i, page_size, retlen, buf + i);
}
}
write_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
return 0;
}
static int macronix_quad_enable(struct spi_nor *nor)
{
int ret, val;
val = read_sr(nor);
write_enable(nor);
nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
if (wait_till_ready(nor))
return 1;
ret = read_sr(nor);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
dev_err(nor->dev, "Macronix Quad bit not set\n");
return -EINVAL;
}
return 0;
}
/*
* Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the
* second byte will be written to the configuration register.
* Return negative if error occured.
*/
static int write_sr_cr(struct spi_nor *nor, u16 val)
{
nor->cmd_buf[0] = val & 0xff;
nor->cmd_buf[1] = (val >> 8);
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
}
static int spansion_quad_enable(struct spi_nor *nor)
{
int ret;
int quad_en = CR_QUAD_EN_SPAN << 8;
write_enable(nor);
ret = write_sr_cr(nor, quad_en);
if (ret < 0) {
dev_err(nor->dev,
"error while writing configuration register\n");
return -EINVAL;
}
/* read back and check it */
ret = read_cr(nor);
if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
dev_err(nor->dev, "Spansion Quad bit not set\n");
return -EINVAL;
}
return 0;
}
static int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
{
int status;
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_MACRONIX:
status = macronix_quad_enable(nor);
if (status) {
dev_err(nor->dev, "Macronix quad-read not enabled\n");
return -EINVAL;
}
return status;
default:
status = spansion_quad_enable(nor);
if (status) {
dev_err(nor->dev, "Spansion quad-read not enabled\n");
return -EINVAL;
}
return status;
}
}
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
!nor->read_reg || !nor->write_reg || !nor->erase) {
pr_err("spi-nor: please fill all the necessary fields!\n");
return -EINVAL;
}
if (!nor->read_id)
nor->read_id = spi_nor_read_id;
if (!nor->wait_till_ready)
nor->wait_till_ready = spi_nor_wait_till_ready;
return 0;
}
int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
enum read_mode mode)
{
struct flash_info *info;
struct flash_platform_data *data;
struct device *dev = nor->dev;
struct mtd_info *mtd = nor->mtd;
struct device_node *np = dev->of_node;
int ret;
int i;
ret = spi_nor_check(nor);
if (ret)
return ret;
/* Platform data helps sort out which chip type we have, as
* well as how this board partitions it. If we don't have
* a chip ID, try the JEDEC id commands; they'll work for most
* newer chips, even if we don't recognize the particular chip.
*/
data = dev_get_platdata(dev);
if (data && data->type) {
const struct spi_device_id *plat_id;
for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) {
plat_id = &spi_nor_ids[i];
if (strcmp(data->type, plat_id->name))
continue;
break;
}
if (i < ARRAY_SIZE(spi_nor_ids) - 1)
id = plat_id;
else
dev_warn(dev, "unrecognized id %s\n", data->type);
}
info = (void *)id->driver_data;
if (info->jedec_id) {
const struct spi_device_id *jid;
jid = jedec_probe(nor);
if (IS_ERR(jid)) {
return PTR_ERR(jid);
} else if (jid != id) {
/*
* JEDEC knows better, so overwrite platform ID. We
* can't trust partitions any longer, but we'll let
* mtd apply them anyway, since some partitions may be
* marked read-only, and we don't want to lose that
* information, even if it's not 100% accurate.
*/
dev_warn(dev, "found %s, expected %s\n",
jid->name, id->name);
id = jid;
info = (void *)jid->driver_data;
}
}
mutex_init(&nor->lock);
/*
* Atmel, SST and Intel/Numonyx serial nor tend to power
* up with the software protection bits set
*/
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
write_enable(nor);
write_sr(nor, 0);
}
if (data && data->name)
mtd->name = data->name;
else
mtd->name = dev_name(dev);
mtd->type = MTD_NORFLASH;
mtd->writesize = 1;
mtd->flags = MTD_CAP_NORFLASH;
mtd->size = info->sector_size * info->n_sectors;
mtd->_erase = spi_nor_erase;
mtd->_read = spi_nor_read;
/* nor protection support for STmicro chips */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
mtd->_lock = spi_nor_lock;
mtd->_unlock = spi_nor_unlock;
}
/* sst nor chips use AAI word program */
if (info->flags & SST_WRITE)
mtd->_write = sst_write;
else
mtd->_write = spi_nor_write;
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
nor->erase_opcode = SPINOR_OP_BE_4K;
mtd->erasesize = 4096;
} else if (info->flags & SECT_4K_PMC) {
nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
mtd->erasesize = 4096;
} else {
nor->erase_opcode = SPINOR_OP_SE;
mtd->erasesize = info->sector_size;
}
if (info->flags & SPI_NOR_NO_ERASE)
mtd->flags |= MTD_NO_ERASE;
mtd->dev.parent = dev;
nor->page_size = info->page_size;
mtd->writebufsize = nor->page_size;
if (np) {
/* If we were instantiated by DT, use it */
if (of_property_read_bool(np, "m25p,fast-read"))
nor->flash_read = SPI_NOR_FAST;
else
nor->flash_read = SPI_NOR_NORMAL;
} else {
/* If we weren't instantiated by DT, default to fast-read */
nor->flash_read = SPI_NOR_FAST;
}
/* Some devices cannot do fast-read, no matter what DT tells us */
if (info->flags & SPI_NOR_NO_FR)
nor->flash_read = SPI_NOR_NORMAL;
/* Quad/Dual-read mode takes precedence over fast/normal */
if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
ret = set_quad_mode(nor, info->jedec_id);
if (ret) {
dev_err(dev, "quad mode not supported\n");
return ret;
}
nor->flash_read = SPI_NOR_QUAD;
} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
nor->flash_read = SPI_NOR_DUAL;
}
/* Default commands */
switch (nor->flash_read) {
case SPI_NOR_QUAD:
nor->read_opcode = SPINOR_OP_READ_1_1_4;
break;
case SPI_NOR_DUAL:
nor->read_opcode = SPINOR_OP_READ_1_1_2;
break;
case SPI_NOR_FAST:
nor->read_opcode = SPINOR_OP_READ_FAST;
break;
case SPI_NOR_NORMAL:
nor->read_opcode = SPINOR_OP_READ;
break;
default:
dev_err(dev, "No Read opcode defined\n");
return -EINVAL;
}
nor->program_opcode = SPINOR_OP_PP;
if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
/* Dedicated 4-byte command set */
switch (nor->flash_read) {
case SPI_NOR_QUAD:
nor->read_opcode = SPINOR_OP_READ4_1_1_4;
break;
case SPI_NOR_DUAL:
nor->read_opcode = SPINOR_OP_READ4_1_1_2;
break;
case SPI_NOR_FAST:
nor->read_opcode = SPINOR_OP_READ4_FAST;
break;
case SPI_NOR_NORMAL:
nor->read_opcode = SPINOR_OP_READ4;
break;
}
nor->program_opcode = SPINOR_OP_PP_4B;
/* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE_4B;
mtd->erasesize = info->sector_size;
} else
set_4byte(nor, info->jedec_id, 1);
} else {
nor->addr_width = 3;
}
nor->read_dummy = spi_nor_read_dummy_cycles(nor);
dev_info(dev, "%s (%lld Kbytes)\n", id->name,
(long long)mtd->size >> 10);
dev_dbg(dev,
"mtd .name = %s, .size = 0x%llx (%lldMiB), "
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20),
mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions);
if (mtd->numeraseregions)
for (i = 0; i < mtd->numeraseregions; i++)
dev_dbg(dev,
"mtd.eraseregions[%d] = { .offset = 0x%llx, "
".erasesize = 0x%.8x (%uKiB), "
".numblocks = %d }\n",
i, (long long)mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].erasesize / 1024,
mtd->eraseregions[i].numblocks);
return 0;
}
EXPORT_SYMBOL_GPL(spi_nor_scan);
const struct spi_device_id *spi_nor_match_id(char *name)
{
const struct spi_device_id *id = spi_nor_ids;
while (id->name[0]) {
if (!strcmp(name, id->name))
return id;
id++;
}
return NULL;
}
EXPORT_SYMBOL_GPL(spi_nor_match_id);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("framework for SPI NOR");
/*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_MTD_SPI_NOR_H
#define __LINUX_MTD_SPI_NOR_H
/*
* Note on opcode nomenclature: some opcodes have a format like
* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
* of I/O lines used for the opcode, address, and data (respectively). The
* FUNCTION has an optional suffix of '4', to represent an opcode which
* requires a 4-byte (32-bit) address.
*/
/* Flash opcodes. */
#define SPINOR_OP_WREN 0x06 /* Write enable */
#define SPINOR_OP_RDSR 0x05 /* Read status register */
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
/* Used for SST flashes only. */
#define SPINOR_OP_BP 0x02 /* Byte program */
#define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
/* Used for Macronix and Winbond flashes. */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
/* Used for Spansion flashes only. */
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
/* Status Register bits. */
#define SR_WIP 1 /* Write in progress */
#define SR_WEL 2 /* Write enable latch */
/* meaning of other SR_* bits may differ between vendors */
#define SR_BP0 4 /* Block protect 0 */
#define SR_BP1 8 /* Block protect 1 */
#define SR_BP2 0x10 /* Block protect 2 */
#define SR_SRWD 0x80 /* SR write protect */
#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
/* Configuration Register bits. */
#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
enum read_mode {
SPI_NOR_NORMAL = 0,
SPI_NOR_FAST,
SPI_NOR_DUAL,
SPI_NOR_QUAD,
};
/**
* struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
* @wren: command for "Write Enable", or 0x00 for not required
* @cmd: command for operation
* @cmd_pins: number of pins to send @cmd (1, 2, 4)
* @addr: address for operation
* @addr_pins: number of pins to send @addr (1, 2, 4)
* @addr_width: number of address bytes
* (3,4, or 0 for address not required)
* @mode: mode data
* @mode_pins: number of pins to send @mode (1, 2, 4)
* @mode_cycles: number of mode cycles (0 for mode not required)
* @dummy_cycles: number of dummy cycles (0 for dummy not required)
*/
struct spi_nor_xfer_cfg {
u8 wren;
u8 cmd;
u8 cmd_pins;
u32 addr;
u8 addr_pins;
u8 addr_width;
u8 mode;
u8 mode_pins;
u8 mode_cycles;
u8 dummy_cycles;
};
#define SPI_NOR_MAX_CMD_SIZE 8
enum spi_nor_ops {
SPI_NOR_OPS_READ = 0,
SPI_NOR_OPS_WRITE,
SPI_NOR_OPS_ERASE,
SPI_NOR_OPS_LOCK,
SPI_NOR_OPS_UNLOCK,
};
/**
* struct spi_nor - Structure for defining a the SPI NOR layer
* @mtd: point to a mtd_info structure
* @lock: the lock for the read/write/erase/lock/unlock operations
* @dev: point to a spi device, or a spi nor controller device.
* @page_size: the page size of the SPI NOR
* @addr_width: number of address bytes
* @erase_opcode: the opcode for erasing a sector
* @read_opcode: the read opcode
* @read_dummy: the dummy needed by the read operation
* @program_opcode: the program opcode
* @flash_read: the mode of the read
* @sst_write_second: used by the SST write operation
* @cfg: used by the read_xfer/write_xfer
* @cmd_buf: used by the write_reg
* @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations
* @unprepare: [OPTIONAL] do some post work after the
* read/write/erase/lock/unlock operations
* @read_xfer: [OPTIONAL] the read fundamental primitive
* @write_xfer: [OPTIONAL] the writefundamental primitive
* @read_reg: [DRIVER-SPECIFIC] read out the register
* @write_reg: [DRIVER-SPECIFIC] write data to the register
* @read_id: [REPLACEABLE] read out the ID data, and find
* the proper spi_device_id
* @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
* at the offset @offs
* @priv: the private data
*/
struct spi_nor {
struct mtd_info *mtd;
struct mutex lock;
struct device *dev;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
enum read_mode flash_read;
bool sst_write_second;
struct spi_nor_xfer_cfg cfg;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
u8 *buf, size_t len);
int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
u8 *buf, size_t len);
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
int write_enable);
const struct spi_device_id *(*read_id)(struct spi_nor *nor);
int (*wait_till_ready)(struct spi_nor *nor);
int (*read)(struct spi_nor *nor, loff_t from,
size_t len, size_t *retlen, u_char *read_buf);
void (*write)(struct spi_nor *nor, loff_t to,
size_t len, size_t *retlen, const u_char *write_buf);
int (*erase)(struct spi_nor *nor, loff_t offs);
void *priv;
};
/**
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure
* @id: the spi_device_id provided by the driver
* @mode: the read mode supported by the driver
*
* The drivers can use this fuction to scan the SPI NOR.
* In the scanning, it will try to get all the necessary information to
* fill the mtd_info{} and the spi_nor{}.
*
* The board may assigns a spi_device_id with @id which be used to compared with
* the spi_device_id detected by the scanning.
*
* Return: 0 for success, others for failure.
*/
int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
enum read_mode mode);
extern const struct spi_device_id spi_nor_ids[];
/**
* spi_nor_match_id() - find the spi_device_id by the name
* @name: the name of the spi_device_id
*
* The drivers use this function to find the spi_device_id
* specified by the @name.
*
* Return: returns the right spi_device_id pointer on success,
* and returns NULL on failure.
*/
const struct spi_device_id *spi_nor_match_id(char *name);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment