diff options
Diffstat (limited to 'drivers/mtd/spi-nor')
-rw-r--r-- | drivers/mtd/spi-nor/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/controllers/aspeed-smc.c | 921 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/core.c | 286 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/core.h | 123 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/debugfs.c | 249 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/eon.c | 3 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/micron-st.c | 139 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 159 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 45 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/xilinx.c | 12 |
12 files changed, 709 insertions, 1240 deletions
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 6b904e439372..e347b435a038 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -17,6 +17,7 @@ spi-nor-objs += sst.o spi-nor-objs += winbond.o spi-nor-objs += xilinx.o spi-nor-objs += xmc.o +spi-nor-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_MTD_SPI_NOR) += controllers/ diff --git a/drivers/mtd/spi-nor/controllers/Kconfig b/drivers/mtd/spi-nor/controllers/Kconfig index 50f4f3484d42..ca45dcd3ffe8 100644 --- a/drivers/mtd/spi-nor/controllers/Kconfig +++ b/drivers/mtd/spi-nor/controllers/Kconfig @@ -1,14 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -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_HISI_SFC tristate "Hisilicon FMC SPI NOR Flash Controller(SFC)" depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/mtd/spi-nor/controllers/Makefile b/drivers/mtd/spi-nor/controllers/Makefile index 6e2a1dc68466..0b8e1d530913 100644 --- a/drivers/mtd/spi-nor/controllers/Makefile +++ b/drivers/mtd/spi-nor/controllers/Makefile @@ -1,4 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c deleted file mode 100644 index acfe010f9dd7..000000000000 --- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c +++ /dev/null @@ -1,921 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * ASPEED Static Memory Controller driver - * - * Copyright (c) 2015-2016, IBM Corporation. - */ - -#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/sizes.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 ahb_window_size; /* chip mapping window size */ - 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 */ - u32 ahb_window_size; /* full mapping window size */ - - struct aspeed_smc_chip *chips[]; /* 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_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) -#define SEGMENT_ADDR_VALUE(start, end) \ - (((((start) >> 23) & 0xFF) << 16) | ((((end) >> 23) & 0xFF) << 24)) -#define SEGMENT_ADDR_REG(controller, cs) \ - ((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4) - -/* - * 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, 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) -{ - struct aspeed_smc_chip *chip = nor->priv; - - mutex_lock(&chip->controller->mutex); - return 0; -} - -static void aspeed_smc_unprep(struct spi_nor *nor) -{ - 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, - size_t 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, const u8 *buf, - size_t 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(SEGMENT_ADDR_REG(controller, chip->cs)); - - if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg)) - return NULL; - - offset = SEGMENT_ADDR_START(reg) - res->start; - } - - return controller->ahb_base + offset; -} - -static u32 aspeed_smc_ahb_base_phy(struct aspeed_smc_controller *controller) -{ - u32 seg0_val = readl(SEGMENT_ADDR_REG(controller, 0)); - - return SEGMENT_ADDR_START(seg0_val); -} - -static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start, - u32 size) -{ - struct aspeed_smc_controller *controller = chip->controller; - void __iomem *seg_reg; - u32 seg_oldval, seg_newval, ahb_base_phy, end; - - ahb_base_phy = aspeed_smc_ahb_base_phy(controller); - - seg_reg = SEGMENT_ADDR_REG(controller, cs); - seg_oldval = readl(seg_reg); - - /* - * If the chip size is not specified, use the default segment - * size, but take into account the possible overlap with the - * previous segment - */ - if (!size) - size = SEGMENT_ADDR_END(seg_oldval) - start; - - /* - * The segment cannot exceed the maximum window size of the - * controller. - */ - if (start + size > ahb_base_phy + controller->ahb_window_size) { - size = ahb_base_phy + controller->ahb_window_size - start; - dev_warn(chip->nor.dev, "CE%d window resized to %dMB", - cs, size >> 20); - } - - end = start + size; - seg_newval = SEGMENT_ADDR_VALUE(start, end); - writel(seg_newval, seg_reg); - - /* - * Restore default value if something goes wrong. The chip - * might have set some bogus value and we would loose access - * to the chip. - */ - if (seg_newval != readl(seg_reg)) { - dev_err(chip->nor.dev, "CE%d window invalid", cs); - writel(seg_oldval, seg_reg); - start = SEGMENT_ADDR_START(seg_oldval); - end = SEGMENT_ADDR_END(seg_oldval); - size = end - start; - } - - dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB", - cs, start, end, size >> 20); - - return size; -} - -/* - * The segment register defines the mapping window on the AHB bus and - * it needs to be configured depending on the chip size. The segment - * register of the following CE also needs to be tuned in order to - * provide a contiguous window across multiple chips. - * - * This is expected to be called in increasing CE order - */ -static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip) -{ - struct aspeed_smc_controller *controller = chip->controller; - u32 ahb_base_phy, start; - u32 size = chip->nor.mtd.size; - - /* - * Each controller has a chip size limit for direct memory - * access - */ - if (size > controller->info->maxsize) - size = controller->info->maxsize; - - /* - * The AST2400 SPI controller only handles one chip and does - * not have segment registers. Let's use the chip size for the - * AHB window. - */ - if (controller->info == &spi_2400_info) - goto out; - - /* - * The AST2500 SPI controller has a HW bug when the CE0 chip - * size reaches 128MB. Enforce a size limit of 120MB to - * prevent the controller from using bogus settings in the - * segment register. - */ - if (chip->cs == 0 && controller->info == &spi_2500_info && - size == SZ_128M) { - size = 120 << 20; - dev_info(chip->nor.dev, - "CE%d window resized to %dMB (AST2500 HW quirk)", - chip->cs, size >> 20); - } - - ahb_base_phy = aspeed_smc_ahb_base_phy(controller); - - /* - * As a start address for the current segment, use the default - * start address if we are handling CE0 or use the previous - * segment ending address - */ - if (chip->cs) { - u32 prev = readl(SEGMENT_ADDR_REG(controller, chip->cs - 1)); - - start = SEGMENT_ADDR_END(prev); - } else { - start = ahb_base_phy; - } - - size = chip_set_segment(chip, chip->cs, start, size); - - /* Update chip base address on the AHB bus */ - chip->ahb_base = controller->ahb_base + (start - ahb_base_phy); - - /* - * Now, make sure the next segment does not overlap with the - * current one we just configured, even if there is no - * available chip. That could break access in Command Mode. - */ - if (chip->cs < controller->info->nce - 1) - chip_set_segment(chip, chip->cs + 1, start + size, 0); - -out: - if (size < chip->nor.mtd.size) - dev_warn(chip->nor.dev, - "CE%d window too small for chip %dMB", - chip->cs, (u32)chip->nor.mtd.size >> 20); - - return size; -} - -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 first chip of the AST2500 FMC flash controller is strapped by - * hardware, or autodetected, but other chips need to be set. Enforce - * the 4B setting for all chips. - */ -static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip) -{ - struct aspeed_smc_controller *controller = chip->controller; - u32 reg; - - 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%d window closed", chip->cs); - 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); - - /* This is for direct AHB access when using Command Mode. */ - chip->ahb_window_size = aspeed_smc_chip_set_segment(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. - */ - if (chip->nor.read_proto == SNOR_PROTO_1_1_1) { - if (chip->nor.read_dummy == 0) - cmd = CONTROL_COMMAND_MODE_NORMAL; - else - cmd = CONTROL_COMMAND_MODE_FREAD; - } else { - 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 const struct spi_nor_controller_ops aspeed_smc_controller_ops = { - .prepare = aspeed_smc_prep, - .unprepare = aspeed_smc_unprep, - .read_reg = aspeed_smc_read_reg, - .write_reg = aspeed_smc_write_reg, - .read = aspeed_smc_read_user, - .write = aspeed_smc_write_user, -}; - -static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller, - struct device_node *np, struct resource *r) -{ - const struct spi_nor_hwcaps hwcaps = { - .mask = SNOR_HWCAPS_READ | - SNOR_HWCAPS_READ_FAST | - SNOR_HWCAPS_PP, - }; - const struct aspeed_smc_info *info = controller->info; - struct device *dev = controller->dev; - struct device_node *child; - unsigned int cs; - int ret = -ENODEV; - bool found_one = false; - - 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->controller_ops = &aspeed_smc_controller_ops; - - ret = aspeed_smc_chip_setup_init(chip, r); - if (ret) - break; - - /* - * TODO: Add support for Dual and Quad SPI protocols - * attach when board support is present as determined - * by of property. - */ - ret = spi_nor_scan(nor, NULL, &hwcaps); - /* - * If we fail to scan the device it might not be present or - * broken. Don't fail the whole controller if others work. - */ - if (ret) { - if (found_one) - ret = 0; - - devm_kfree(controller->dev, chip); - continue; - } - - ret = aspeed_smc_chip_setup_finish(chip); - if (ret) - break; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) - break; - - controller->chips[cs] = chip; - found_one = true; - } - - if (ret) { - of_node_put(child); - 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, - struct_size(controller, chips, info->nce), - 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)) - 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)) - return PTR_ERR(controller->ahb_base); - - controller->ahb_window_size = resource_size(res); - - 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/core.c b/drivers/mtd/spi-nor/core.c index b4f141ad9c9c..502967c76c5f 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -308,6 +308,52 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, } /** + * spi_nor_read_any_reg() - read any register from flash memory, nonvolatile or + * volatile. + * @nor: pointer to 'struct spi_nor'. + * @op: SPI memory operation. op->data.buf must be DMA-able. + * @proto: SPI protocol to use for the register operation. + * + * Return: zero on success, -errno otherwise + */ +int spi_nor_read_any_reg(struct spi_nor *nor, struct spi_mem_op *op, + enum spi_nor_protocol proto) +{ + if (!nor->spimem) + return -EOPNOTSUPP; + + spi_nor_spimem_setup_op(nor, op, proto); + return spi_nor_spimem_exec_op(nor, op); +} + +/** + * spi_nor_write_any_volatile_reg() - write any volatile register to flash + * memory. + * @nor: pointer to 'struct spi_nor' + * @op: SPI memory operation. op->data.buf must be DMA-able. + * @proto: SPI protocol to use for the register operation. + * + * Writing volatile registers are instant according to some manufacturers + * (Cypress, Micron) and do not need any status polling. + * + * Return: zero on success, -errno otherwise + */ +int spi_nor_write_any_volatile_reg(struct spi_nor *nor, struct spi_mem_op *op, + enum spi_nor_protocol proto) +{ + int ret; + + if (!nor->spimem) + return -EOPNOTSUPP; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + spi_nor_spimem_setup_op(nor, op, proto); + return spi_nor_spimem_exec_op(nor, op); +} + +/** * spi_nor_write_enable() - Set write enable latch with Write Enable command. * @nor: pointer to 'struct spi_nor'. * @@ -318,11 +364,7 @@ int spi_nor_write_enable(struct spi_nor *nor) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPI_NOR_WREN_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -349,11 +391,7 @@ int spi_nor_write_disable(struct spi_nor *nor) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPI_NOR_WRDI_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -370,6 +408,37 @@ int spi_nor_write_disable(struct spi_nor *nor) } /** + * spi_nor_read_id() - Read the JEDEC ID. + * @nor: pointer to 'struct spi_nor'. + * @naddr: number of address bytes to send. Can be zero if the operation + * does not need to send an address. + * @ndummy: number of dummy bytes to send after an opcode or address. Can + * be zero if the operation does not require dummy bytes. + * @id: pointer to a DMA-able buffer where the value of the JEDEC ID + * will be written. + * @proto: the SPI protocol for register operation. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id, + enum spi_nor_protocol proto) +{ + int ret; + + if (nor->spimem) { + struct spi_mem_op op = + SPI_NOR_READID_OP(naddr, ndummy, id, SPI_NOR_MAX_ID_LEN); + + spi_nor_spimem_setup_op(nor, &op, proto); + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, + SPI_NOR_MAX_ID_LEN); + } + return ret; +} + +/** * spi_nor_read_sr() - Read the Status Register. * @nor: pointer to 'struct spi_nor'. * @sr: pointer to a DMA-able buffer where the value of the @@ -382,11 +451,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr, 0)); + struct spi_mem_op op = SPI_NOR_RDSR_OP(sr); if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { op.addr.nbytes = nor->params->rdsr_addr_nbytes; @@ -426,11 +491,7 @@ int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, cr, 0)); + struct spi_mem_op op = SPI_NOR_RDCR_OP(cr); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -459,14 +520,7 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? - SPINOR_OP_EN4B : - SPINOR_OP_EX4B, - 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPI_NOR_EN4B_EX4B_OP(enable); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -500,11 +554,7 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) nor->bouncebuf[0] = enable << 7; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0)); + struct spi_mem_op op = SPI_NOR_BRWR_OP(nor->bouncebuf); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -521,40 +571,6 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) } /** - * spi_nor_write_ear() - Write Extended Address Register. - * @nor: pointer to 'struct spi_nor'. - * @ear: value to write to the Extended Address Register. - * - * Return: 0 on success, -errno otherwise. - */ -int spi_nor_write_ear(struct spi_nor *nor, u8 ear) -{ - int ret; - - nor->bouncebuf[0] = ear; - - if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0)); - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREAR, - nor->bouncebuf, 1); - } - - if (ret) - dev_dbg(nor->dev, "error %d writing EAR\n", ret); - - return ret; -} - -/** * spi_nor_sr_ready() - Query the Status Register to see if the flash is ready * for new commands. * @nor: pointer to 'struct spi_nor'. @@ -649,11 +665,7 @@ int spi_nor_global_block_unlock(struct spi_nor *nor) return ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPI_NOR_GBULK_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -688,11 +700,7 @@ int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) return ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, sr, 0)); + struct spi_mem_op op = SPI_NOR_WRSR_OP(sr, len); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -788,6 +796,15 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) if (ret) return ret; + ret = spi_nor_read_sr(nor, sr_cr); + if (ret) + return ret; + + if (sr1 != sr_cr[0]) { + dev_dbg(nor->dev, "SR: Read back test failed\n"); + return -EIO; + } + if (nor->flags & SNOR_F_NO_READ_CR) return 0; @@ -892,11 +909,7 @@ static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) return ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, sr2, 0)); + struct spi_mem_op op = SPI_NOR_WRSR2_OP(sr2); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -928,11 +941,7 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr2, 0)); + struct spi_mem_op op = SPI_NOR_RDSR2_OP(sr2); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -961,11 +970,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor) dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP; spi_nor_spimem_setup_op(nor, &op, nor->write_proto); @@ -1107,10 +1112,8 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, addr, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode, + nor->addr_width, addr); spi_nor_spimem_setup_op(nor, &op, nor->write_proto); @@ -1629,58 +1632,45 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_xmc, }; -static const struct flash_info * -spi_nor_search_part_by_id(const struct flash_info *parts, unsigned int nparts, - const u8 *id) +static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, + const u8 *id) { - unsigned int i; + const struct flash_info *part; + unsigned int i, j; - for (i = 0; i < nparts; i++) { - if (parts[i].id_len && - !memcmp(parts[i].id, id, parts[i].id_len)) - return &parts[i]; + for (i = 0; i < ARRAY_SIZE(manufacturers); i++) { + for (j = 0; j < manufacturers[i]->nparts; j++) { + part = &manufacturers[i]->parts[j]; + if (part->id_len && + !memcmp(part->id, id, part->id_len)) { + nor->manufacturer = manufacturers[i]; + return part; + } + } } return NULL; } -static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) +static const struct flash_info *spi_nor_detect(struct spi_nor *nor) { const struct flash_info *info; u8 *id = nor->bouncebuf; - unsigned int i; int ret; - if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1)); - - ret = spi_mem_exec_op(nor->spimem, &op); - } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, - SPI_NOR_MAX_ID_LEN); - } + ret = spi_nor_read_id(nor, 0, 0, id, nor->reg_proto); if (ret) { dev_dbg(nor->dev, "error %d reading JEDEC ID\n", ret); return ERR_PTR(ret); } - for (i = 0; i < ARRAY_SIZE(manufacturers); i++) { - info = spi_nor_search_part_by_id(manufacturers[i]->parts, - manufacturers[i]->nparts, - id); - if (info) { - nor->manufacturer = manufacturers[i]; - return info; - } + info = spi_nor_match_id(nor, id); + if (!info) { + dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", + SPI_NOR_MAX_ID_LEN, id); + return ERR_PTR(-ENODEV); } - - dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", - SPI_NOR_MAX_ID_LEN, id); - return ERR_PTR(-ENODEV); + return info; } static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, @@ -1860,7 +1850,7 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps) ARRAY_SIZE(hwcaps_read2cmd)); } -static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) +int spi_nor_hwcaps_pp2cmd(u32 hwcaps) { static const int hwcaps_pp2cmd[][2] = { { SNOR_HWCAPS_PP, SNOR_CMD_PP }, @@ -1919,10 +1909,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor, static int spi_nor_spimem_check_readop(struct spi_nor *nor, const struct spi_nor_read_command *read) { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0), - SPI_MEM_OP_ADDR(3, 0, 0), - SPI_MEM_OP_DUMMY(1, 0), - SPI_MEM_OP_DATA_IN(2, NULL, 0)); + struct spi_mem_op op = SPI_NOR_READ_OP(read->opcode); spi_nor_spimem_setup_op(nor, &op, read->proto); @@ -1945,10 +1932,7 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor, static int spi_nor_spimem_check_pp(struct spi_nor *nor, const struct spi_nor_pp_command *pp) { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0), - SPI_MEM_OP_ADDR(3, 0, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(2, NULL, 0)); + struct spi_mem_op op = SPI_NOR_PP_OP(pp->opcode); spi_nor_spimem_setup_op(nor, &op, pp->proto); @@ -2772,10 +2756,7 @@ static void spi_nor_soft_reset(struct spi_nor *nor) struct spi_mem_op op; int ret; - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DATA); + op = (struct spi_mem_op)SPINOR_SRSTEN_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -2785,10 +2766,7 @@ static void spi_nor_soft_reset(struct spi_nor *nor) return; } - op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DATA); + op = (struct spi_mem_op)SPINOR_SRST_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -2876,8 +2854,8 @@ void spi_nor_restore(struct spi_nor *nor) } EXPORT_SYMBOL_GPL(spi_nor_restore); -static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, - const char *name) +static const struct flash_info *spi_nor_match_name(struct spi_nor *nor, + const char *name) { unsigned int i, j; @@ -2899,12 +2877,10 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, const struct flash_info *info = NULL; if (name) - info = spi_nor_match_id(nor, name); + info = spi_nor_match_name(nor, name); /* Try to auto-detect if chip name wasn't specified or not found */ if (!info) - info = spi_nor_read_id(nor); - if (IS_ERR_OR_NULL(info)) - return ERR_PTR(-ENOENT); + return spi_nor_detect(nor); /* * If caller has specified name of flash model that can normally be @@ -2913,7 +2889,7 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, if (name && info->id_len) { const struct flash_info *jinfo; - jinfo = spi_nor_read_id(nor); + jinfo = spi_nor_detect(nor); if (IS_ERR(jinfo)) { return jinfo; } else if (jinfo != info) { @@ -3156,6 +3132,8 @@ static int spi_nor_probe(struct spi_mem *spimem) if (ret) return ret; + spi_nor_debugfs_register(nor); + /* * None of the existing parts have > 512B pages, but let's play safe * and add this logic so that if anyone ever adds support for such diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index b7fd760e3b47..3f841ec36e56 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -11,6 +11,110 @@ #define SPI_NOR_MAX_ID_LEN 6 +/* Standard SPI NOR flash operations. */ +#define SPI_NOR_READID_OP(naddr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 0), \ + SPI_MEM_OP_ADDR(naddr, 0, 0), \ + SPI_MEM_OP_DUMMY(ndummy, 0), \ + SPI_MEM_OP_DATA_IN(len, buf, 0)) + +#define SPI_NOR_WREN_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPI_NOR_WRDI_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPI_NOR_RDSR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + +#define SPI_NOR_WRSR_OP(buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 0)) + +#define SPI_NOR_RDSR2_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 0)) + +#define SPI_NOR_WRSR2_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 0)) + +#define SPI_NOR_RDCR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + +#define SPI_NOR_EN4B_EX4B_OP(enable) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPI_NOR_BRWR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 0)) + +#define SPI_NOR_GBULK_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPI_NOR_CHIP_ERASE_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_width, addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ + SPI_MEM_OP_ADDR(addr_width, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPI_NOR_READ_OP(opcode) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ + SPI_MEM_OP_ADDR(3, 0, 0), \ + SPI_MEM_OP_DUMMY(1, 0), \ + SPI_MEM_OP_DATA_IN(2, NULL, 0)) + +#define SPI_NOR_PP_OP(opcode) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ + SPI_MEM_OP_ADDR(3, 0, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(2, NULL, 0)) + +#define SPINOR_SRSTEN_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DATA) + +#define SPINOR_SRST_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DATA) + +/* Keep these in sync with the list in debugfs.c */ enum spi_nor_option_flags { SNOR_F_HAS_SR_TB = BIT(0), SNOR_F_NO_OP_CHIP_ERASE = BIT(1), @@ -236,9 +340,10 @@ struct spi_nor_otp { * @writesize Minimal writable flash unit size. Defaults to 1. Set to * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. - * @rdsr_dummy: dummy cycles needed for Read Status Register command. + * @rdsr_dummy: dummy cycles needed for Read Status Register command + * in octal DTR mode. * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register - * command. + * command in octal DTR mode. * @hwcaps: describes the read and page program hardware * capabilities. * @reads: read capabilities ordered by priority: the higher index @@ -526,7 +631,6 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor, int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); -int spi_nor_write_ear(struct spi_nor *nor, u8 ear); int spi_nor_wait_till_ready(struct spi_nor *nor); int spi_nor_global_block_unlock(struct spi_nor *nor); int spi_nor_lock_and_prep(struct spi_nor *nor); @@ -534,6 +638,8 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor); int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); +int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id, + enum spi_nor_protocol reg_proto); int spi_nor_read_sr(struct spi_nor *nor, u8 *sr); int spi_nor_sr_ready(struct spi_nor *nor); int spi_nor_read_cr(struct spi_nor *nor, u8 *cr); @@ -545,6 +651,10 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf); ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, const u8 *buf); +int spi_nor_read_any_reg(struct spi_nor *nor, struct spi_mem_op *op, + enum spi_nor_protocol proto); +int spi_nor_write_any_volatile_reg(struct spi_nor *nor, struct spi_mem_op *op, + enum spi_nor_protocol proto); int spi_nor_erase_sector(struct spi_nor *nor, u32 addr); int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); @@ -555,6 +665,7 @@ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region); int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region); int spi_nor_hwcaps_read2cmd(u32 hwcaps); +int spi_nor_hwcaps_pp2cmd(u32 hwcaps); u8 spi_nor_convert_3to4_read(u8 opcode); void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, @@ -590,4 +701,10 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) return container_of(mtd, struct spi_nor, mtd); } +#ifdef CONFIG_DEBUG_FS +void spi_nor_debugfs_register(struct spi_nor *nor); +#else +static inline void spi_nor_debugfs_register(struct spi_nor *nor) {} +#endif + #endif /* __LINUX_MTD_SPI_NOR_INTERNAL_H */ diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c new file mode 100644 index 000000000000..eaf84f7a0676 --- /dev/null +++ b/drivers/mtd/spi-nor/debugfs.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/mtd/spi-nor.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> +#include <linux/debugfs.h> + +#include "core.h" + +#define SPI_NOR_DEBUGFS_ROOT "spi-nor" + +#define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name +static const char *const snor_f_names[] = { + SNOR_F_NAME(HAS_SR_TB), + SNOR_F_NAME(NO_OP_CHIP_ERASE), + SNOR_F_NAME(BROKEN_RESET), + SNOR_F_NAME(4B_OPCODES), + SNOR_F_NAME(HAS_4BAIT), + SNOR_F_NAME(HAS_LOCK), + SNOR_F_NAME(HAS_16BIT_SR), + SNOR_F_NAME(NO_READ_CR), + SNOR_F_NAME(HAS_SR_TB_BIT6), + SNOR_F_NAME(HAS_4BIT_BP), + SNOR_F_NAME(HAS_SR_BP3_BIT6), + SNOR_F_NAME(IO_MODE_EN_VOLATILE), + SNOR_F_NAME(SOFT_RESET), + SNOR_F_NAME(SWP_IS_VOLATILE), +}; +#undef SNOR_F_NAME + +static const char *spi_nor_protocol_name(enum spi_nor_protocol proto) +{ + switch (proto) { + case SNOR_PROTO_1_1_1: return "1S-1S-1S"; + case SNOR_PROTO_1_1_2: return "1S-1S-2S"; + case SNOR_PROTO_1_1_4: return "1S-1S-4S"; + case SNOR_PROTO_1_1_8: return "1S-1S-8S"; + case SNOR_PROTO_1_2_2: return "1S-2S-2S"; + case SNOR_PROTO_1_4_4: return "1S-4S-4S"; + case SNOR_PROTO_1_8_8: return "1S-8S-8S"; + case SNOR_PROTO_2_2_2: return "2S-2S-2S"; + case SNOR_PROTO_4_4_4: return "4S-4S-4S"; + case SNOR_PROTO_8_8_8: return "8S-8S-8S"; + case SNOR_PROTO_1_1_1_DTR: return "1D-1D-1D"; + case SNOR_PROTO_1_2_2_DTR: return "1D-2D-2D"; + case SNOR_PROTO_1_4_4_DTR: return "1D-4D-4D"; + case SNOR_PROTO_1_8_8_DTR: return "1D-8D-8D"; + case SNOR_PROTO_8_8_8_DTR: return "8D-8D-8D"; + } + + return "<unknown>"; +} + +static void spi_nor_print_flags(struct seq_file *s, unsigned long flags, + const char *const *names, int names_len) +{ + bool sep = false; + int i; + + for (i = 0; i < sizeof(flags) * BITS_PER_BYTE; i++) { + if (!(flags & BIT(i))) + continue; + if (sep) + seq_puts(s, " | "); + sep = true; + if (i < names_len && names[i]) + seq_puts(s, names[i]); + else + seq_printf(s, "1<<%d", i); + } +} + +static int spi_nor_params_show(struct seq_file *s, void *data) +{ + struct spi_nor *nor = s->private; + struct spi_nor_flash_parameter *params = nor->params; + struct spi_nor_erase_map *erase_map = ¶ms->erase_map; + struct spi_nor_erase_region *region; + const struct flash_info *info = nor->info; + char buf[16], *str; + int i; + + seq_printf(s, "name\t\t%s\n", info->name); + seq_printf(s, "id\t\t%*ph\n", info->id_len, info->id); + string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); + seq_printf(s, "size\t\t%s\n", buf); + seq_printf(s, "write size\t%u\n", params->writesize); + seq_printf(s, "page size\t%u\n", params->page_size); + seq_printf(s, "address width\t%u\n", nor->addr_width); + + seq_puts(s, "flags\t\t"); + spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names)); + seq_puts(s, "\n"); + + seq_puts(s, "\nopcodes\n"); + seq_printf(s, " read\t\t0x%02x\n", nor->read_opcode); + seq_printf(s, " dummy cycles\t%u\n", nor->read_dummy); + seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode); + seq_printf(s, " program\t0x%02x\n", nor->program_opcode); + + switch (nor->cmd_ext_type) { + case SPI_NOR_EXT_NONE: + str = "none"; + break; + case SPI_NOR_EXT_REPEAT: + str = "repeat"; + break; + case SPI_NOR_EXT_INVERT: + str = "invert"; + break; + default: + str = "<unknown>"; + break; + } + seq_printf(s, " 8D extension\t%s\n", str); + + seq_puts(s, "\nprotocols\n"); + seq_printf(s, " read\t\t%s\n", + spi_nor_protocol_name(nor->read_proto)); + seq_printf(s, " write\t\t%s\n", + spi_nor_protocol_name(nor->write_proto)); + seq_printf(s, " register\t%s\n", + spi_nor_protocol_name(nor->reg_proto)); + + seq_puts(s, "\nerase commands\n"); + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + struct spi_nor_erase_type *et = &erase_map->erase_type[i]; + + if (et->size) { + string_get_size(et->size, 1, STRING_UNITS_2, buf, + sizeof(buf)); + seq_printf(s, " %02x (%s) [%d]\n", et->opcode, buf, i); + } + } + + if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { + string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); + seq_printf(s, " %02x (%s)\n", SPINOR_OP_CHIP_ERASE, buf); + } + + seq_puts(s, "\nsector map\n"); + seq_puts(s, " region (in hex) | erase mask | flags\n"); + seq_puts(s, " ------------------+------------+----------\n"); + for (region = erase_map->regions; + region; + region = spi_nor_region_next(region)) { + u64 start = region->offset & ~SNOR_ERASE_FLAGS_MASK; + u64 flags = region->offset & SNOR_ERASE_FLAGS_MASK; + u64 end = start + region->size - 1; + + seq_printf(s, " %08llx-%08llx | [%c%c%c%c] | %s\n", + start, end, + flags & BIT(0) ? '0' : ' ', + flags & BIT(1) ? '1' : ' ', + flags & BIT(2) ? '2' : ' ', + flags & BIT(3) ? '3' : ' ', + flags & SNOR_OVERLAID_REGION ? "overlaid" : ""); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(spi_nor_params); + +static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap, + struct spi_nor_read_command *cmd) +{ + seq_printf(s, " %s%s\n", spi_nor_protocol_name(cmd->proto), + cap == SNOR_HWCAPS_READ_FAST ? " (fast read)" : ""); + seq_printf(s, " opcode\t0x%02x\n", cmd->opcode); + seq_printf(s, " mode cycles\t%u\n", cmd->num_mode_clocks); + seq_printf(s, " dummy cycles\t%u\n", cmd->num_wait_states); +} + +static void spi_nor_print_pp_cmd(struct seq_file *s, + struct spi_nor_pp_command *cmd) +{ + seq_printf(s, " %s\n", spi_nor_protocol_name(cmd->proto)); + seq_printf(s, " opcode\t0x%02x\n", cmd->opcode); +} + +static int spi_nor_capabilities_show(struct seq_file *s, void *data) +{ + struct spi_nor *nor = s->private; + struct spi_nor_flash_parameter *params = nor->params; + u32 hwcaps = params->hwcaps.mask; + int i, cmd; + + seq_puts(s, "Supported read modes by the flash\n"); + for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) { + if (!(hwcaps & BIT(i))) + continue; + + cmd = spi_nor_hwcaps_read2cmd(BIT(i)); + if (cmd < 0) + continue; + + spi_nor_print_read_cmd(s, BIT(i), ¶ms->reads[cmd]); + hwcaps &= ~BIT(i); + } + + seq_puts(s, "\nSupported page program modes by the flash\n"); + for (i = 0; i < sizeof(hwcaps) * BITS_PER_BYTE; i++) { + if (!(hwcaps & BIT(i))) + continue; + + cmd = spi_nor_hwcaps_pp2cmd(BIT(i)); + if (cmd < 0) + continue; + + spi_nor_print_pp_cmd(s, ¶ms->page_programs[cmd]); + hwcaps &= ~BIT(i); + } + + if (hwcaps) + seq_printf(s, "\nunknown hwcaps 0x%x\n", hwcaps); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(spi_nor_capabilities); + +static void spi_nor_debugfs_unregister(void *data) +{ + struct spi_nor *nor = data; + + debugfs_remove(nor->debugfs_root); + nor->debugfs_root = NULL; +} + +void spi_nor_debugfs_register(struct spi_nor *nor) +{ + struct dentry *rootdir, *d; + int ret; + + /* Create rootdir once. Will never be deleted again. */ + rootdir = debugfs_lookup(SPI_NOR_DEBUGFS_ROOT, NULL); + if (!rootdir) + rootdir = debugfs_create_dir(SPI_NOR_DEBUGFS_ROOT, NULL); + + ret = devm_add_action(nor->dev, spi_nor_debugfs_unregister, nor); + if (ret) + return; + + d = debugfs_create_dir(dev_name(nor->dev), rootdir); + nor->debugfs_root = d; + + debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops); + debugfs_create_file("capabilities", 0444, d, nor, + &spi_nor_capabilities_fops); +} diff --git a/drivers/mtd/spi-nor/eon.c b/drivers/mtd/spi-nor/eon.c index 8c1c57530281..50a11053711f 100644 --- a/drivers/mtd/spi-nor/eon.c +++ b/drivers/mtd/spi-nor/eon.c @@ -25,7 +25,8 @@ static const struct flash_info eon_nor_parts[] = { { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512) + PARSE_SFDP }, { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128) NO_SFDP_FLAGS(SECT_4K) }, }; diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 8a20475ce77a..a96f74e0f568 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -28,82 +28,87 @@ #define FSR_P_ERR BIT(4) /* Program operation status */ #define FSR_PT_ERR BIT(1) /* Protection error bit */ -static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +/* Micron ST SPI NOR flash operations. */ +#define MICRON_ST_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 0), \ + SPI_MEM_OP_ADDR(naddr, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) + +#define MICRON_ST_RDFSR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + +#define MICRON_ST_CLFSR_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +static int micron_st_nor_octal_dtr_en(struct spi_nor *nor) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; int ret; - if (enable) { - /* Use 20 dummy cycles for memory array reads. */ - ret = spi_nor_write_enable(nor); - if (ret) - return ret; - - *buf = 20; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, buf, 1)); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) - return ret; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; - } + /* Use 20 dummy cycles for memory array reads. */ + *buf = 20; + op = (struct spi_mem_op) + MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR1V, 1, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; - ret = spi_nor_write_enable(nor); + buf[0] = SPINOR_MT_OCT_DTR; + op = (struct spi_mem_op) + MICRON_ST_NOR_WR_ANY_REG_OP(3, SPINOR_REG_MT_CFR0V, 1, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) return ret; - if (enable) { - buf[0] = SPINOR_MT_OCT_DTR; - } else { - /* - * The register is 1-byte wide, but 1-byte transactions are not - * allowed in 8D-8D-8D mode. The next register is the dummy - * cycle configuration register. Since the transaction needs to - * be at least 2 bytes wide, set the next register to its - * default value. This also makes sense because the value was - * changed when enabling 8D-8D-8D mode, it should be reset when - * disabling. - */ - buf[0] = SPINOR_MT_EXSPI; - buf[1] = SPINOR_REG_MT_CFR1V_DEF; + /* Read flash ID to make sure the switch was successful. */ + ret = spi_nor_read_id(nor, 0, 8, buf, SNOR_PROTO_8_8_8_DTR); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret); + return ret; } - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(enable ? 3 : 4, - SPINOR_REG_MT_CFR0V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1)); + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; - if (!enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + return 0; +} - ret = spi_mem_exec_op(nor->spimem, &op); +static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + /* + * The register is 1-byte wide, but 1-byte transactions are not allowed + * in 8D-8D-8D mode. The next register is the dummy cycle configuration + * register. Since the transaction needs to be at least 2 bytes wide, + * set the next register to its default value. This also makes sense + * because the value was changed when enabling 8D-8D-8D mode, it should + * be reset when disabling. + */ + buf[0] = SPINOR_MT_EXSPI; + buf[1] = SPINOR_REG_MT_CFR1V_DEF; + op = (struct spi_mem_op) + MICRON_ST_NOR_WR_ANY_REG_OP(4, SPINOR_REG_MT_CFR0V, 2, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); if (ret) return ret; /* Read flash ID to make sure the switch was successful. */ - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1), - SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), - buf, 1)); - - if (enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) + ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret); return ret; + } if (memcmp(buf, nor->info->id, nor->info->id_len)) return -EINVAL; @@ -111,6 +116,12 @@ static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) return 0; } +static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + return enable ? micron_st_nor_octal_dtr_en(nor) : + micron_st_nor_octal_dtr_dis(nor); +} + static void mt35xu512aba_default_init(struct spi_nor *nor) { nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable; @@ -322,11 +333,7 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, fsr, 0)); + struct spi_mem_op op = MICRON_ST_RDFSR_OP(fsr); if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { op.addr.nbytes = nor->params->rdsr_addr_nbytes; @@ -361,11 +368,7 @@ static void micron_st_nor_clear_fsr(struct spi_nor *nor) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = MICRON_ST_CLFSR_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index f24e546e04a5..43cd6cd92537 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -23,94 +23,89 @@ #define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0 #define SPINOR_OP_CYPRESS_RD_FAST 0xee -/** - * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. - * @nor: pointer to a 'struct spi_nor' - * @enable: whether to enable or disable Octal DTR - * - * This also sets the memory access latency cycles to 24 to allow the flash to - * run at up to 200MHz. - * - * Return: 0 on success, -errno otherwise. - */ -static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +/* Cypress SPI NOR flash operations. */ +#define CYPRESS_NOR_WR_ANY_REG_OP(naddr, addr, ndata, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 0), \ + SPI_MEM_OP_ADDR(naddr, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) + +#define CYPRESS_NOR_RD_ANY_REG_OP(naddr, addr, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 0), \ + SPI_MEM_OP_ADDR(naddr, addr, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + +#define SPANSION_CLSR_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +static int cypress_nor_octal_dtr_en(struct spi_nor *nor) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; int ret; - if (enable) { - /* Use 24 dummy cycles for memory array reads. */ - ret = spi_nor_write_enable(nor); - if (ret) - return ret; - - *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V, - 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, buf, 1)); + /* Use 24 dummy cycles for memory array reads. */ + *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR2V, 1, buf); - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) - return ret; + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; - ret = spi_nor_wait_till_ready(nor); - if (ret) - return ret; + nor->read_dummy = 24; - nor->read_dummy = 24; - } + /* Set the octal and DTR enable bits. */ + buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR5V, 1, buf); - /* Set/unset the octal and DTR enable bits. */ - ret = spi_nor_write_enable(nor); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) return ret; - if (enable) { - buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; - } else { - /* - * The register is 1-byte wide, but 1-byte transactions are not - * allowed in 8D-8D-8D mode. Since there is no register at the - * next location, just initialize the value to 0 and let the - * transaction go on. - */ - buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; - buf[1] = 0; + /* Read flash ID to make sure the switch was successful. */ + ret = spi_nor_read_id(nor, 4, 3, buf, SNOR_PROTO_8_8_8_DTR); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after enabling 8D-8D-8D mode\n", ret); + return ret; } - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), - SPI_MEM_OP_ADDR(enable ? 3 : 4, - SPINOR_REG_CYPRESS_CFR5V, - 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1)); + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; - if (!enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + return 0; +} - ret = spi_mem_exec_op(nor->spimem, &op); +static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + /* + * The register is 1-byte wide, but 1-byte transactions are not allowed + * in 8D-8D-8D mode. Since there is no register at the next location, + * just initialize the value to 0 and let the transaction go on. + */ + buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; + buf[1] = 0; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(4, SPINOR_REG_CYPRESS_CFR5V, 2, buf); + ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); if (ret) return ret; /* Read flash ID to make sure the switch was successful. */ - op = (struct spi_mem_op) - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), - SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1), - SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1), - SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), - buf, 1)); - - if (enable) - spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) + ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID after disabling 8D-8D-8D mode\n", ret); return ret; + } if (memcmp(buf, nor->info->id, nor->info->id_len)) return -EINVAL; @@ -118,6 +113,22 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) return 0; } +/** + * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable or disable Octal DTR + * + * This also sets the memory access latency cycles to 24 to allow the flash to + * run at up to 200MHz. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + return enable ? cypress_nor_octal_dtr_en(nor) : + cypress_nor_octal_dtr_dis(nor); +} + static void s28hs512t_default_init(struct spi_nor *nor) { nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable; @@ -162,12 +173,12 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, * CFR3V[4] and set the correct size. */ struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1), - SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); + CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, + nor->bouncebuf); int ret; + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); if (ret) return ret; @@ -317,11 +328,7 @@ static void spansion_nor_clear_sr(struct spi_nor *nor) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_NO_DATA); + struct spi_mem_op op = SPANSION_CLSR_OP; spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index fe80dffc2e70..ffaa24055259 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -8,6 +8,15 @@ #include "core.h" +#define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ +#define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */ + +#define WINBOND_NOR_WREAR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 0)) + static int w25q256_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -124,12 +133,46 @@ static const struct flash_info winbond_nor_parts[] = { { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) }, + { "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024) + PARSE_SFDP + OTP_INFO(256, 3, 0x1000, 0x1000) }, { "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, }; /** + * winbond_nor_write_ear() - Write Extended Address Register. + * @nor: pointer to 'struct spi_nor'. + * @ear: value to write to the Extended Address Register. + * + * Return: 0 on success, -errno otherwise. + */ +static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear) +{ + int ret; + + nor->bouncebuf[0] = ear; + + if (nor->spimem) { + struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_write_reg(nor, + WINBOND_NOR_OP_WREAR, + nor->bouncebuf, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d writing EAR\n", ret); + + return ret; +} + +/** * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond * flashes. * @nor: pointer to 'struct spi_nor'. @@ -155,7 +198,7 @@ static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) if (ret) return ret; - ret = spi_nor_write_ear(nor, 0); + ret = winbond_nor_write_ear(nor, 0); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c index 9459ac2609dc..1d2f5db047bd 100644 --- a/drivers/mtd/spi-nor/xilinx.c +++ b/drivers/mtd/spi-nor/xilinx.c @@ -15,6 +15,12 @@ #define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ #define XSR_RDY BIT(7) /* Ready */ +#define XILINX_RDSR_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, buf, 0)) + #define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ .id = { \ ((_jedec_id) >> 16) & 0xff, \ @@ -72,11 +78,7 @@ static int xilinx_nor_read_sr(struct spi_nor *nor, u8 *sr) int ret; if (nor->spimem) { - struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(XILINX_OP_RDSR, 0), - SPI_MEM_OP_NO_ADDR, - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr, 0)); + struct spi_mem_op op = XILINX_RDSR_OP(sr); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); |