Commit 3f069622 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mtd/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux

Pull MTD updates from Miquel Raynal:
 "This contains the following changes for MTD:

  MTD core changes:
   - New Hyperbus framework
   - New _is_locked (concat) implementation
   - Various cleanups

  NAND core changes:
   - use longest matching pattern in ->exec_op() default parser
   - export NAND operation tracer
   - add flag to indicate panic_write in MTD
   - use kzalloc() instead of kmalloc() and memset()

  Raw NAND controller drivers changes:
   - brcmnand:
       - fix BCH ECC layout for large page NAND parts
       - fallback to detected ecc-strength, ecc-step-size
       - when oops in progress use pio and interrupt polling
       - code refactor code to introduce helper functions
       - add support for v7.3 controller
   - FSMC:
       - use nand_op_trace for operation tracing
   - GPMI:
       - move all driver code into single file
       - various cleanups (including dmaengine changes)
       - use runtime PM to manage clocks
       - implement exec_op
   - MTK:
       - correct low level time calculation of r/w cycle
       - improve data sampling timing for read cycle
       - add validity check for CE# pin setting
       - fix wrongly assigned OOB buffer pointer issue
       - re-license MTK NAND driver as Dual MIT/GPL
   - STM32:
       - manage the get_irq error case
       - increase DMA completion timeouts

  Raw NAND chips drivers changes:
   - Macronix: add read-retry support

  Onenand driver changes:
   - add support for 8Gb datasize chips
   - avoid fall-through warnings

  SPI-NAND changes:
   - define macros for page-read ops with three-byte addresses
   - add support for two-byte device IDs and then for GigaDevice
     GD5F1GQ4UFxxG
   - add initial support for Paragon PN26G0xA
   - handle the case where the last page read has bitflips

  SPI-NOR core changes:
   - add support for the mt25ql02g and w25q16jv flashes
   - print error in case of jedec read id fails
   - is25lp256: add post BFPT fix to correct the addr_width

  SPI NOR controller drivers changes:
   - intel-spi: Add support for Intel Elkhart Lake SPI serial flash
   - smt32: remove the driver as the driver was replaced by spi-stm32-qspi.c
   - cadence-quadspi: add reset control"

* tag 'mtd/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (60 commits)
  mtd: concat: implement _is_locked mtd operation
  mtd: concat: refactor concat_lock/concat_unlock
  mtd: abi: do not use C++ style comments in uapi header
  mtd: afs: remove unneeded NULL check
  mtd: rawnand: stm32_fmc2: increase DMA completion timeouts
  mtd: rawnand: Use kzalloc() instead of kmalloc() and memset()
  mtd: hyperbus: Add driver for TI's HyperBus memory controller
  mtd: spinand: read returns badly if the last page has bitflips
  mtd: spinand: Add initial support for Paragon PN26G0xA
  mtd: rawnand: mtk: Re-license MTK NAND driver as Dual MIT/GPL
  mtd: rawnand: gpmi: remove double assignment to block_size
  dt-bindings: mtd: brcmnand: Add brcmnand, brcmnand-v7.3 support
  mtd: rawnand: brcmnand: Add support for v7.3 controller
  mtd: rawnand: brcmnand: Refactored code to introduce helper functions
  mtd: rawnand: brcmnand: When oops in progress use pio and interrupt polling
  mtd: Add flag to indicate panic_write
  mtd: rawnand: Add Macronix NAND read retry support
  mtd: onenand: Avoid fall-through warnings
  mtd: spinand: Add support for GigaDevice GD5F1GQ4UFxxG
  mtd: spinand: Add support for two-byte device IDs
  ...
parents 22608405 46ce10df
...@@ -28,6 +28,7 @@ Required properties: ...@@ -28,6 +28,7 @@ Required properties:
brcm,brcmnand-v7.0 brcm,brcmnand-v7.0
brcm,brcmnand-v7.1 brcm,brcmnand-v7.1
brcm,brcmnand-v7.2 brcm,brcmnand-v7.2
brcm,brcmnand-v7.3
brcm,brcmnand brcm,brcmnand
- reg : the register start and length for NAND register region. - reg : the register start and length for NAND register region.
(optional) Flash DMA register range (if present) (optional) Flash DMA register range (if present)
...@@ -101,10 +102,10 @@ Required properties: ...@@ -101,10 +102,10 @@ Required properties:
number (e.g., 0, 1, 2, etc.) number (e.g., 0, 1, 2, etc.)
- #address-cells : see partition.txt - #address-cells : see partition.txt
- #size-cells : see partition.txt - #size-cells : see partition.txt
- nand-ecc-strength : see nand-controller.yaml
- nand-ecc-step-size : must be 512 or 1024. See nand-controller.yaml
Optional properties: Optional properties:
- nand-ecc-strength : see nand-controller.yaml
- nand-ecc-step-size : must be 512 or 1024. See nand-controller.yaml
- nand-on-flash-bbt : boolean, to enable the on-flash BBT for this - nand-on-flash-bbt : boolean, to enable the on-flash BBT for this
chip-select. See nand-controller.yaml chip-select. See nand-controller.yaml
- brcm,nand-oob-sector-size : integer, to denote the spare area sector size - brcm,nand-oob-sector-size : integer, to denote the spare area sector size
......
...@@ -35,6 +35,9 @@ custom properties: ...@@ -35,6 +35,9 @@ custom properties:
(qspi_n_ss_out). (qspi_n_ss_out).
- cdns,tslch-ns : Delay in nanoseconds between setting qspi_n_ss_out low - cdns,tslch-ns : Delay in nanoseconds between setting qspi_n_ss_out low
and first bit transfer. and first bit transfer.
- resets : Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names : Must include either "qspi" and/or "qspi-ocp".
Example: Example:
...@@ -50,6 +53,8 @@ Example: ...@@ -50,6 +53,8 @@ Example:
cdns,fifo-depth = <128>; cdns,fifo-depth = <128>;
cdns,fifo-width = <4>; cdns,fifo-width = <4>;
cdns,trigger-address = <0x00000000>; cdns,trigger-address = <0x00000000>;
resets = <&rst QSPI_RESET>, <&rst QSPI_OCP_RESET>;
reset-names = "qspi", "qspi-ocp";
flash0: n25q00@0 { flash0: n25q00@0 {
... ...
......
Bindings for HyperFlash NOR flash chips compliant with Cypress HyperBus
specification and supports Cypress CFI specification 1.5 command set.
Required properties:
- compatible : "cypress,hyperflash", "cfi-flash" for HyperFlash NOR chips
- reg : Address of flash's memory map
Example:
flash@0 {
compatible = "cypress,hyperflash", "cfi-flash";
reg = <0x0 0x4000000>;
};
* STMicroelectronics Quad Serial Peripheral Interface(QuadSPI)
Required properties:
- compatible: should be "st,stm32f469-qspi"
- reg: the first contains the register location and length.
the second contains the memory mapping address and length
- reg-names: should contain the reg names "qspi" "qspi_mm"
- interrupts: should contain the interrupt for the device
- clocks: the phandle of the clock needed by the QSPI controller
- A pinctrl must be defined to set pins in mode of operation for QSPI transfer
Optional properties:
- resets: must contain the phandle to the reset controller.
A spi flash must be a child of the nor_flash node and could have some
properties. Also see jedec,spi-nor.txt.
Required properties:
- reg: chip-Select number (QSPI controller may connect 2 nor flashes)
- spi-max-frequency: max frequency of spi bus
Optional property:
- spi-rx-bus-width: see ../spi/spi-bus.txt for the description
Example:
qspi: spi@a0001000 {
compatible = "st,stm32f469-qspi";
reg = <0xa0001000 0x1000>, <0x90000000 0x10000000>;
reg-names = "qspi", "qspi_mm";
interrupts = <91>;
resets = <&rcc STM32F4_AHB3_RESET(QSPI)>;
clocks = <&rcc 0 STM32F4_AHB3_CLOCK(QSPI)>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_qspi0>;
flash@0 {
reg = <0>;
spi-rx-bus-width = <4>;
spi-max-frequency = <108000000>;
...
};
};
Bindings for HyperBus Memory Controller (HBMC) on TI's K3 family of SoCs
Required properties:
- compatible : "ti,am654-hbmc" for AM654 SoC
- reg : Two entries:
First entry pointed to the register space of HBMC controller
Second entry pointing to the memory map region dedicated for
MMIO access to attached flash devices
- ranges : Address translation from offset within CS to allocated MMIO
space in SoC
Optional properties:
- mux-controls : phandle to the multiplexer that controls selection of
HBMC vs OSPI inside Flash SubSystem (FSS). Default is OSPI,
if property is absent.
See Documentation/devicetree/bindings/mux/reg-mux.txt
for mmio-mux binding details
Example:
system-controller@47000000 {
compatible = "syscon", "simple-mfd";
reg = <0x0 0x47000000 0x0 0x100>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
hbmc_mux: multiplexer {
compatible = "mmio-mux";
#mux-control-cells = <1>;
mux-reg-masks = <0x4 0x2>; /* 0: reg 0x4, bit 1 */
};
};
hbmc: hyperbus@47034000 {
compatible = "ti,am654-hbmc";
reg = <0x0 0x47034000 0x0 0x100>,
<0x5 0x00000000 0x1 0x0000000>;
power-domains = <&k3_pds 55>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0x0 0x0 0x5 0x00000000 0x4000000>, /* CS0 - 64MB */
<0x1 0x0 0x5 0x04000000 0x4000000>; /* CS1 - 64MB */
mux-controls = <&hbmc_mux 0>;
/* Slave flash node */
flash@0,0 {
compatible = "cypress,hyperflash", "cfi-flash";
reg = <0x0 0x0 0x4000000>;
};
};
...@@ -7435,6 +7435,14 @@ F: include/asm-generic/mshyperv.h ...@@ -7435,6 +7435,14 @@ F: include/asm-generic/mshyperv.h
F: tools/hv/ F: tools/hv/
F: Documentation/ABI/stable/sysfs-bus-vmbus F: Documentation/ABI/stable/sysfs-bus-vmbus
HYPERBUS SUPPORT
M: Vignesh Raghavendra <vigneshr@ti.com>
S: Supported
F: drivers/mtd/hyperbus/
F: include/linux/mtd/hyperbus.h
F: Documentation/devicetree/bindings/mtd/cypress,hyperflash.txt
F: Documentation/devicetree/bindings/mtd/ti,am654-hbmc.txt
HYPERVISOR VIRTUAL CONSOLE DRIVER HYPERVISOR VIRTUAL CONSOLE DRIVER
L: linuxppc-dev@lists.ozlabs.org L: linuxppc-dev@lists.ozlabs.org
S: Odd Fixes S: Odd Fixes
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_dma.h> #include <linux/of_dma.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/dma/mxs-dma.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -77,6 +78,7 @@ ...@@ -77,6 +78,7 @@
#define BM_CCW_COMMAND (3 << 0) #define BM_CCW_COMMAND (3 << 0)
#define CCW_CHAIN (1 << 2) #define CCW_CHAIN (1 << 2)
#define CCW_IRQ (1 << 3) #define CCW_IRQ (1 << 3)
#define CCW_WAIT4RDY (1 << 5)
#define CCW_DEC_SEM (1 << 6) #define CCW_DEC_SEM (1 << 6)
#define CCW_WAIT4END (1 << 7) #define CCW_WAIT4END (1 << 7)
#define CCW_HALT_ON_TERM (1 << 8) #define CCW_HALT_ON_TERM (1 << 8)
...@@ -477,16 +479,16 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) ...@@ -477,16 +479,16 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan)
* ...... * ......
* ->device_prep_slave_sg(0); * ->device_prep_slave_sg(0);
* ...... * ......
* ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); * ->device_prep_slave_sg(DMA_CTRL_ACK);
* ...... * ......
* [3] If there are more than two DMA commands in the DMA chain, the code * [3] If there are more than two DMA commands in the DMA chain, the code
* should be: * should be:
* ...... * ......
* ->device_prep_slave_sg(0); // First * ->device_prep_slave_sg(0); // First
* ...... * ......
* ->device_prep_slave_sg(DMA_PREP_INTERRUPT [| DMA_CTRL_ACK]); * ->device_prep_slave_sg(DMA_CTRL_ACK]);
* ...... * ......
* ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); // Last * ->device_prep_slave_sg(DMA_CTRL_ACK); // Last
* ...... * ......
*/ */
static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
...@@ -500,13 +502,12 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ...@@ -500,13 +502,12 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
struct scatterlist *sg; struct scatterlist *sg;
u32 i, j; u32 i, j;
u32 *pio; u32 *pio;
bool append = flags & DMA_PREP_INTERRUPT; int idx = 0;
int idx = append ? mxs_chan->desc_count : 0;
if (mxs_chan->status == DMA_IN_PROGRESS && !append) if (mxs_chan->status == DMA_IN_PROGRESS)
return NULL; idx = mxs_chan->desc_count;
if (sg_len + (append ? idx : 0) > NUM_CCW) { if (sg_len + idx > NUM_CCW) {
dev_err(mxs_dma->dma_device.dev, dev_err(mxs_dma->dma_device.dev,
"maximum number of sg exceeded: %d > %d\n", "maximum number of sg exceeded: %d > %d\n",
sg_len, NUM_CCW); sg_len, NUM_CCW);
...@@ -520,7 +521,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ...@@ -520,7 +521,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
* If the sg is prepared with append flag set, the sg * If the sg is prepared with append flag set, the sg
* will be appended to the last prepared sg. * will be appended to the last prepared sg.
*/ */
if (append) { if (idx) {
BUG_ON(idx < 1); BUG_ON(idx < 1);
ccw = &mxs_chan->ccw[idx - 1]; ccw = &mxs_chan->ccw[idx - 1];
ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx; ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
...@@ -541,12 +542,14 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ...@@ -541,12 +542,14 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
ccw->bits = 0; ccw->bits = 0;
ccw->bits |= CCW_IRQ; ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_DEC_SEM; ccw->bits |= CCW_DEC_SEM;
if (flags & DMA_CTRL_ACK) if (flags & MXS_DMA_CTRL_WAIT4END)
ccw->bits |= CCW_WAIT4END; ccw->bits |= CCW_WAIT4END;
ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_HALT_ON_TERM;
ccw->bits |= CCW_TERM_FLUSH; ccw->bits |= CCW_TERM_FLUSH;
ccw->bits |= BF_CCW(sg_len, PIO_NUM); ccw->bits |= BF_CCW(sg_len, PIO_NUM);
ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND); ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND);
if (flags & MXS_DMA_CTRL_WAIT4RDY)
ccw->bits |= CCW_WAIT4RDY;
} else { } else {
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
if (sg_dma_len(sg) > MAX_XFER_BYTES) { if (sg_dma_len(sg) > MAX_XFER_BYTES) {
...@@ -573,7 +576,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ...@@ -573,7 +576,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg(
ccw->bits &= ~CCW_CHAIN; ccw->bits &= ~CCW_CHAIN;
ccw->bits |= CCW_IRQ; ccw->bits |= CCW_IRQ;
ccw->bits |= CCW_DEC_SEM; ccw->bits |= CCW_DEC_SEM;
if (flags & DMA_CTRL_ACK) if (flags & MXS_DMA_CTRL_WAIT4END)
ccw->bits |= CCW_WAIT4END; ccw->bits |= CCW_WAIT4END;
} }
} }
......
...@@ -274,4 +274,6 @@ source "drivers/mtd/spi-nor/Kconfig" ...@@ -274,4 +274,6 @@ source "drivers/mtd/spi-nor/Kconfig"
source "drivers/mtd/ubi/Kconfig" source "drivers/mtd/ubi/Kconfig"
source "drivers/mtd/hyperbus/Kconfig"
endif # MTD endif # MTD
...@@ -34,3 +34,4 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/ ...@@ -34,3 +34,4 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/ obj-$(CONFIG_MTD_UBI) += ubi/
obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/
This diff is collapsed.
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
if MTD_HYPERBUS
config HBMC_AM654
tristate "HyperBus controller driver for AM65x SoC"
select MULTIPLEXER
select MUX_MMIO
help
This is the driver for HyperBus controller on TI's AM65x and
other SoCs
endif # MTD_HYPERBUS
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o
obj-$(CONFIG_HBMC_AM654) += hbmc-am654.o
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
// Author: Vignesh Raghavendra <vigneshr@ti.com>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/hyperbus.h>
#include <linux/mtd/mtd.h>
#include <linux/mux/consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#define AM654_HBMC_CALIB_COUNT 25
struct am654_hbmc_priv {
struct hyperbus_ctlr ctlr;
struct hyperbus_device hbdev;
struct mux_control *mux_ctrl;
};
static int am654_hbmc_calibrate(struct hyperbus_device *hbdev)
{
struct map_info *map = &hbdev->map;
struct cfi_private cfi;
int count = AM654_HBMC_CALIB_COUNT;
int pass_count = 0;
int ret;
cfi.interleave = 1;
cfi.device_type = CFI_DEVICETYPE_X16;
cfi_send_gen_cmd(0xF0, 0, 0, map, &cfi, cfi.device_type, NULL);
cfi_send_gen_cmd(0x98, 0x55, 0, map, &cfi, cfi.device_type, NULL);
while (count--) {
ret = cfi_qry_present(map, 0, &cfi);
if (ret)
pass_count++;
else
pass_count = 0;
if (pass_count == 5)
break;
}
cfi_qry_mode_off(0, map, &cfi);
return ret;
}
static const struct hyperbus_ops am654_hbmc_ops = {
.calibrate = am654_hbmc_calibrate,
};
static int am654_hbmc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct am654_hbmc_priv *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
if (of_property_read_bool(dev->of_node, "mux-controls")) {
struct mux_control *control = devm_mux_control_get(dev, NULL);
if (IS_ERR(control))
return PTR_ERR(control);
ret = mux_control_select(control, 1);
if (ret) {
dev_err(dev, "Failed to select HBMC mux\n");
return ret;
}
priv->mux_ctrl = control;
}
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
goto disable_pm;
}
priv->ctlr.dev = dev;
priv->ctlr.ops = &am654_hbmc_ops;
priv->hbdev.ctlr = &priv->ctlr;
priv->hbdev.np = of_get_next_child(dev->of_node, NULL);
ret = hyperbus_register_device(&priv->hbdev);
if (ret) {
dev_err(dev, "failed to register controller\n");
pm_runtime_put_sync(&pdev->dev);
goto disable_pm;
}
return 0;
disable_pm:
pm_runtime_disable(dev);
if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl);
return ret;
}
static int am654_hbmc_remove(struct platform_device *pdev)
{
struct am654_hbmc_priv *priv = platform_get_drvdata(pdev);
int ret;
ret = hyperbus_unregister_device(&priv->hbdev);
if (priv->mux_ctrl)
mux_control_deselect(priv->mux_ctrl);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
static const struct of_device_id am654_hbmc_dt_ids[] = {
{
.compatible = "ti,am654-hbmc",
},
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, am654_hbmc_dt_ids);
static struct platform_driver am654_hbmc_platform_driver = {
.probe = am654_hbmc_probe,
.remove = am654_hbmc_remove,
.driver = {
.name = "hbmc-am654",
.of_match_table = am654_hbmc_dt_ids,
},
};
module_platform_driver(am654_hbmc_platform_driver);
MODULE_DESCRIPTION("HBMC driver for AM654 SoC");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:hbmc-am654");
MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
// Author: Vignesh Raghavendra <vigneshr@ti.com>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/hyperbus.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/types.h>
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 <vigneshr@ti.com>");
...@@ -437,7 +437,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -437,7 +437,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
return err; return err;
} }
static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) static int concat_xxlock(struct mtd_info *mtd, loff_t ofs, uint64_t len,
bool is_lock)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL; int i, err = -EINVAL;
...@@ -456,7 +457,10 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -456,7 +457,10 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
else else
size = len; size = len;
err = mtd_lock(subdev, ofs, size); if (is_lock)
err = mtd_lock(subdev, ofs, size);
else
err = mtd_unlock(subdev, ofs, size);
if (err) if (err)
break; break;
...@@ -471,35 +475,33 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -471,35 +475,33 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
return err; return err;
} }
static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
return concat_xxlock(mtd, ofs, len, true);
}
static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
return concat_xxlock(mtd, ofs, len, false);
}
static int concat_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{ {
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
int i, err = 0; int i, err = -EINVAL;
for (i = 0; i < concat->num_subdev; i++) { for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i]; struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
if (ofs >= subdev->size) { if (ofs >= subdev->size) {
size = 0;
ofs -= subdev->size; ofs -= subdev->size;
continue; continue;
} }
if (ofs + len > subdev->size)
size = subdev->size - ofs;
else
size = len;
err = mtd_unlock(subdev, ofs, size);
if (err)
break;
len -= size; if (ofs + len > subdev->size)
if (len == 0)
break; break;
err = -EINVAL; return mtd_is_locked(subdev, ofs, len);
ofs = 0;
} }
return err; return err;
...@@ -704,6 +706,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c ...@@ -704,6 +706,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd._sync = concat_sync; concat->mtd._sync = concat_sync;
concat->mtd._lock = concat_lock; concat->mtd._lock = concat_lock;
concat->mtd._unlock = concat_unlock; concat->mtd._unlock = concat_unlock;
concat->mtd._is_locked = concat_is_locked;
concat->mtd._suspend = concat_suspend; concat->mtd._suspend = concat_suspend;
concat->mtd._resume = concat_resume; concat->mtd._resume = concat_resume;
......
...@@ -1124,6 +1124,9 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, ...@@ -1124,6 +1124,9 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
return -EROFS; return -EROFS;
if (!len) if (!len)
return 0; return 0;
if (!mtd->oops_panic_write)
mtd->oops_panic_write = true;
return mtd->_panic_write(mtd, to, len, retlen, buf); return mtd->_panic_write(mtd, to, len, retlen, buf);
} }
EXPORT_SYMBOL_GPL(mtd_panic_write); EXPORT_SYMBOL_GPL(mtd_panic_write);
......
...@@ -3257,6 +3257,8 @@ static void onenand_check_features(struct mtd_info *mtd) ...@@ -3257,6 +3257,8 @@ static void onenand_check_features(struct mtd_info *mtd)
/* Lock scheme */ /* Lock scheme */
switch (density) { switch (density) {
case ONENAND_DEVICE_DENSITY_8Gb:
this->options |= ONENAND_HAS_NOP_1;
case ONENAND_DEVICE_DENSITY_4Gb: case ONENAND_DEVICE_DENSITY_4Gb:
if (ONENAND_IS_DDP(this)) if (ONENAND_IS_DDP(this))
this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_2PLANE;
...@@ -3277,12 +3279,15 @@ static void onenand_check_features(struct mtd_info *mtd) ...@@ -3277,12 +3279,15 @@ static void onenand_check_features(struct mtd_info *mtd)
if ((this->version_id & 0xf) == 0xe) if ((this->version_id & 0xf) == 0xe)
this->options |= ONENAND_HAS_NOP_1; this->options |= ONENAND_HAS_NOP_1;
} }
this->options |= ONENAND_HAS_UNLOCK_ALL;
break;
case ONENAND_DEVICE_DENSITY_2Gb: case ONENAND_DEVICE_DENSITY_2Gb:
/* 2Gb DDP does not have 2 plane */ /* 2Gb DDP does not have 2 plane */
if (!ONENAND_IS_DDP(this)) if (!ONENAND_IS_DDP(this))
this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_2PLANE;
this->options |= ONENAND_HAS_UNLOCK_ALL; this->options |= ONENAND_HAS_UNLOCK_ALL;
break;
case ONENAND_DEVICE_DENSITY_1Gb: case ONENAND_DEVICE_DENSITY_1Gb:
/* A-Die has all block unlock */ /* A-Die has all block unlock */
......
This diff is collapsed.
...@@ -613,28 +613,20 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, ...@@ -613,28 +613,20 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
for (op_id = 0; op_id < op->ninstrs; op_id++) { for (op_id = 0; op_id < op->ninstrs; op_id++) {
instr = &op->instrs[op_id]; instr = &op->instrs[op_id];
nand_op_trace(" ", instr);
switch (instr->type) { switch (instr->type) {
case NAND_OP_CMD_INSTR: case NAND_OP_CMD_INSTR:
pr_debug(" ->CMD [0x%02x]\n",
instr->ctx.cmd.opcode);
writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va); writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va);
break; break;
case NAND_OP_ADDR_INSTR: case NAND_OP_ADDR_INSTR:
pr_debug(" ->ADDR [%d cyc]",
instr->ctx.addr.naddrs);
for (i = 0; i < instr->ctx.addr.naddrs; i++) for (i = 0; i < instr->ctx.addr.naddrs; i++)
writeb_relaxed(instr->ctx.addr.addrs[i], writeb_relaxed(instr->ctx.addr.addrs[i],
host->addr_va); host->addr_va);
break; break;
case NAND_OP_DATA_IN_INSTR: case NAND_OP_DATA_IN_INSTR:
pr_debug(" ->DATA_IN [%d B%s]\n", instr->ctx.data.len,
instr->ctx.data.force_8bit ?
", force 8-bit" : "");
if (host->mode == USE_DMA_ACCESS) if (host->mode == USE_DMA_ACCESS)
fsmc_read_buf_dma(host, instr->ctx.data.buf.in, fsmc_read_buf_dma(host, instr->ctx.data.buf.in,
instr->ctx.data.len); instr->ctx.data.len);
...@@ -644,10 +636,6 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, ...@@ -644,10 +636,6 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
break; break;
case NAND_OP_DATA_OUT_INSTR: case NAND_OP_DATA_OUT_INSTR:
pr_debug(" ->DATA_OUT [%d B%s]\n", instr->ctx.data.len,
instr->ctx.data.force_8bit ?
", force 8-bit" : "");
if (host->mode == USE_DMA_ACCESS) if (host->mode == USE_DMA_ACCESS)
fsmc_write_buf_dma(host, fsmc_write_buf_dma(host,
instr->ctx.data.buf.out, instr->ctx.data.buf.out,
...@@ -658,9 +646,6 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, ...@@ -658,9 +646,6 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
break; break;
case NAND_OP_WAITRDY_INSTR: case NAND_OP_WAITRDY_INSTR:
pr_debug(" ->WAITRDY [max %d ms]\n",
instr->ctx.waitrdy.timeout_ms);
ret = nand_soft_waitrdy(chip, ret = nand_soft_waitrdy(chip,
instr->ctx.waitrdy.timeout_ms); instr->ctx.waitrdy.timeout_ms);
break; break;
......
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o
gpmi_nand-objs += gpmi-nand.o gpmi_nand-objs += gpmi-nand.o
gpmi_nand-objs += gpmi-lib.o
This diff is collapsed.
This diff is collapsed.
...@@ -103,6 +103,14 @@ struct gpmi_nfc_hardware_timing { ...@@ -103,6 +103,14 @@ struct gpmi_nfc_hardware_timing {
u32 ctrl1n; u32 ctrl1n;
}; };
#define GPMI_MAX_TRANSFERS 8
struct gpmi_transfer {
u8 cmdbuf[8];
struct scatterlist sgl;
enum dma_data_direction direction;
};
struct gpmi_nand_data { struct gpmi_nand_data {
/* Devdata */ /* Devdata */
const struct gpmi_devdata *devdata; const struct gpmi_devdata *devdata;
...@@ -126,25 +134,18 @@ struct gpmi_nand_data { ...@@ -126,25 +134,18 @@ struct gpmi_nand_data {
struct boot_rom_geometry rom_geometry; struct boot_rom_geometry rom_geometry;
/* MTD / NAND */ /* MTD / NAND */
struct nand_controller base;
struct nand_chip nand; struct nand_chip nand;
/* General-use Variables */ struct gpmi_transfer transfers[GPMI_MAX_TRANSFERS];
int current_chip; int ntransfers;
unsigned int command_length;
struct scatterlist cmd_sgl; bool bch;
char *cmd_buffer; uint32_t bch_flashlayout0;
uint32_t bch_flashlayout1;
struct scatterlist data_sgl;
char *data_buffer_dma; char *data_buffer_dma;
void *page_buffer_virt;
dma_addr_t page_buffer_phys;
unsigned int page_buffer_size;
void *payload_virt;
dma_addr_t payload_phys;
void *auxiliary_virt; void *auxiliary_virt;
dma_addr_t auxiliary_phys; dma_addr_t auxiliary_phys;
...@@ -154,45 +155,8 @@ struct gpmi_nand_data { ...@@ -154,45 +155,8 @@ struct gpmi_nand_data {
#define DMA_CHANS 8 #define DMA_CHANS 8
struct dma_chan *dma_chans[DMA_CHANS]; struct dma_chan *dma_chans[DMA_CHANS];
struct completion dma_done; struct completion dma_done;
/* private */
void *private;
}; };
/* Common Services */
int common_nfc_set_geometry(struct gpmi_nand_data *);
struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
bool prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len,
enum dma_data_direction dr);
int start_dma_without_bch_irq(struct gpmi_nand_data *,
struct dma_async_tx_descriptor *);
int start_dma_with_bch_irq(struct gpmi_nand_data *,
struct dma_async_tx_descriptor *);
/* GPMI-NAND helper function library */
int gpmi_init(struct gpmi_nand_data *);
void gpmi_clear_bch(struct gpmi_nand_data *);
void gpmi_dump_info(struct gpmi_nand_data *);
int bch_set_geometry(struct gpmi_nand_data *);
int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
int gpmi_send_command(struct gpmi_nand_data *);
int gpmi_enable_clk(struct gpmi_nand_data *this);
int gpmi_disable_clk(struct gpmi_nand_data *this);
int gpmi_setup_data_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf);
void gpmi_nfc_apply_timings(struct gpmi_nand_data *this);
int gpmi_read_data(struct gpmi_nand_data *, void *buf, int len);
int gpmi_send_data(struct gpmi_nand_data *, const void *buf, int len);
int gpmi_send_page(struct gpmi_nand_data *,
dma_addr_t payload, dma_addr_t auxiliary);
int gpmi_read_page(struct gpmi_nand_data *,
dma_addr_t payload, dma_addr_t auxiliary);
void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
const u8 *src, size_t src_bit_off,
size_t nbits);
/* BCH : Status Block Completion Codes */ /* BCH : Status Block Completion Codes */
#define STATUS_GOOD 0x00 #define STATUS_GOOD 0x00
#define STATUS_ERASED 0xff #define STATUS_ERASED 0xff
......
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0 OR MIT
/* /*
* MTK ECC controller driver. * MTK ECC controller driver.
* Copyright (C) 2016 MediaTek Inc. * Copyright (C) 2016 MediaTek Inc.
...@@ -596,4 +596,4 @@ module_platform_driver(mtk_ecc_driver); ...@@ -596,4 +596,4 @@ module_platform_driver(mtk_ecc_driver);
MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>"); MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
MODULE_DESCRIPTION("MTK Nand ECC Driver"); MODULE_DESCRIPTION("MTK Nand ECC Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("Dual MIT/GPL");
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* /*
* MTK SDG1 ECC controller * MTK SDG1 ECC controller
* *
......
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0 OR MIT
/* /*
* MTK NAND Flash controller driver. * MTK NAND Flash controller driver.
* Copyright (C) 2016 MediaTek Inc. * Copyright (C) 2016 MediaTek Inc.
...@@ -79,6 +79,10 @@ ...@@ -79,6 +79,10 @@
#define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2) #define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2)
#define NFI_FDM_MAX_SIZE (8) #define NFI_FDM_MAX_SIZE (8)
#define NFI_FDM_MIN_SIZE (1) #define NFI_FDM_MIN_SIZE (1)
#define NFI_DEBUG_CON1 (0x220)
#define STROBE_MASK GENMASK(4, 3)
#define STROBE_SHIFT (3)
#define MAX_STROBE_DLY (3)
#define NFI_MASTER_STA (0x224) #define NFI_MASTER_STA (0x224)
#define MASTER_STA_MASK (0x0FFF) #define MASTER_STA_MASK (0x0FFF)
#define NFI_EMPTY_THRESH (0x23C) #define NFI_EMPTY_THRESH (0x23C)
...@@ -150,6 +154,8 @@ struct mtk_nfc { ...@@ -150,6 +154,8 @@ struct mtk_nfc {
struct list_head chips; struct list_head chips;
u8 *buffer; u8 *buffer;
unsigned long assigned_cs;
}; };
/* /*
...@@ -500,7 +506,8 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline, ...@@ -500,7 +506,8 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline,
{ {
struct mtk_nfc *nfc = nand_get_controller_data(chip); struct mtk_nfc *nfc = nand_get_controller_data(chip);
const struct nand_sdr_timings *timings; const struct nand_sdr_timings *timings;
u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt; u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst = 0, trlt = 0;
u32 temp, tsel = 0;
timings = nand_get_sdr_timings(conf); timings = nand_get_sdr_timings(conf);
if (IS_ERR(timings)) if (IS_ERR(timings))
...@@ -536,14 +543,53 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline, ...@@ -536,14 +543,53 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline,
twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; twh = DIV_ROUND_UP(twh * rate, 1000000) - 1;
twh &= 0xf; twh &= 0xf;
twst = timings->tWP_min / 1000; /* Calculate real WE#/RE# hold time in nanosecond */
temp = (twh + 1) * 1000000 / rate;
/* nanosecond to picosecond */
temp *= 1000;
/*
* WE# low level time should be expaned to meet WE# pulse time
* and WE# cycle time at the same time.
*/
if (temp < timings->tWC_min)
twst = timings->tWC_min - temp;
twst = max(timings->tWP_min, twst) / 1000;
twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; twst = DIV_ROUND_UP(twst * rate, 1000000) - 1;
twst &= 0xf; twst &= 0xf;
trlt = max(timings->tREA_max, timings->tRP_min) / 1000; /*
* RE# low level time should be expaned to meet RE# pulse time
* and RE# cycle time at the same time.
*/
if (temp < timings->tRC_min)
trlt = timings->tRC_min - temp;
trlt = max(trlt, timings->tRP_min) / 1000;
trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1;
trlt &= 0xf; trlt &= 0xf;
/* Calculate RE# pulse time in nanosecond. */
temp = (trlt + 1) * 1000000 / rate;
/* nanosecond to picosecond */
temp *= 1000;
/*
* If RE# access time is bigger than RE# pulse time,
* delay sampling data timing.
*/
if (temp < timings->tREA_max) {
tsel = timings->tREA_max / 1000;
tsel = DIV_ROUND_UP(tsel * rate, 1000000);
tsel -= (trlt + 1);
if (tsel > MAX_STROBE_DLY) {
trlt += tsel - MAX_STROBE_DLY;
tsel = MAX_STROBE_DLY;
}
}
temp = nfi_readl(nfc, NFI_DEBUG_CON1);
temp &= ~STROBE_MASK;
temp |= tsel << STROBE_SHIFT;
nfi_writel(nfc, temp, NFI_DEBUG_CON1);
/* /*
* ACCON: access timing control register * ACCON: access timing control register
* ------------------------------------- * -------------------------------------
...@@ -835,19 +881,21 @@ static int mtk_nfc_write_oob_std(struct nand_chip *chip, int page) ...@@ -835,19 +881,21 @@ static int mtk_nfc_write_oob_std(struct nand_chip *chip, int page)
return mtk_nfc_write_page_raw(chip, NULL, 1, page); return mtk_nfc_write_page_raw(chip, NULL, 1, page);
} }
static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 start,
u32 sectors)
{ {
struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *chip = mtd_to_nand(mtd);
struct mtk_nfc *nfc = nand_get_controller_data(chip); struct mtk_nfc *nfc = nand_get_controller_data(chip);
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
struct mtk_ecc_stats stats; struct mtk_ecc_stats stats;
u32 reg_size = mtk_nand->fdm.reg_size;
int rc, i; int rc, i;
rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE; rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
if (rc) { if (rc) {
memset(buf, 0xff, sectors * chip->ecc.size); memset(buf, 0xff, sectors * chip->ecc.size);
for (i = 0; i < sectors; i++) for (i = 0; i < sectors; i++)
memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size); memset(oob_ptr(chip, start + i), 0xff, reg_size);
return 0; return 0;
} }
...@@ -867,7 +915,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -867,7 +915,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
u32 spare = mtk_nand->spare_per_sector; u32 spare = mtk_nand->spare_per_sector;
u32 column, sectors, start, end, reg; u32 column, sectors, start, end, reg;
dma_addr_t addr; dma_addr_t addr;
int bitflips; int bitflips = 0;
size_t len; size_t len;
u8 *buf; u8 *buf;
int rc; int rc;
...@@ -934,14 +982,11 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -934,14 +982,11 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
if (rc < 0) { if (rc < 0) {
dev_err(nfc->dev, "subpage done timeout\n"); dev_err(nfc->dev, "subpage done timeout\n");
bitflips = -EIO; bitflips = -EIO;
} else { } else if (!raw) {
bitflips = 0; rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE);
if (!raw) { bitflips = rc < 0 ? -ETIMEDOUT :
rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); mtk_nfc_update_ecc_stats(mtd, buf, start, sectors);
bitflips = rc < 0 ? -ETIMEDOUT : mtk_nfc_read_fdm(chip, start, sectors);
mtk_nfc_update_ecc_stats(mtd, buf, sectors);
mtk_nfc_read_fdm(chip, start, sectors);
}
} }
dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
...@@ -1315,6 +1360,17 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, ...@@ -1315,6 +1360,17 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
dev_err(dev, "reg property failure : %d\n", ret); dev_err(dev, "reg property failure : %d\n", ret);
return ret; return ret;
} }
if (tmp >= MTK_NAND_MAX_NSELS) {
dev_err(dev, "invalid CS: %u\n", tmp);
return -EINVAL;
}
if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
dev_err(dev, "CS %u already assigned\n", tmp);
return -EINVAL;
}
chip->sels[i] = tmp; chip->sels[i] = tmp;
} }
...@@ -1589,6 +1645,6 @@ static struct platform_driver mtk_nfc_driver = { ...@@ -1589,6 +1645,6 @@ static struct platform_driver mtk_nfc_driver = {
module_platform_driver(mtk_nfc_driver); module_platform_driver(mtk_nfc_driver);
MODULE_LICENSE("GPL"); MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>"); MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
...@@ -2111,35 +2111,7 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) ...@@ -2111,35 +2111,7 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
if (instr == &ctx->subop.instrs[0]) if (instr == &ctx->subop.instrs[0])
prefix = " ->"; prefix = " ->";
switch (instr->type) { nand_op_trace(prefix, instr);
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;
}
if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1])
prefix = " "; prefix = " ";
...@@ -2152,6 +2124,22 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) ...@@ -2152,6 +2124,22 @@ static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx)
} }
#endif #endif
static int nand_op_parser_cmp_ctx(const struct nand_op_parser_ctx *a,
const struct nand_op_parser_ctx *b)
{
if (a->subop.ninstrs < b->subop.ninstrs)
return -1;
else if (a->subop.ninstrs > b->subop.ninstrs)
return 1;
if (a->subop.last_instr_end_off < b->subop.last_instr_end_off)
return -1;
else if (a->subop.last_instr_end_off > b->subop.last_instr_end_off)
return 1;
return 0;
}
/** /**
* nand_op_parser_exec_op - exec_op parser * nand_op_parser_exec_op - exec_op parser
* @chip: the NAND chip * @chip: the NAND chip
...@@ -2186,32 +2174,40 @@ int nand_op_parser_exec_op(struct nand_chip *chip, ...@@ -2186,32 +2174,40 @@ int nand_op_parser_exec_op(struct nand_chip *chip,
unsigned int i; unsigned int i;
while (ctx.subop.instrs < op->instrs + op->ninstrs) { while (ctx.subop.instrs < op->instrs + op->ninstrs) {
int ret; const struct nand_op_parser_pattern *pattern;
struct nand_op_parser_ctx best_ctx;
int ret, best_pattern = -1;
for (i = 0; i < parser->npatterns; i++) { for (i = 0; i < parser->npatterns; i++) {
const struct nand_op_parser_pattern *pattern; struct nand_op_parser_ctx test_ctx = ctx;
pattern = &parser->patterns[i]; pattern = &parser->patterns[i];
if (!nand_op_parser_match_pat(pattern, &ctx)) if (!nand_op_parser_match_pat(pattern, &test_ctx))
continue; continue;
nand_op_parser_trace(&ctx); if (best_pattern >= 0 &&
nand_op_parser_cmp_ctx(&test_ctx, &best_ctx) <= 0)
if (check_only) continue;
break;
ret = pattern->exec(chip, &ctx.subop);
if (ret)
return ret;
break; best_pattern = i;
best_ctx = test_ctx;
} }
if (i == parser->npatterns) { if (best_pattern < 0) {
pr_debug("->exec_op() parser: pattern not found!\n"); pr_debug("->exec_op() parser: pattern not found!\n");
return -ENOTSUPP; return -ENOTSUPP;
} }
ctx = best_ctx;
nand_op_parser_trace(&ctx);
if (!check_only) {
pattern = &parser->patterns[best_pattern];
ret = pattern->exec(chip, &ctx.subop);
if (ret)
return ret;
}
/* /*
* Update the context structure by pointing to the start of the * Update the context structure by pointing to the start of the
* next subop. * next subop.
......
...@@ -170,7 +170,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) ...@@ -170,7 +170,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
goto fail; goto fail;
} }
nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL);
nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL);
if (!nbc->eccmask || !nbc->errloc) if (!nbc->eccmask || !nbc->errloc)
goto fail; goto fail;
...@@ -182,7 +182,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) ...@@ -182,7 +182,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
goto fail; goto fail;
memset(erased_page, 0xff, eccsize); memset(erased_page, 0xff, eccsize);
memset(nbc->eccmask, 0, eccbytes);
encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
kfree(erased_page); kfree(erased_page);
......
...@@ -8,6 +8,50 @@ ...@@ -8,6 +8,50 @@
#include "internals.h" #include "internals.h"
#define MACRONIX_READ_RETRY_BIT BIT(0)
#define MACRONIX_NUM_READ_RETRY_MODES 6
struct nand_onfi_vendor_macronix {
u8 reserved;
u8 reliability_func;
} __packed;
static int macronix_nand_setup_read_retry(struct nand_chip *chip, int mode)
{
u8 feature[ONFI_SUBFEATURE_PARAM_LEN];
if (!chip->parameters.supports_set_get_features ||
!test_bit(ONFI_FEATURE_ADDR_READ_RETRY,
chip->parameters.set_feature_list))
return -ENOTSUPP;
feature[0] = mode;
return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature);
}
static void macronix_nand_onfi_init(struct nand_chip *chip)
{
struct nand_parameters *p = &chip->parameters;
struct nand_onfi_vendor_macronix *mxic;
if (!p->onfi)
return;
mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor;
if ((mxic->reliability_func & MACRONIX_READ_RETRY_BIT) == 0)
return;
chip->read_retries = MACRONIX_NUM_READ_RETRY_MODES;
chip->setup_read_retry = macronix_nand_setup_read_retry;
if (p->supports_set_get_features) {
bitmap_set(p->set_feature_list,
ONFI_FEATURE_ADDR_READ_RETRY, 1);
bitmap_set(p->get_feature_list,
ONFI_FEATURE_ADDR_READ_RETRY, 1);
}
}
/* /*
* Macronix AC series does not support using SET/GET_FEATURES to change * Macronix AC series does not support using SET/GET_FEATURES to change
* the timings unlike what is declared in the parameter page. Unflag * the timings unlike what is declared in the parameter page. Unflag
...@@ -56,6 +100,7 @@ static int macronix_nand_init(struct nand_chip *chip) ...@@ -56,6 +100,7 @@ static int macronix_nand_init(struct nand_chip *chip)
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
macronix_nand_fix_broken_get_timings(chip); macronix_nand_fix_broken_get_timings(chip);
macronix_nand_onfi_init(chip);
return 0; return 0;
} }
......
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
/* Max ECC buffer length */ /* Max ECC buffer length */
#define FMC2_MAX_ECC_BUF_LEN (FMC2_BCHDSRS_LEN * FMC2_MAX_SG) #define FMC2_MAX_ECC_BUF_LEN (FMC2_BCHDSRS_LEN * FMC2_MAX_SG)
#define FMC2_TIMEOUT_MS 1000
/* Timings */ /* Timings */
#define FMC2_THIZ 1 #define FMC2_THIZ 1
#define FMC2_TIO 8000 #define FMC2_TIO 8000
...@@ -530,7 +532,8 @@ static int stm32_fmc2_ham_calculate(struct nand_chip *chip, const u8 *data, ...@@ -530,7 +532,8 @@ static int stm32_fmc2_ham_calculate(struct nand_chip *chip, const u8 *data,
int ret; int ret;
ret = readl_relaxed_poll_timeout(fmc2->io_base + FMC2_SR, ret = readl_relaxed_poll_timeout(fmc2->io_base + FMC2_SR,
sr, sr & FMC2_SR_NWRF, 10, 1000); sr, sr & FMC2_SR_NWRF, 10,
FMC2_TIMEOUT_MS);
if (ret) { if (ret) {
dev_err(fmc2->dev, "ham timeout\n"); dev_err(fmc2->dev, "ham timeout\n");
return ret; return ret;
...@@ -611,7 +614,7 @@ static int stm32_fmc2_bch_calculate(struct nand_chip *chip, const u8 *data, ...@@ -611,7 +614,7 @@ static int stm32_fmc2_bch_calculate(struct nand_chip *chip, const u8 *data,
/* Wait until the BCH code is ready */ /* Wait until the BCH code is ready */
if (!wait_for_completion_timeout(&fmc2->complete, if (!wait_for_completion_timeout(&fmc2->complete,
msecs_to_jiffies(1000))) { msecs_to_jiffies(FMC2_TIMEOUT_MS))) {
dev_err(fmc2->dev, "bch timeout\n"); dev_err(fmc2->dev, "bch timeout\n");
stm32_fmc2_disable_bch_irq(fmc2); stm32_fmc2_disable_bch_irq(fmc2);
return -ETIMEDOUT; return -ETIMEDOUT;
...@@ -696,7 +699,7 @@ static int stm32_fmc2_bch_correct(struct nand_chip *chip, u8 *dat, ...@@ -696,7 +699,7 @@ static int stm32_fmc2_bch_correct(struct nand_chip *chip, u8 *dat,
/* Wait until the decoding error is ready */ /* Wait until the decoding error is ready */
if (!wait_for_completion_timeout(&fmc2->complete, if (!wait_for_completion_timeout(&fmc2->complete,
msecs_to_jiffies(1000))) { msecs_to_jiffies(FMC2_TIMEOUT_MS))) {
dev_err(fmc2->dev, "bch timeout\n"); dev_err(fmc2->dev, "bch timeout\n");
stm32_fmc2_disable_bch_irq(fmc2); stm32_fmc2_disable_bch_irq(fmc2);
return -ETIMEDOUT; return -ETIMEDOUT;
...@@ -969,7 +972,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, ...@@ -969,7 +972,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf,
/* Wait end of sequencer transfer */ /* Wait end of sequencer transfer */
if (!wait_for_completion_timeout(&fmc2->complete, if (!wait_for_completion_timeout(&fmc2->complete,
msecs_to_jiffies(1000))) { msecs_to_jiffies(FMC2_TIMEOUT_MS))) {
dev_err(fmc2->dev, "seq timeout\n"); dev_err(fmc2->dev, "seq timeout\n");
stm32_fmc2_disable_seq_irq(fmc2); stm32_fmc2_disable_seq_irq(fmc2);
dmaengine_terminate_all(dma_ch); dmaengine_terminate_all(dma_ch);
...@@ -981,7 +984,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, ...@@ -981,7 +984,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf,
/* Wait DMA data transfer completion */ /* Wait DMA data transfer completion */
if (!wait_for_completion_timeout(&fmc2->dma_data_complete, if (!wait_for_completion_timeout(&fmc2->dma_data_complete,
msecs_to_jiffies(100))) { msecs_to_jiffies(FMC2_TIMEOUT_MS))) {
dev_err(fmc2->dev, "data DMA timeout\n"); dev_err(fmc2->dev, "data DMA timeout\n");
dmaengine_terminate_all(dma_ch); dmaengine_terminate_all(dma_ch);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
...@@ -990,7 +993,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, ...@@ -990,7 +993,7 @@ static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf,
/* Wait DMA ECC transfer completion */ /* Wait DMA ECC transfer completion */
if (!write_data && !raw) { if (!write_data && !raw) {
if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete, if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete,
msecs_to_jiffies(100))) { msecs_to_jiffies(FMC2_TIMEOUT_MS))) {
dev_err(fmc2->dev, "ECC DMA timeout\n"); dev_err(fmc2->dev, "ECC DMA timeout\n");
dmaengine_terminate_all(fmc2->dma_ecc_ch); dmaengine_terminate_all(fmc2->dma_ecc_ch);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
...@@ -1909,6 +1912,12 @@ static int stm32_fmc2_probe(struct platform_device *pdev) ...@@ -1909,6 +1912,12 @@ static int stm32_fmc2_probe(struct platform_device *pdev)
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) {
if (irq != -EPROBE_DEFER)
dev_err(dev, "IRQ error missing or invalid\n");
return irq;
}
ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0, ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0,
dev_name(dev), fmc2); dev_name(dev), fmc2);
if (ret) { if (ret) {
......
# SPDX-License-Identifier: GPL-2.0 # 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 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -371,8 +371,7 @@ static int parse_afs_partitions(struct mtd_info *mtd, ...@@ -371,8 +371,7 @@ static int parse_afs_partitions(struct mtd_info *mtd,
out_free_parts: out_free_parts:
while (i >= 0) { while (i >= 0) {
if (parts[i].name) kfree(parts[i].name);
kfree(parts[i].name);
i--; i--;
} }
kfree(parts); kfree(parts);
......
...@@ -105,11 +105,4 @@ config SPI_INTEL_SPI_PLATFORM ...@@ -105,11 +105,4 @@ config SPI_INTEL_SPI_PLATFORM
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called intel-spi-platform. will be called intel-spi-platform.
config SPI_STM32_QUADSPI
tristate "STM32 Quad SPI controller"
depends on ARCH_STM32 || COMPILE_TEST
help
This enables support for the STM32 Quad SPI controller.
We only connect the NOR to this controller.
endif # MTD_SPI_NOR endif # MTD_SPI_NOR
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment