From 4844ef80305d0180051d0787cd91c63573255dc2 Mon Sep 17 00:00:00 2001 From: Vignesh Raghavendra Date: Tue, 25 Jun 2019 13:27:42 +0530 Subject: mtd: cfi_cmdset_0002: Add support for polling status register HyperFlash devices are compliant with CFI AMD/Fujitsu Extended Command Set (0x0002) for flash operations, therefore drivers/mtd/chips/cfi_cmdset_0002.c can be used as is. But these devices do not support DQ polling method of determining chip ready/good status. These flashes provide Status Register whose bits can be polled to know status of flash operation. Cypress HyperFlash datasheet here[1], talks about CFI Amd/Fujitsu Extended Query version 1.5. Bit 0 of "Software Features supported" field of CFI Primary Vendor-Specific Extended Query table indicates presence/absence of status register and Bit 1 indicates whether or not DQ polling is supported. Using these bits, its possible to determine whether flash supports DQ polling or need to use Status Register. Add support for polling Status Register to know device ready/status of erase/write operations when DQ polling is not supported. Print error messages on erase/program failure by looking at related Status Register bits. [1] https://www.cypress.com/file/213346/download Signed-off-by: Vignesh Raghavendra Reviewed-by: Tokunori Ikegami Signed-off-by: Miquel Raynal --- include/linux/mtd/cfi.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux/mtd') diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index cbf77168658c..7fdbc1ff6527 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -233,6 +233,13 @@ struct cfi_pri_amdstd { uint8_t VppMin; uint8_t VppMax; uint8_t TopBottom; + /* Below field are added from version 1.5 */ + uint8_t ProgramSuspend; + uint8_t UnlockBypass; + uint8_t SecureSiliconSector; + uint8_t SoftwareFeatures; +#define CFI_POLL_STATUS_REG BIT(0) +#define CFI_POLL_DQ BIT(1) } __packed; /* Vendor-Specific PRI for Atmel chips (command set 0x0002) */ -- cgit v1.2.3 From dcc7d3446a0fa19bd7e8074920b8f9ef3b7ec00c Mon Sep 17 00:00:00 2001 From: Vignesh Raghavendra Date: Tue, 25 Jun 2019 13:27:44 +0530 Subject: mtd: Add support for HyperBus memory devices Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate Bus interface between a host system master and one or more slave interfaces. HyperBus is used to connect microprocessor, microcontroller, or ASIC devices with random access NOR flash memory (called HyperFlash) or self refresh DRAM (called HyperRAM). Its a 8-bit data bus (DQ[7:0]) with Read-Write Data Strobe (RWDS) signal and either Single-ended clock(3.0V parts) or Differential clock (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves. At bus level, it follows a separate protocol described in HyperBus specification[1]. HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar to that of existing parallel NORs. Since HyperBus is x8 DDR bus, its equivalent to x16 parallel NOR flash with respect to bits per clock cycle. But HyperBus operates at >166MHz frequencies. HyperRAM provides direct random read/write access to flash memory array. But, HyperBus memory controllers seem to abstract implementation details and expose a simple MMIO interface to access connected flash. Add support for registering HyperFlash devices with MTD framework. MTD maps framework along with CFI chip support framework are used to support communicating with flash. Framework is modelled along the lines of spi-nor framework. HyperBus memory controller (HBMC) drivers calls hyperbus_register_device() to register a single HyperFlash device. HyperFlash core parses MMIO access information from DT, sets up the map_info struct, probes CFI flash and registers it with MTD framework. Some HBMC masters need calibration/training sequence[3] to be carried out, in order for DLL inside the controller to lock, by reading a known string/pattern. This is done by repeatedly reading CFI Query Identification String. Calibration needs to be done before trying to detect flash as part of CFI flash probe. HyperRAM is not supported at the moment. HyperBus specification can be found at[1] HyperFlash datasheet can be found at[2] [1] https://www.cypress.com/file/213356/download [2] https://www.cypress.com/file/213346/download [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf Table 12-5741. HyperFlash Access Sequence Signed-off-by: Vignesh Raghavendra Signed-off-by: Miquel Raynal --- MAINTAINERS | 7 ++ drivers/mtd/Kconfig | 2 + drivers/mtd/Makefile | 1 + drivers/mtd/hyperbus/Kconfig | 11 +++ drivers/mtd/hyperbus/Makefile | 3 + drivers/mtd/hyperbus/hyperbus-core.c | 153 +++++++++++++++++++++++++++++++++++ include/linux/mtd/hyperbus.h | 84 +++++++++++++++++++ 7 files changed, 261 insertions(+) create mode 100644 drivers/mtd/hyperbus/Kconfig create mode 100644 drivers/mtd/hyperbus/Makefile create mode 100644 drivers/mtd/hyperbus/hyperbus-core.c create mode 100644 include/linux/mtd/hyperbus.h (limited to 'include/linux/mtd') diff --git a/MAINTAINERS b/MAINTAINERS index 5cfbea4ce575..f1253adb8cf6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7305,6 +7305,13 @@ F: include/uapi/linux/hyperv.h F: tools/hv/ F: Documentation/ABI/stable/sysfs-bus-vmbus +HYPERBUS SUPPORT +M: Vignesh Raghavendra +S: Supported +F: drivers/mtd/hyperbus/ +F: include/linux/mtd/hyperbus.h +F: Documentation/devicetree/bindings/mtd/cypress,hyperflash.txt + HYPERVISOR VIRTUAL CONSOLE DRIVER L: linuxppc-dev@lists.ozlabs.org S: Odd Fixes diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fb31a7f649a3..80a6e2dcd085 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -274,4 +274,6 @@ source "drivers/mtd/spi-nor/Kconfig" source "drivers/mtd/ubi/Kconfig" +source "drivers/mtd/hyperbus/Kconfig" + endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 806287e80e84..62d649a959e2 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -34,3 +34,4 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ +obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig new file mode 100644 index 000000000000..98147e28caa0 --- /dev/null +++ b/drivers/mtd/hyperbus/Kconfig @@ -0,0 +1,11 @@ +menuconfig MTD_HYPERBUS + tristate "HyperBus support" + select MTD_CFI + select MTD_MAP_BANK_WIDTH_2 + select MTD_CFI_AMDSTD + select MTD_COMPLEX_MAPPINGS + help + This is the framework for the HyperBus which can be used by + the HyperBus Controller driver to communicate with + HyperFlash. See Cypress HyperBus specification for more + details diff --git a/drivers/mtd/hyperbus/Makefile b/drivers/mtd/hyperbus/Makefile new file mode 100644 index 000000000000..ca61dedd730d --- /dev/null +++ b/drivers/mtd/hyperbus/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c new file mode 100644 index 000000000000..6af9ea34117d --- /dev/null +++ b/drivers/mtd/hyperbus/hyperbus-core.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// Author: Vignesh Raghavendra + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct hyperbus_device *map_to_hbdev(struct map_info *map) +{ + return container_of(map, struct hyperbus_device, map); +} + +static map_word hyperbus_read16(struct map_info *map, unsigned long addr) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + map_word read_data; + + read_data.x[0] = ctlr->ops->read16(hbdev, addr); + + return read_data; +} + +static void hyperbus_write16(struct map_info *map, map_word d, + unsigned long addr) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + + ctlr->ops->write16(hbdev, addr, d.x[0]); +} + +static void hyperbus_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + + ctlr->ops->copy_from(hbdev, to, from, len); +} + +static void hyperbus_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + struct hyperbus_device *hbdev = map_to_hbdev(map); + struct hyperbus_ctlr *ctlr = hbdev->ctlr; + + ctlr->ops->copy_to(hbdev, to, from, len); +} + +int hyperbus_register_device(struct hyperbus_device *hbdev) +{ + const struct hyperbus_ops *ops; + struct hyperbus_ctlr *ctlr; + struct device_node *np; + struct map_info *map; + struct resource res; + struct device *dev; + int ret; + + if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) { + pr_err("hyperbus: please fill all the necessary fields!\n"); + return -EINVAL; + } + + np = hbdev->np; + ctlr = hbdev->ctlr; + if (!of_device_is_compatible(np, "cypress,hyperflash")) + return -ENODEV; + + hbdev->memtype = HYPERFLASH; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + + dev = ctlr->dev; + map = &hbdev->map; + map->size = resource_size(&res); + map->virt = devm_ioremap_resource(dev, &res); + if (IS_ERR(map->virt)) + return PTR_ERR(map->virt); + + map->name = dev_name(dev); + map->bankwidth = 2; + map->device_node = np; + + simple_map_init(map); + ops = ctlr->ops; + if (ops) { + if (ops->read16) + map->read = hyperbus_read16; + if (ops->write16) + map->write = hyperbus_write16; + if (ops->copy_to) + map->copy_to = hyperbus_copy_to; + if (ops->copy_from) + map->copy_from = hyperbus_copy_from; + + if (ops->calibrate && !ctlr->calibrated) { + ret = ops->calibrate(hbdev); + if (!ret) { + dev_err(dev, "Calibration failed\n"); + return -ENODEV; + } + ctlr->calibrated = true; + } + } + + hbdev->mtd = do_map_probe("cfi_probe", map); + if (!hbdev->mtd) { + dev_err(dev, "probing of hyperbus device failed\n"); + return -ENODEV; + } + + hbdev->mtd->dev.parent = dev; + mtd_set_of_node(hbdev->mtd, np); + + ret = mtd_device_register(hbdev->mtd, NULL, 0); + if (ret) { + dev_err(dev, "failed to register mtd device\n"); + map_destroy(hbdev->mtd); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hyperbus_register_device); + +int hyperbus_unregister_device(struct hyperbus_device *hbdev) +{ + int ret = 0; + + if (hbdev && hbdev->mtd) { + ret = mtd_device_unregister(hbdev->mtd); + map_destroy(hbdev->mtd); + } + + return ret; +} +EXPORT_SYMBOL_GPL(hyperbus_unregister_device); + +MODULE_DESCRIPTION("HyperBus Framework"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Vignesh Raghavendra "); diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h new file mode 100644 index 000000000000..2dfe65964f6e --- /dev/null +++ b/include/linux/mtd/hyperbus.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#ifndef __LINUX_MTD_HYPERBUS_H__ +#define __LINUX_MTD_HYPERBUS_H__ + +#include + +enum hyperbus_memtype { + HYPERFLASH, + HYPERRAM, +}; + +/** + * struct hyperbus_device - struct representing HyperBus slave device + * @map: map_info struct for accessing MMIO HyperBus flash memory + * @np: pointer to HyperBus slave device node + * @mtd: pointer to MTD struct + * @ctlr: pointer to HyperBus controller struct + * @memtype: type of memory device: HyperFlash or HyperRAM + */ + +struct hyperbus_device { + struct map_info map; + struct device_node *np; + struct mtd_info *mtd; + struct hyperbus_ctlr *ctlr; + enum hyperbus_memtype memtype; +}; + +/** + * struct hyperbus_ops - struct representing custom HyperBus operations + * @read16: read 16 bit of data from flash in a single burst. Used to read + * from non default address space, such as ID/CFI space + * @write16: write 16 bit of data to flash in a single burst. Used to + * send cmd to flash or write single 16 bit word at a time. + * @copy_from: copy data from flash memory + * @copy_to: copy data to flash memory + * @calibrate: calibrate HyperBus controller + */ + +struct hyperbus_ops { + u16 (*read16)(struct hyperbus_device *hbdev, unsigned long addr); + void (*write16)(struct hyperbus_device *hbdev, + unsigned long addr, u16 val); + void (*copy_from)(struct hyperbus_device *hbdev, void *to, + unsigned long from, ssize_t len); + void (*copy_to)(struct hyperbus_device *dev, unsigned long to, + const void *from, ssize_t len); + int (*calibrate)(struct hyperbus_device *dev); +}; + +/** + * struct hyperbus_ctlr - struct representing HyperBus controller + * @dev: pointer to HyperBus controller device + * @calibrated: flag to indicate ctlr calibration sequence is complete + * @ops: HyperBus controller ops + */ +struct hyperbus_ctlr { + struct device *dev; + bool calibrated; + + const struct hyperbus_ops *ops; +}; + +/** + * hyperbus_register_device - probe and register a HyperBus slave memory device + * @hbdev: hyperbus_device struct with dev, np and ctlr field populated + * + * Return: 0 for success, others for failure. + */ +int hyperbus_register_device(struct hyperbus_device *hbdev); + +/** + * hyperbus_unregister_device - deregister HyperBus slave memory device + * @hbdev: hyperbus_device to be unregistered + * + * Return: 0 for success, others for failure. + */ +int hyperbus_unregister_device(struct hyperbus_device *hbdev); + +#endif /* __LINUX_MTD_HYPERBUS_H__ */ -- cgit v1.2.3 From 855eff216a97afa4a2233b792cb3c812b5ebd876 Mon Sep 17 00:00:00 2001 From: Jonathan Bakker Date: Fri, 26 Apr 2019 17:06:34 +0200 Subject: mtd: onenand: Add support for 8Gb datasize onenand Used in several S5PV210-based Galaxy S devices, among them SGH-T959V, SGH-T959P, SGH-T839, and SPH-D700. Signed-off-by: Jonathan Bakker Signed-off-by: PaweÅ‚ Chmiel Signed-off-by: Miquel Raynal --- drivers/mtd/nand/onenand/onenand_base.c | 2 ++ include/linux/mtd/onenand_regs.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index f41d76248550..492c0059673d 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3260,6 +3260,8 @@ static void onenand_check_features(struct mtd_info *mtd) /* Lock scheme */ switch (density) { + case ONENAND_DEVICE_DENSITY_8Gb: + this->options |= ONENAND_HAS_NOP_1; case ONENAND_DEVICE_DENSITY_4Gb: if (ONENAND_IS_DDP(this)) this->options |= ONENAND_HAS_2PLANE; diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index d60130f88eed..9640d707cbf8 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -80,6 +80,7 @@ #define ONENAND_DEVICE_DENSITY_1Gb (0x003) #define ONENAND_DEVICE_DENSITY_2Gb (0x004) #define ONENAND_DEVICE_DENSITY_4Gb (0x005) +#define ONENAND_DEVICE_DENSITY_8Gb (0x006) /* * Version ID Register F002h (R) -- cgit v1.2.3 From 14a82ea7e1682645d942d9fb41fcb6126fd1645e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 21 May 2019 09:06:30 +0200 Subject: mtd: rawnand: export NAND operation tracer The NAND core has a NAND operation tracing function, but it can only be used by drivers using the generic option parser from the NAND core. Export the tracing function as a static inline function in rawnand.h so that drivers implementing exec_op directly do not have to write their own operation tracing. Signed-off-by: Sascha Hauer Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 30 +----------------------------- include/linux/mtd/rawnand.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 29 deletions(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index d565b4a9dce1..6ecd1c496ce3 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -2115,35 +2115,7 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) if (instr == &ctx->subop.instrs[0]) prefix = " ->"; - switch (instr->type) { - case NAND_OP_CMD_INSTR: - pr_debug("%sCMD [0x%02x]\n", prefix, - instr->ctx.cmd.opcode); - break; - case NAND_OP_ADDR_INSTR: - pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, - instr->ctx.addr.naddrs, - instr->ctx.addr.naddrs < 64 ? - instr->ctx.addr.naddrs : 64, - instr->ctx.addr.addrs); - break; - case NAND_OP_DATA_IN_INSTR: - pr_debug("%sDATA_IN [%d B%s]\n", prefix, - instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - break; - case NAND_OP_DATA_OUT_INSTR: - pr_debug("%sDATA_OUT [%d B%s]\n", prefix, - instr->ctx.data.len, - instr->ctx.data.force_8bit ? - ", force 8-bit" : ""); - break; - case NAND_OP_WAITRDY_INSTR: - pr_debug("%sWAITRDY [max %d ms]\n", prefix, - instr->ctx.waitrdy.timeout_ms); - break; - } + nand_op_trace(prefix, instr); if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) prefix = " "; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index dbfffa5bec7b..f5bb6f11c36b 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -877,6 +877,42 @@ int nand_op_parser_exec_op(struct nand_chip *chip, const struct nand_op_parser *parser, const struct nand_operation *op, bool check_only); +static inline void nand_op_trace(const char *prefix, + const struct nand_op_instr *instr) +{ +#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + switch (instr->type) { + case NAND_OP_CMD_INSTR: + pr_debug("%sCMD [0x%02x]\n", prefix, + instr->ctx.cmd.opcode); + break; + case NAND_OP_ADDR_INSTR: + pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, + instr->ctx.addr.naddrs, + instr->ctx.addr.naddrs < 64 ? + instr->ctx.addr.naddrs : 64, + instr->ctx.addr.addrs); + break; + case NAND_OP_DATA_IN_INSTR: + pr_debug("%sDATA_IN [%d B%s]\n", prefix, + instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + break; + case NAND_OP_DATA_OUT_INSTR: + pr_debug("%sDATA_OUT [%d B%s]\n", prefix, + instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + break; + case NAND_OP_WAITRDY_INSTR: + pr_debug("%sWAITRDY [max %d ms]\n", prefix, + instr->ctx.waitrdy.timeout_ms); + break; + } +#endif +} + /** * struct nand_controller_ops - Controller operations * -- cgit v1.2.3 From bded033062396e67ffbb3111084cf7ea202473d5 Mon Sep 17 00:00:00 2001 From: Jeff Kletsky Date: Wed, 22 May 2019 15:05:53 -0700 Subject: mtd: spinand: Define macros for page-read ops with three-byte addresses The GigaDevice GD5F1GQ4UFxxG SPI NAND utilizes three-byte addresses for its page-read ops. http://www.gigadevice.com/datasheet/gd5f1gq4xfxxg/ Signed-off-by: Jeff Kletsky Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal --- include/linux/mtd/spinand.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'include/linux/mtd') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 507f7e289bd1..8aa39ac41e8e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -68,30 +68,60 @@ SPI_MEM_OP_DUMMY(ndummy, 1), \ SPI_MEM_OP_DATA_IN(len, buf, 1)) +#define SPINAND_PAGE_READ_FROM_CACHE_OP_3A(fast, addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + #define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ SPI_MEM_OP_ADDR(2, addr, 1), \ SPI_MEM_OP_DUMMY(ndummy, 1), \ SPI_MEM_OP_DATA_IN(len, buf, 2)) +#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + #define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ SPI_MEM_OP_ADDR(2, addr, 1), \ SPI_MEM_OP_DUMMY(ndummy, 1), \ SPI_MEM_OP_DATA_IN(len, buf, 4)) +#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + #define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ SPI_MEM_OP_ADDR(2, addr, 2), \ SPI_MEM_OP_DUMMY(ndummy, 2), \ SPI_MEM_OP_DATA_IN(len, buf, 2)) +#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP_3A(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(3, addr, 2), \ + SPI_MEM_OP_DUMMY(ndummy, 2), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + #define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ SPI_MEM_OP_ADDR(2, addr, 4), \ SPI_MEM_OP_DUMMY(ndummy, 4), \ SPI_MEM_OP_DATA_IN(len, buf, 4)) +#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP_3A(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ + SPI_MEM_OP_ADDR(3, addr, 4), \ + SPI_MEM_OP_DUMMY(ndummy, 4), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + #define SPINAND_PROG_EXEC_OP(addr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \ SPI_MEM_OP_ADDR(3, addr, 1), \ -- cgit v1.2.3 From 878844908e563a2f02b977bacd221c288e681c47 Mon Sep 17 00:00:00 2001 From: Jeff Kletsky Date: Wed, 22 May 2019 15:05:54 -0700 Subject: mtd: spinand: Add support for two-byte device IDs The GigaDevice GD5F1GQ4UFxxG SPI NAND utilizes two-byte device IDs. http://www.gigadevice.com/datasheet/gd5f1gq4xfxxg/ Signed-off-by: Jeff Kletsky Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 2 +- include/linux/mtd/spinand.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4c15bb58c623..556bfdb34455 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -845,7 +845,7 @@ spinand_select_op_variant(struct spinand_device *spinand, */ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u8 devid) + unsigned int table_size, u16 devid) { struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 8aa39ac41e8e..fbc0423bb4ae 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -290,7 +290,7 @@ struct spinand_ecc_info { */ struct spinand_info { const char *model; - u8 devid; + u16 devid; u32 flags; struct nand_memory_organization memorg; struct nand_ecc_req eccreq; @@ -452,7 +452,7 @@ static inline void spinand_set_of_node(struct spinand_device *spinand, int spinand_match_and_init(struct spinand_device *dev, const struct spinand_info *table, - unsigned int table_size, u8 devid); + unsigned int table_size, u16 devid); int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int target); -- cgit v1.2.3 From 9f897bfdd89f5f08a12fa263a7f57fbf8ad9292f Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Thu, 16 May 2019 12:41:46 -0400 Subject: mtd: Add flag to indicate panic_write Added a flag to indicate a panic_write so that low level drivers can use it to take required action where applicable, to ensure oops data gets written to assigned mtd device. Signed-off-by: Kamal Dasu Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 3 +++ include/linux/mtd/mtd.h | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'include/linux/mtd') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 453242d6cf56..408615f29e57 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1124,6 +1124,9 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return -EROFS; if (!len) return 0; + if (!mtd->oops_panic_write) + mtd->oops_panic_write = true; + return mtd->_panic_write(mtd, to, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_panic_write); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 936a3fdb48b5..4ca8c1c845fb 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -316,6 +316,12 @@ struct mtd_info { int (*_get_device) (struct mtd_info *mtd); void (*_put_device) (struct mtd_info *mtd); + /* + * flag indicates a panic write, low level drivers can take appropriate + * action if required to ensure writes go through + */ + bool oops_panic_write; + struct notifier_block reboot_notifier; /* default mode before reboot */ /* ECC status information */ -- cgit v1.2.3 From 3552691616c940a7c4125c2678ba816653cd725e Mon Sep 17 00:00:00 2001 From: Jeff Kletsky Date: Tue, 18 Jun 2019 10:08:05 -0700 Subject: mtd: spinand: Add initial support for Paragon PN26G0xA Add initial support for Paragon Technology PN26G01Axxxxx and PN26G02Axxxxx SPI NAND Datasheets available at http://www.xtxtech.com/upfile/2016082517274590.pdf http://www.xtxtech.com/upfile/2016082517282329.pdf Signed-off-by: Jeff Kletsky Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/paragon.c | 147 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/paragon.c (limited to 'include/linux/mtd') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 753125082640..9662b9c1d5a9 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o +spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 556bfdb34455..f0f3528aab8f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -757,6 +757,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, + ¶gon_spinand_manufacturer, &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, }; diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c new file mode 100644 index 000000000000..52307681cbd0 --- /dev/null +++ b/drivers/mtd/nand/spi/paragon.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Jeff Kletsky + * + * Author: Jeff Kletsky + */ + +#include +#include +#include + + +#define SPINAND_MFR_PARAGON 0xa1 + + +#define PN26G0XA_STATUS_ECC_BITMASK (3 << 4) + +#define PN26G0XA_STATUS_ECC_NONE_DETECTED (0 << 4) +#define PN26G0XA_STATUS_ECC_1_7_CORRECTED (1 << 4) +#define PN26G0XA_STATUS_ECC_ERRORED (2 << 4) +#define PN26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) + + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + + +static int pn26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = 6 + (15 * section); /* 4 BBM + 2 user bytes */ + region->length = 13; + + return 0; +} + +static int pn26g0xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 4) + return -ERANGE; + + if (section == 4) { + region->offset = 64; + region->length = 64; + } else { + region->offset = 4 + (15 * section); + region->length = 2; + } + + return 0; +} + +static int pn26g0xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & PN26G0XA_STATUS_ECC_BITMASK) { + case PN26G0XA_STATUS_ECC_NONE_DETECTED: + return 0; + + case PN26G0XA_STATUS_ECC_1_7_CORRECTED: + return 7; /* Return upper limit by convention */ + + case PN26G0XA_STATUS_ECC_8_CORRECTED: + return 8; + + case PN26G0XA_STATUS_ECC_ERRORED: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { + .ecc = pn26g0xa_ooblayout_ecc, + .free = pn26g0xa_ooblayout_free, +}; + + +static const struct spinand_info paragon_spinand_table[] = { + SPINAND_INFO("PN26G01A", 0xe1, + NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), + SPINAND_INFO("PN26G02A", 0xe2, + NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&pn26g0xa_ooblayout, + pn26g0xa_ecc_get_status)), +}; + +static int paragon_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* Read ID returns [0][MID][DID] */ + + if (id[1] != SPINAND_MFR_PARAGON) + return 0; + + ret = spinand_match_and_init(spinand, paragon_spinand_table, + ARRAY_SIZE(paragon_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { + .detect = paragon_spinand_detect, +}; + +const struct spinand_manufacturer paragon_spinand_manufacturer = { + .id = SPINAND_MFR_PARAGON, + .name = "Paragon", + .ops = ¶gon_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index fbc0423bb4ae..4ea558bd3c46 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -227,6 +227,7 @@ struct spinand_manufacturer { extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer paragon_spinand_manufacturer; extern const struct spinand_manufacturer toshiba_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; -- cgit v1.2.3