diff options
-rw-r--r-- | Documentation/devicetree/bindings/mtd/aspeed-smc.txt | 51 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 3 | ||||
-rw-r--r-- | drivers/mtd/devices/serial_flash_cmds.h | 7 | ||||
-rw-r--r-- | drivers/mtd/devices/st_spi_fsm.c | 28 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/Kconfig | 12 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/aspeed-smc.c | 759 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/cadence-quadspi.c | 6 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/spi-nor.c | 263 | ||||
-rw-r--r-- | drivers/spi/spi-bcm-qspi.c | 6 | ||||
-rw-r--r-- | include/linux/mtd/spi-nor.h | 34 |
11 files changed, 1106 insertions, 64 deletions
diff --git a/Documentation/devicetree/bindings/mtd/aspeed-smc.txt b/Documentation/devicetree/bindings/mtd/aspeed-smc.txt new file mode 100644 index 000000000000..49f6528ef547 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/aspeed-smc.txt @@ -0,0 +1,51 @@ +* Aspeed Firmware Memory controller +* Aspeed SPI Flash Memory Controller + +The Firmware Memory Controller in the Aspeed AST2500 SoC supports +three chip selects, two of which are always of SPI type and the third +can be SPI or NOR type flash. These bindings only describe SPI. + +The two SPI flash memory controllers in the AST2500 each support two +chip selects. + +Required properties: + - compatible : Should be one of + "aspeed,ast2400-fmc" for the AST2400 Firmware Memory Controller + "aspeed,ast2400-spi" for the AST2400 SPI Flash memory Controller + "aspeed,ast2500-fmc" for the AST2500 Firmware Memory Controller + "aspeed,ast2500-spi" for the AST2500 SPI flash memory controllers + + - reg : the first contains the control register location and length, + the second contains the memory window mapping address and length + - #address-cells : must be 1 corresponding to chip select child binding + - #size-cells : must be 0 corresponding to chip select child binding + +Optional properties: + - interrupts : Should contain the interrupt for the dma device if an + FMC + +The child nodes are the SPI flash modules which must have a compatible +property as specified in bindings/mtd/jedec,spi-nor.txt + +Optionally, the child node can contain properties for SPI mode (may be +ignored): + - spi-max-frequency - max frequency of spi bus + + +Example: +fmc: fmc@1e620000 { + compatible = "aspeed,ast2500-fmc"; + reg = < 0x1e620000 0x94 + 0x20000000 0x02000000 >; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <19>; + flash@0 { + reg = < 0 >; + compatible = "jedec,spi-nor"; + /* spi-max-frequency = <>; */ + /* m25p,fast-read; */ + #address-cells = <1>; + #size-cells = <1>; + }; +}; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 9cf7fcd28034..16a7df2a0246 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -172,7 +172,8 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, t[1].rx_buf = buf; t[1].rx_nbits = m25p80_rx_nbits(nor); - t[1].len = min(len, spi_max_transfer_size(spi)); + t[1].len = min3(len, spi_max_transfer_size(spi), + spi_max_message_size(spi) - t[0].len); spi_message_add_tail(&t[1], &m); ret = spi_sync(spi, &m); diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h index f59a125295d0..8b81e15105dd 100644 --- a/drivers/mtd/devices/serial_flash_cmds.h +++ b/drivers/mtd/devices/serial_flash_cmds.h @@ -18,19 +18,12 @@ #define SPINOR_OP_RDVCR 0x85 /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ -#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */ -#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */ - #define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */ #define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ #define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ -/* READ commands with 32-bit addressing */ -#define SPINOR_OP_READ4_1_2_2 0xbc -#define SPINOR_OP_READ4_1_4_4 0xec - /* Configuration flags */ #define FLASH_FLAG_SINGLE 0x000000ff #define FLASH_FLAG_READ_WRITE 0x00000001 diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 5454b4113589..804313a33f2b 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = { * - 'FAST' variants configured for 8 dummy cycles (see note above.) */ static struct seq_rw_config n25q_read4_configs[] = { - {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 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, SPINOR_OP_READ4_1_2_2, 0, 2, 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, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, - {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0}, - {0x00, 0, 0, 0, 0, 0x00, 0, 0}, + {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; /* @@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) * entering a state that is incompatible with the SPIBoot Controller. */ static struct seq_rw_config stfsm_s25fl_read4_configs[] = { - {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4}, - {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, - {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0}, - {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, - {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, - {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0}, - {0x00, 0, 0, 0, 0, 0x00, 0, 0}, + {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 2, 4}, + {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; static struct seq_rw_config stfsm_s25fl_write4_configs[] = { diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 02013ffa5231..7252087ef407 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -29,6 +29,16 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). +config SPI_ASPEED_SMC + tristate "Aspeed flash controllers in SPI mode" + depends on ARCH_ASPEED || COMPILE_TEST + depends on HAS_IOMEM && OF + help + This enables support for the Firmware Memory controller (FMC) + in the Aspeed AST2500/AST2400 SoCs when attached to SPI NOR chips, + and support for the SPI flash memory controller (SPI) for + the host firmware. The implementation only supports SPI NOR. + config SPI_ATMEL_QUADSPI tristate "Atmel Quad SPI Controller" depends on ARCH_AT91 || (ARM && COMPILE_TEST) @@ -40,7 +50,7 @@ config SPI_ATMEL_QUADSPI config SPI_CADENCE_QUADSPI tristate "Cadence Quad SPI controller" - depends on OF && ARM + depends on OF && (ARM || COMPILE_TEST) help Enable support for the Cadence Quad SPI Flash controller. diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 1796e8cd6e1a..72238a793198 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o +obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c new file mode 100644 index 000000000000..6bb4c7d1788c --- /dev/null +++ b/drivers/mtd/spi-nor/aspeed-smc.c @@ -0,0 +1,759 @@ +/* + * ASPEED Static Memory Controller driver + * + * Copyright (c) 2015-2016, IBM Corporation. + * + * 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/bug.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/spi-nor.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/sysfs.h> + +#define DEVICE_NAME "aspeed-smc" + +/* + * The driver only support SPI flash + */ +enum aspeed_smc_flash_type { + smc_type_nor = 0, + smc_type_nand = 1, + smc_type_spi = 2, +}; + +struct aspeed_smc_chip; + +struct aspeed_smc_info { + u32 maxsize; /* maximum size of chip window */ + u8 nce; /* number of chip enables */ + bool hastype; /* flash type field exists in config reg */ + u8 we0; /* shift for write enable bit for CE0 */ + u8 ctl0; /* offset in regs of ctl for CE0 */ + + void (*set_4b)(struct aspeed_smc_chip *chip); +}; + +static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip); +static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip); + +static const struct aspeed_smc_info fmc_2400_info = { + .maxsize = 64 * 1024 * 1024, + .nce = 5, + .hastype = true, + .we0 = 16, + .ctl0 = 0x10, + .set_4b = aspeed_smc_chip_set_4b, +}; + +static const struct aspeed_smc_info spi_2400_info = { + .maxsize = 64 * 1024 * 1024, + .nce = 1, + .hastype = false, + .we0 = 0, + .ctl0 = 0x04, + .set_4b = aspeed_smc_chip_set_4b_spi_2400, +}; + +static const struct aspeed_smc_info fmc_2500_info = { + .maxsize = 256 * 1024 * 1024, + .nce = 3, + .hastype = true, + .we0 = 16, + .ctl0 = 0x10, + .set_4b = aspeed_smc_chip_set_4b, +}; + +static const struct aspeed_smc_info spi_2500_info = { + .maxsize = 128 * 1024 * 1024, + .nce = 2, + .hastype = false, + .we0 = 16, + .ctl0 = 0x10, + .set_4b = aspeed_smc_chip_set_4b, +}; + +enum aspeed_smc_ctl_reg_value { + smc_base, /* base value without mode for other commands */ + smc_read, /* command reg for (maybe fast) reads */ + smc_write, /* command reg for writes */ + smc_max, +}; + +struct aspeed_smc_controller; + +struct aspeed_smc_chip { + int cs; + struct aspeed_smc_controller *controller; + void __iomem *ctl; /* control register */ + void __iomem *ahb_base; /* base of chip window */ + u32 ctl_val[smc_max]; /* control settings */ + enum aspeed_smc_flash_type type; /* what type of flash */ + struct spi_nor nor; +}; + +struct aspeed_smc_controller { + struct device *dev; + + struct mutex mutex; /* controller access mutex */ + const struct aspeed_smc_info *info; /* type info of controller */ + void __iomem *regs; /* controller registers */ + void __iomem *ahb_base; /* per-chip windows resource */ + + struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */ +}; + +/* + * SPI Flash Configuration Register (AST2500 SPI) + * or + * Type setting Register (AST2500 FMC). + * CE0 and CE1 can only be of type SPI. CE2 can be of type NOR but the + * driver does not support it. + */ +#define CONFIG_REG 0x0 +#define CONFIG_DISABLE_LEGACY BIT(31) /* 1 */ + +#define CONFIG_CE2_WRITE BIT(18) +#define CONFIG_CE1_WRITE BIT(17) +#define CONFIG_CE0_WRITE BIT(16) + +#define CONFIG_CE2_TYPE BIT(4) /* AST2500 FMC only */ +#define CONFIG_CE1_TYPE BIT(2) /* AST2500 FMC only */ +#define CONFIG_CE0_TYPE BIT(0) /* AST2500 FMC only */ + +/* + * CE Control Register + */ +#define CE_CONTROL_REG 0x4 + +/* + * CEx Control Register + */ +#define CONTROL_AAF_MODE BIT(31) +#define CONTROL_IO_MODE_MASK GENMASK(30, 28) +#define CONTROL_IO_DUAL_DATA BIT(29) +#define CONTROL_IO_DUAL_ADDR_DATA (BIT(29) | BIT(28)) +#define CONTROL_IO_QUAD_DATA BIT(30) +#define CONTROL_IO_QUAD_ADDR_DATA (BIT(30) | BIT(28)) +#define CONTROL_CE_INACTIVE_SHIFT 24 +#define CONTROL_CE_INACTIVE_MASK GENMASK(27, \ + CONTROL_CE_INACTIVE_SHIFT) +/* 0 = 16T ... 15 = 1T T=HCLK */ +#define CONTROL_COMMAND_SHIFT 16 +#define CONTROL_DUMMY_COMMAND_OUT BIT(15) +#define CONTROL_IO_DUMMY_HI BIT(14) +#define CONTROL_IO_DUMMY_HI_SHIFT 14 +#define CONTROL_CLK_DIV4 BIT(13) /* others */ +#define CONTROL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI */ +#define CONTROL_RW_MERGE BIT(12) +#define CONTROL_IO_DUMMY_LO_SHIFT 6 +#define CONTROL_IO_DUMMY_LO GENMASK(7, \ + CONTROL_IO_DUMMY_LO_SHIFT) +#define CONTROL_IO_DUMMY_MASK (CONTROL_IO_DUMMY_HI | \ + CONTROL_IO_DUMMY_LO) +#define CONTROL_IO_DUMMY_SET(dummy) \ + (((((dummy) >> 2) & 0x1) << CONTROL_IO_DUMMY_HI_SHIFT) | \ + (((dummy) & 0x3) << CONTROL_IO_DUMMY_LO_SHIFT)) + +#define CONTROL_CLOCK_FREQ_SEL_SHIFT 8 +#define CONTROL_CLOCK_FREQ_SEL_MASK GENMASK(11, \ + CONTROL_CLOCK_FREQ_SEL_SHIFT) +#define CONTROL_LSB_FIRST BIT(5) +#define CONTROL_CLOCK_MODE_3 BIT(4) +#define CONTROL_IN_DUAL_DATA BIT(3) +#define CONTROL_CE_STOP_ACTIVE_CONTROL BIT(2) +#define CONTROL_COMMAND_MODE_MASK GENMASK(1, 0) +#define CONTROL_COMMAND_MODE_NORMAL 0 +#define CONTROL_COMMAND_MODE_FREAD 1 +#define CONTROL_COMMAND_MODE_WRITE 2 +#define CONTROL_COMMAND_MODE_USER 3 + +#define CONTROL_KEEP_MASK \ + (CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \ + CONTROL_IO_DUMMY_MASK | CONTROL_CLOCK_FREQ_SEL_MASK | \ + CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3) + +/* + * The Segment Register uses a 8MB unit to encode the start address + * and the end address of the mapping window of a flash SPI slave : + * + * | byte 1 | byte 2 | byte 3 | byte 4 | + * +--------+--------+--------+--------+ + * | end | start | 0 | 0 | + */ +#define SEGMENT_ADDR_REG0 0x30 +#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23) +#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23) + +/* + * In user mode all data bytes read or written to the chip decode address + * range are transferred to or from the SPI bus. The range is treated as a + * fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned + * to its size. The address within the multiple 8kB range is ignored when + * sending bytes to the SPI bus. + * + * On the arm architecture, as of Linux version 4.3, memcpy_fromio and + * memcpy_toio on little endian targets use the optimized memcpy routines + * that were designed for well behavied memory storage. These routines + * have a stutter if the source and destination are not both word aligned, + * once with a duplicate access to the source after aligning to the + * destination to a word boundary, and again with a duplicate access to + * the source when the final byte count is not word aligned. + * + * When writing or reading the fifo this stutter discards data or sends + * too much data to the fifo and can not be used by this driver. + * + * While the low level io string routines that implement the insl family do + * the desired accesses and memory increments, the cross architecture io + * macros make them essentially impossible to use on a memory mapped address + * instead of a a token from the call to iomap of an io port. + * + * These fifo routines use readl and friends to a constant io port and update + * the memory buffer pointer and count via explicit code. The final updates + * to len are optimistically suppressed. + */ +static int aspeed_smc_read_from_ahb(void *buf, const void __iomem *src, + size_t len) +{ + size_t offset = 0; + + if (IS_ALIGNED((uintptr_t)src, sizeof(uintptr_t)) && + IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { + ioread32_rep(src, buf, len >> 2); + offset = len & ~0x3; + len -= offset; + } + ioread8_rep(src, (u8 *)buf + offset, len); + return 0; +} + +static int aspeed_smc_write_to_ahb(void __iomem *dst, const void *buf, + size_t len) +{ + size_t offset = 0; + + if (IS_ALIGNED((uintptr_t)dst, sizeof(uintptr_t)) && + IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { + iowrite32_rep(dst, buf, len >> 2); + offset = len & ~0x3; + len -= offset; + } + iowrite8_rep(dst, (const u8 *)buf + offset, len); + return 0; +} + +static inline u32 aspeed_smc_chip_write_bit(struct aspeed_smc_chip *chip) +{ + return BIT(chip->controller->info->we0 + chip->cs); +} + +static void aspeed_smc_chip_check_config(struct aspeed_smc_chip *chip) +{ + struct aspeed_smc_controller *controller = chip->controller; + u32 reg; + + reg = readl(controller->regs + CONFIG_REG); + + if (reg & aspeed_smc_chip_write_bit(chip)) + return; + + dev_dbg(controller->dev, "config write is not set ! @%p: 0x%08x\n", + controller->regs + CONFIG_REG, reg); + reg |= aspeed_smc_chip_write_bit(chip); + writel(reg, controller->regs + CONFIG_REG); +} + +static void aspeed_smc_start_user(struct spi_nor *nor) +{ + struct aspeed_smc_chip *chip = nor->priv; + u32 ctl = chip->ctl_val[smc_base]; + + /* + * When the chip is controlled in user mode, we need write + * access to send the opcodes to it. So check the config. + */ + aspeed_smc_chip_check_config(chip); + + ctl |= CONTROL_COMMAND_MODE_USER | + CONTROL_CE_STOP_ACTIVE_CONTROL; + writel(ctl, chip->ctl); + + ctl &= ~CONTROL_CE_STOP_ACTIVE_CONTROL; + writel(ctl, chip->ctl); +} + +static void aspeed_smc_stop_user(struct spi_nor *nor) +{ + struct aspeed_smc_chip *chip = nor->priv; + + u32 ctl = chip->ctl_val[smc_read]; + u32 ctl2 = ctl | CONTROL_COMMAND_MODE_USER | + CONTROL_CE_STOP_ACTIVE_CONTROL; + + writel(ctl2, chip->ctl); /* stop user CE control */ + writel(ctl, chip->ctl); /* default to fread or read mode */ +} + +static int aspeed_smc_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct aspeed_smc_chip *chip = nor->priv; + + mutex_lock(&chip->controller->mutex); + return 0; +} + +static void aspeed_smc_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct aspeed_smc_chip *chip = nor->priv; + + mutex_unlock(&chip->controller->mutex); +} + +static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) +{ + struct aspeed_smc_chip *chip = nor->priv; + + aspeed_smc_start_user(nor); + aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1); + aspeed_smc_read_from_ahb(buf, chip->ahb_base, len); + aspeed_smc_stop_user(nor); + return 0; +} + +static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len) +{ + struct aspeed_smc_chip *chip = nor->priv; + + aspeed_smc_start_user(nor); + aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1); + aspeed_smc_write_to_ahb(chip->ahb_base, buf, len); + aspeed_smc_stop_user(nor); + return 0; +} + +static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr) +{ + struct aspeed_smc_chip *chip = nor->priv; + __be32 temp; + u32 cmdaddr; + + switch (nor->addr_width) { + default: + WARN_ONCE(1, "Unexpected address width %u, defaulting to 3\n", + nor->addr_width); + /* FALLTHROUGH */ + case 3: + cmdaddr = addr & 0xFFFFFF; + cmdaddr |= cmd << 24; + + temp = cpu_to_be32(cmdaddr); + aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4); + break; + case 4: + temp = cpu_to_be32(addr); + aspeed_smc_write_to_ahb(chip->ahb_base, &cmd, 1); + aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4); + break; + } +} + +static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from, + size_t len, u_char *read_buf) +{ + struct aspeed_smc_chip *chip = nor->priv; + int i; + u8 dummy = 0xFF; + + aspeed_smc_start_user(nor); + aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from); + for (i = 0; i < chip->nor.read_dummy / 8; i++) + aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy)); + + aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len); + aspeed_smc_stop_user(nor); + return len; +} + +static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to, + size_t len, const u_char *write_buf) +{ + struct aspeed_smc_chip *chip = nor->priv; + + aspeed_smc_start_user(nor); + aspeed_smc_send_cmd_addr(nor, nor->program_opcode, to); + aspeed_smc_write_to_ahb(chip->ahb_base, write_buf, len); + aspeed_smc_stop_user(nor); + return len; +} + +static int aspeed_smc_unregister(struct aspeed_smc_controller *controller) +{ + struct aspeed_smc_chip *chip; + int n; + + for (n = 0; n < controller->info->nce; n++) { + chip = controller->chips[n]; + if (chip) + mtd_device_unregister(&chip->nor.mtd); + } + + return 0; +} + +static int aspeed_smc_remove(struct platform_device *dev) +{ + return aspeed_smc_unregister(platform_get_drvdata(dev)); +} + +static const struct of_device_id aspeed_smc_matches[] = { + { .compatible = "aspeed,ast2400-fmc", .data = &fmc_2400_info }, + { .compatible = "aspeed,ast2400-spi", .data = &spi_2400_info }, + { .compatible = "aspeed,ast2500-fmc", .data = &fmc_2500_info }, + { .compatible = "aspeed,ast2500-spi", .data = &spi_2500_info }, + { } +}; +MODULE_DEVICE_TABLE(of, aspeed_smc_matches); + +/* + * Each chip has a mapping window defined by a segment address + * register defining a start and an end address on the AHB bus. These + * addresses can be configured to fit the chip size and offer a + * contiguous memory region across chips. For the moment, we only + * check that each chip segment is valid. + */ +static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip, + struct resource *res) +{ + struct aspeed_smc_controller *controller = chip->controller; + u32 offset = 0; + u32 reg; + + if (controller->info->nce > 1) { + reg = readl(controller->regs + SEGMENT_ADDR_REG0 + + chip->cs * 4); + + if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg)) + return NULL; + + offset = SEGMENT_ADDR_START(reg) - res->start; + } + + return controller->ahb_base + offset; +} + +static void aspeed_smc_chip_enable_write(struct aspeed_smc_chip *chip) +{ + struct aspeed_smc_controller *controller = chip->controller; + u32 reg; + + reg = readl(controller->regs + CONFIG_REG); + + reg |= aspeed_smc_chip_write_bit(chip); + writel(reg, controller->regs + CONFIG_REG); +} + +static void aspeed_smc_chip_set_type(struct aspeed_smc_chip *chip, int type) +{ + struct aspeed_smc_controller *controller = chip->controller; + u32 reg; + + chip->type = type; + + reg = readl(controller->regs + CONFIG_REG); + reg &= ~(3 << (chip->cs * 2)); + reg |= chip->type << (chip->cs * 2); + writel(reg, controller->regs + CONFIG_REG); +} + +/* + * The AST2500 FMC flash controller should be strapped by hardware, or + * autodetected, but the AST2500 SPI flash needs to be set. + */ +static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip) +{ + struct aspeed_smc_controller *controller = chip->controller; + u32 reg; + + if (chip->controller->info == &spi_2500_info) { + reg = readl(controller->regs + CE_CONTROL_REG); + reg |= 1 << chip->cs; + writel(reg, controller->regs + CE_CONTROL_REG); + } +} + +/* + * The AST2400 SPI flash controller does not have a CE Control + * register. It uses the CE0 control register to set 4Byte mode at the + * controller level. + */ +static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip) +{ + chip->ctl_val[smc_base] |= CONTROL_IO_ADDRESS_4B; + chip->ctl_val[smc_read] |= CONTROL_IO_ADDRESS_4B; +} + +static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip, + struct resource *res) +{ + struct aspeed_smc_controller *controller = chip->controller; + const struct aspeed_smc_info *info = controller->info; + u32 reg, base_reg; + + /* + * Always turn on the write enable bit to allow opcodes to be + * sent in user mode. + */ + aspeed_smc_chip_enable_write(chip); + + /* The driver only supports SPI type flash */ + if (info->hastype) + aspeed_smc_chip_set_type(chip, smc_type_spi); + + /* + * Configure chip base address in memory + */ + chip->ahb_base = aspeed_smc_chip_base(chip, res); + if (!chip->ahb_base) { + dev_warn(chip->nor.dev, "CE segment window closed.\n"); + return -EINVAL; + } + + /* + * Get value of the inherited control register. U-Boot usually + * does some timing calibration on the FMC chip, so it's good + * to keep them. In the future, we should handle calibration + * from Linux. + */ + reg = readl(chip->ctl); + dev_dbg(controller->dev, "control register: %08x\n", reg); + + base_reg = reg & CONTROL_KEEP_MASK; + if (base_reg != reg) { + dev_dbg(controller->dev, + "control register changed to: %08x\n", + base_reg); + } + chip->ctl_val[smc_base] = base_reg; + + /* + * Retain the prior value of the control register as the + * default if it was normal access mode. Otherwise start with + * the sanitized base value set to read mode. + */ + if ((reg & CONTROL_COMMAND_MODE_MASK) == + CONTROL_COMMAND_MODE_NORMAL) + chip->ctl_val[smc_read] = reg; + else + chip->ctl_val[smc_read] = chip->ctl_val[smc_base] | + CONTROL_COMMAND_MODE_NORMAL; + + dev_dbg(controller->dev, "default control register: %08x\n", + chip->ctl_val[smc_read]); + return 0; +} + +static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip) +{ + struct aspeed_smc_controller *controller = chip->controller; + const struct aspeed_smc_info *info = controller->info; + u32 cmd; + + if (chip->nor.addr_width == 4 && info->set_4b) + info->set_4b(chip); + + /* + * base mode has not been optimized yet. use it for writes. + */ + chip->ctl_val[smc_write] = chip->ctl_val[smc_base] | + chip->nor.program_opcode << CONTROL_COMMAND_SHIFT | + CONTROL_COMMAND_MODE_WRITE; + + dev_dbg(controller->dev, "write control register: %08x\n", + chip->ctl_val[smc_write]); + + /* + * TODO: Adjust clocks if fast read is supported and interpret + * SPI-NOR flags to adjust controller settings. + */ + switch (chip->nor.flash_read) { + case SPI_NOR_NORMAL: + cmd = CONTROL_COMMAND_MODE_NORMAL; + break; + case SPI_NOR_FAST: + cmd = CONTROL_COMMAND_MODE_FREAD; + break; + default: + dev_err(chip->nor.dev, "unsupported SPI read mode\n"); + return -EINVAL; + } + + chip->ctl_val[smc_read] |= cmd | + CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8); + + dev_dbg(controller->dev, "base control register: %08x\n", + chip->ctl_val[smc_read]); + return 0; +} + +static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, + struct device_node *np, struct resource *r) +{ + const struct aspeed_smc_info *info = controller->info; + struct device *dev = controller->dev; + struct device_node *child; + unsigned int cs; + int ret = -ENODEV; + + for_each_available_child_of_node(np, child) { + struct aspeed_smc_chip *chip; + struct spi_nor *nor; + struct mtd_info *mtd; + + /* This driver does not support NAND or NOR flash devices. */ + if (!of_device_is_compatible(child, "jedec,spi-nor")) + continue; + + ret = of_property_read_u32(child, "reg", &cs); + if (ret) { + dev_err(dev, "Couldn't not read chip select.\n"); + break; + } + + if (cs >= info->nce) { + dev_err(dev, "Chip select %d out of range.\n", + cs); + ret = -ERANGE; + break; + } + + if (controller->chips[cs]) { + dev_err(dev, "Chip select %d already in use by %s\n", + cs, dev_name(controller->chips[cs]->nor.dev)); + ret = -EBUSY; + break; + } + + chip = devm_kzalloc(controller->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + break; + } + + chip->controller = controller; + chip->ctl = controller->regs + info->ctl0 + cs * 4; + chip->cs = cs; + + nor = &chip->nor; + mtd = &nor->mtd; + + nor->dev = dev; + nor->priv = chip; + spi_nor_set_flash_node(nor, child); + nor->read = aspeed_smc_read_user; + nor->write = aspeed_smc_write_user; + nor->read_reg = aspeed_smc_read_reg; + nor->write_reg = aspeed_smc_write_reg; + nor->prepare = aspeed_smc_prep; + nor->unprepare = aspeed_smc_unprep; + + ret = aspeed_smc_chip_setup_init(chip, r); + if (ret) + break; + + /* + * TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL + * attach when board support is present as determined + * by of property. + */ + ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL); + if (ret) + break; + + ret = aspeed_smc_chip_setup_finish(chip); + if (ret) + break; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + break; + + controller->chips[cs] = chip; + } + + if (ret) + aspeed_smc_unregister(controller); + + return ret; +} + +static int aspeed_smc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct aspeed_smc_controller *controller; + const struct of_device_id *match; + const struct aspeed_smc_info *info; + struct resource *res; + int ret; + + match = of_match_device(aspeed_smc_matches, &pdev->dev); + if (!match || !match->data) + return -ENODEV; + info = match->data; + + controller = devm_kzalloc(&pdev->dev, sizeof(*controller) + + info->nce * sizeof(controller->chips[0]), GFP_KERNEL); + if (!controller) + return -ENOMEM; + controller->info = info; + controller->dev = dev; + + mutex_init(&controller->mutex); + platform_set_drvdata(pdev, controller); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + controller->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(controller->regs)) { + dev_err(dev, "Cannot remap controller address.\n"); + return PTR_ERR(controller->regs); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + controller->ahb_base = devm_ioremap_resource(dev, res); + if (IS_ERR(controller->ahb_base)) { + dev_err(dev, "Cannot remap controller address.\n"); + return PTR_ERR(controller->ahb_base); + } + + ret = aspeed_smc_setup_flash(controller, np, res); + if (ret) + dev_err(dev, "Aspeed SMC probe failed %d\n", ret); + + return ret; +} + +static struct platform_driver aspeed_smc_driver = { + .probe = aspeed_smc_probe, + .remove = aspeed_smc_remove, + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_smc_matches, + } +}; + +module_platform_driver(aspeed_smc_driver); + +MODULE_DESCRIPTION("ASPEED Static Memory Controller Driver"); +MODULE_AUTHOR("Cedric Le Goater <clg@kaod.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index d489fbd07c12..3fb7be857bb6 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -526,7 +526,8 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, bytes_to_read *= cqspi->fifo_width; bytes_to_read = bytes_to_read > remaining ? remaining : bytes_to_read; - readsl(ahb_base, rxbuf, DIV_ROUND_UP(bytes_to_read, 4)); + ioread32_rep(ahb_base, rxbuf, + DIV_ROUND_UP(bytes_to_read, 4)); rxbuf += bytes_to_read; remaining -= bytes_to_read; bytes_to_read = cqspi_get_rd_sram_level(cqspi); @@ -610,7 +611,8 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, while (remaining > 0) { write_bytes = remaining > page_size ? page_size : remaining; - writesl(cqspi->ahb_base, txbuf, DIV_ROUND_UP(write_bytes, 4)); + iowrite32_rep(cqspi->ahb_base, txbuf, + DIV_ROUND_UP(write_bytes, 4)); ret = wait_for_completion_timeout(&cqspi->transfer_complete, msecs_to_jiffies diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index da7cd69d4857..5f8b475255cd 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -75,6 +75,16 @@ struct flash_info { * bit. Must be used with * SPI_NOR_HAS_LOCK. */ +#define SPI_S3AN BIT(10) /* + * Xilinx Spartan 3AN In-System Flash + * (MFR cannot be used for probing + * because it has the same value as + * ATMEL flashes) + */ +#define SPI_NOR_4B_OPCODES BIT(11) /* + * Use dedicated 4byte address op codes + * to support memory size above 128Mib. + */ }; #define JEDEC_MFR(info) ((info)->id[0]) @@ -188,6 +198,78 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) return mtd->priv; } + +static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + if (table[i][0] == opcode) + return table[i][1]; + + /* No conversion found, keep input op code. */ + return opcode; +} + +static inline u8 spi_nor_convert_3to4_read(u8 opcode) +{ + static const u8 spi_nor_3to4_read[][2] = { + { SPINOR_OP_READ, SPINOR_OP_READ_4B }, + { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B }, + { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B }, + { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B }, + { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B }, + { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B }, + }; + + return spi_nor_convert_opcode(opcode, spi_nor_3to4_read, + ARRAY_SIZE(spi_nor_3to4_read)); +} + +static inline u8 spi_nor_convert_3to4_program(u8 opcode) +{ + static const u8 spi_nor_3to4_program[][2] = { + { SPINOR_OP_PP, SPINOR_OP_PP_4B }, + { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B }, + { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B }, + }; + + return spi_nor_convert_opcode(opcode, spi_nor_3to4_program, + ARRAY_SIZE(spi_nor_3to4_program)); +} + +static inline u8 spi_nor_convert_3to4_erase(u8 opcode) +{ + static const u8 spi_nor_3to4_erase[][2] = { + { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B }, + { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B }, + { SPINOR_OP_SE, SPINOR_OP_SE_4B }, + }; + + return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase, + ARRAY_SIZE(spi_nor_3to4_erase)); +} + +static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, + const struct flash_info *info) +{ + /* Do some manufacturer fixups first */ + switch (JEDEC_MFR(info)) { + case SNOR_MFR_SPANSION: + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = SPINOR_OP_SE; + nor->mtd.erasesize = info->sector_size; + break; + + default: + break; + } + + nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); + nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode); + nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); +} + /* Enable/disable 4-byte addressing mode. */ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, int enable) @@ -217,6 +299,21 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); } } + +static int s3an_sr_ready(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; + } + + return !!(val & XSR_RDY); +} + static inline int spi_nor_sr_ready(struct spi_nor *nor) { int sr = read_sr(nor); @@ -238,7 +335,11 @@ static inline int spi_nor_fsr_ready(struct spi_nor *nor) static int spi_nor_ready(struct spi_nor *nor) { int sr, fsr; - sr = spi_nor_sr_ready(nor); + + if (nor->flags & SNOR_F_READY_XSR_RDY) + sr = s3an_sr_ready(nor); + else + sr = spi_nor_sr_ready(nor); if (sr < 0) return sr; fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; @@ -320,6 +421,24 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) } /* + * This code converts an address to the Default Address Mode, that has non + * power of two page sizes. We must support this mode because it is the default + * mode supported by Xilinx tools, it can access the whole flash area and + * changing over to the Power-of-two mode is irreversible and corrupts the + * original data. + * Addr can safely be unsigned int, the biggest S3AN device is smaller than + * 4 MiB. + */ +static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) +{ + unsigned int offset = addr; + + offset %= nor->page_size; + + return ((addr - offset) << 1) | offset; +} + +/* * Initiate the erasure of a single sector */ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) @@ -327,6 +446,9 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; int i; + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); + if (nor->erase) return nor->erase(nor, addr); @@ -368,7 +490,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; /* whole-chip erase? */ - if (len == mtd->size) { + if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { unsigned long timeout; write_enable(nor); @@ -782,6 +904,19 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) .addr_width = (_addr_width), \ .flags = (_flags), +#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff \ + }, \ + .id_len = 3, \ + .sector_size = (8*_page_size), \ + .n_sectors = (_n_sectors), \ + .page_size = _page_size, \ + .addr_width = 3, \ + .flags = SPI_NOR_NO_FR | SPI_S3AN, + /* 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. @@ -821,7 +956,7 @@ static const struct flash_info spi_nor_ids[] = { { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, /* ESMT */ - { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, /* Everspin */ { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, @@ -1014,6 +1149,13 @@ static const struct flash_info spi_nor_ids[] = { { "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) }, + + /* Xilinx S3AN Internal Flash */ + { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, + { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, + { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, + { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, + { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, { }, }; @@ -1054,7 +1196,12 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, return ret; while (len) { - ret = nor->read(nor, from, len, buf); + loff_t addr = from; + + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); + + ret = nor->read(nor, addr, len, buf); if (ret == 0) { /* We shouldn't see 0-length reads */ ret = -EIO; @@ -1175,17 +1322,32 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, for (i = 0; i < len; ) { ssize_t written; + loff_t addr = to + i; - page_offset = (to + i) & (nor->page_size - 1); - WARN_ONCE(page_offset, - "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.", - page_offset); + /* + * If page_size is a power of two, the offset can be quickly + * calculated with an AND operation. On the other cases we + * need to do a modulus operation (more expensive). + * Power of two numbers have only one bit set and we can use + * the instruction hweight32 to detect if we need to do a + * modulus (do_div()) or not. + */ + if (hweight32(nor->page_size) == 1) { + page_offset = addr & (nor->page_size - 1); + } else { + uint64_t aux = addr; + + page_offset = do_div(aux, nor->page_size); + } /* the size of data remaining on the first page */ page_remain = min_t(size_t, nor->page_size - page_offset, len - i); + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) + addr = spi_nor_s3an_addr_convert(nor, addr); + write_enable(nor); - ret = nor->write(nor, to + i, page_remain, buf + i); + ret = nor->write(nor, addr, page_remain, buf + i); if (ret < 0) goto write_err; written = ret; @@ -1216,6 +1378,9 @@ static int macronix_quad_enable(struct spi_nor *nor) val = read_sr(nor); if (val < 0) return val; + if (val & SR_QUAD_EN_MX) + return 0; + write_enable(nor); write_sr(nor, val | SR_QUAD_EN_MX); @@ -1312,6 +1477,47 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } +static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); + return ret; + } + + nor->erase_opcode = SPINOR_OP_XSE; + nor->program_opcode = SPINOR_OP_XPP; + nor->read_opcode = SPINOR_OP_READ; + nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; + + /* + * This flashes have a page size of 264 or 528 bytes (known as + * Default addressing mode). It can be changed to a more standard + * Power of two mode where the page size is 256/512. This comes + * with a price: there is 3% less of space, the data is corrupted + * and the page size cannot be changed back to default addressing + * mode. + * + * The current addressing mode can be read from the XRDSR register + * and should not be changed, because is a destructive operation. + */ + if (val & XSR_PAGESIZE) { + /* Flash in Power of 2 mode */ + nor->page_size = (nor->page_size == 264) ? 256 : 512; + nor->mtd.writebufsize = nor->page_size; + nor->mtd.size = 8 * nor->page_size * info->n_sectors; + nor->mtd.erasesize = 8 * nor->page_size; + } else { + /* Flash in Default addressing mode */ + nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT; + } + + return 0; +} + int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) { const struct flash_info *info = NULL; @@ -1360,6 +1566,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) mutex_init(&nor->lock); /* + * Make sure the XSR_RDY flag is set before calling + * spi_nor_wait_till_ready(). Xilinx S3AN share MFR + * with Atmel spi-nor + */ + if (info->flags & SPI_S3AN) + nor->flags |= SNOR_F_READY_XSR_RDY; + + /* * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up * with the software protection bits set */ @@ -1483,27 +1697,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) else if (mtd->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width = 4; - if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) { - /* 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 + if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || + info->flags & SPI_NOR_4B_OPCODES) + spi_nor_set_4byte_opcodes(nor, info); + else set_4byte(nor, info, 1); } else { nor->addr_width = 3; @@ -1517,6 +1714,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) nor->read_dummy = spi_nor_read_dummy_cycles(nor); + if (info->flags & SPI_S3AN) { + ret = s3an_nor_scan(info, nor); + if (ret) + return ret; + } + dev_info(dev, "%s (%lld Kbytes)\n", info->name, (long long)mtd->size >> 10); diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 14f9dea3173f..d7843fd8c610 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -371,7 +371,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, /* default mode, does not need flex_cmd */ flex_mode = 0; else - command = SPINOR_OP_READ4_FAST; + command = SPINOR_OP_READ_FAST_4B; break; case SPI_NBITS_DUAL: bpc = 0x00000001; @@ -384,7 +384,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, } else { command = SPINOR_OP_READ_1_1_2; if (spans_4byte) - command = SPINOR_OP_READ4_1_1_2; + command = SPINOR_OP_READ_1_1_2_4B; } break; case SPI_NBITS_QUAD: @@ -399,7 +399,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, } else { command = SPINOR_OP_READ_1_1_4; if (spans_4byte) - command = SPINOR_OP_READ4_1_1_4; + command = SPINOR_OP_READ_1_1_4_4B; } break; default: diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index c425c7b4c2a0..f2a718030476 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -43,9 +43,13 @@ #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_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */ +#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */ +#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */ +#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */ +#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */ #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 */ @@ -56,11 +60,17 @@ #define SPINOR_OP_RDFSR 0x70 /* Read flag status 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_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */ +#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */ +#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */ +#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */ +#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */ +#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */ +#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */ +#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ /* Used for SST flashes only. */ @@ -68,6 +78,15 @@ #define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ +/* Used for S3AN flashes only */ +#define SPINOR_OP_XSE 0x50 /* Sector erase */ +#define SPINOR_OP_XPP 0x82 /* Page program */ +#define SPINOR_OP_XRDSR 0xd7 /* Read status register */ + +#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ +#define XSR_RDY BIT(7) /* Ready */ + + /* Used for Macronix and Winbond flashes. */ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ @@ -119,6 +138,9 @@ enum spi_nor_ops { enum spi_nor_option_flags { SNOR_F_USE_FSR = BIT(0), SNOR_F_HAS_SR_TB = BIT(1), + SNOR_F_NO_OP_CHIP_ERASE = BIT(2), + SNOR_F_S3AN_ADDR_DEFAULT = BIT(3), + SNOR_F_READY_XSR_RDY = BIT(4), }; /** |