NAND core changes:
- Fourth batch of fixes/cleanup to the raw NAND core impacting various controller drivers (Sunxi, Marvell, MTK, TMIO, OMAP2). - Checking the return code of nand_reset() and nand_readid_op(). - Removing ->legacy.erase and single_erase(). - Simplifying the locking. - Several implicit fall through annotations. Raw NAND controllers drivers changes: - Fixing various possible object reference leaks (MTK, JZ4780, Atmel). - ST: * Adding support for STM32 FMC2 NAND flash controller. - Meson: * Adding support for Amlogic NAND flash controller. - Denali: * Several cleanup patches. - Sunxi: * Several cleanup patches. - FSMC: * Disabling NAND on remove(). * Resetting NAND timings on resume(). SPI-NAND drivers changes: - Toshiba: * Adding support for all Toshiba products. - Macronix: * Fixing ECC status read. - Gigadevice: * Adding support for GD5F1GQ4UExxG. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAlxwE4kACgkQJWrqGEe9 VoRQVQf+OOkScM6ZbsO2GbMRe0ybMOCOyBXfojPEGtVv80gHHrAPRocLqWMB1Xdb MSfm7j+lyZU1I8eYVvdYXby7s5FlVWbrh8NSnnwox1QusdmyBm7vcxc6st15Poy/ /WtjE+Cry49eIx5L/pmmNH3AIqMdvSRTJD7NgOYjo/SIq4GLR+KQEgt1VLp/KyO0 TGeMDIT3Q3+MXVwvUh3YlHUwNJ2jOpjpPW94QxD0mef/b9doUlkvrOdHs3JeGQzX aiiqWI1nRBmjNs34wp7Dyf3G/feplKyi+IS/ghTiijAgwbWdRI7KxrPtyCUYiint 3XqVgZAF/sfFaKFMIiJBMzoLQB15dQ== =VbaE -----END PGP SIGNATURE----- Merge tag 'nand/for-5.1' of git://git.infradead.org/linux-mtd into mtd/next NAND core changes: - Fourth batch of fixes/cleanup to the raw NAND core impacting various controller drivers (Sunxi, Marvell, MTK, TMIO, OMAP2). - Checking the return code of nand_reset() and nand_readid_op(). - Removing ->legacy.erase and single_erase(). - Simplifying the locking. - Several implicit fall through annotations. Raw NAND controllers drivers changes: - Fixing various possible object reference leaks (MTK, JZ4780, Atmel). - ST: * Adding support for STM32 FMC2 NAND flash controller. - Meson: * Adding support for Amlogic NAND flash controller. - Denali: * Several cleanup patches. - Sunxi: * Several cleanup patches. - FSMC: * Disabling NAND on remove(). * Resetting NAND timings on resume(). SPI-NAND drivers changes: - Toshiba: * Adding support for all Toshiba products. - Macronix: * Fixing ECC status read. - Gigadevice: * Adding support for GD5F1GQ4UExxG.
This commit is contained in:
commit
9220d7befc
|
@ -0,0 +1,60 @@
|
|||
Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs
|
||||
|
||||
This file documents the properties in addition to those available in
|
||||
the MTD NAND bindings.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains one of:
|
||||
- "amlogic,meson-gxl-nfc"
|
||||
- "amlogic,meson-axg-nfc"
|
||||
- clocks :
|
||||
A list of phandle + clock-specifier pairs for the clocks listed
|
||||
in clock-names.
|
||||
|
||||
- clock-names: Should contain the following:
|
||||
"core" - NFC module gate clock
|
||||
"device" - device clock from eMMC sub clock controller
|
||||
"rx" - rx clock phase
|
||||
"tx" - tx clock phase
|
||||
|
||||
- amlogic,mmc-syscon : Required for NAND clocks, it's shared with SD/eMMC
|
||||
controller port C
|
||||
|
||||
Optional children nodes:
|
||||
Children nodes represent the available nand chips.
|
||||
|
||||
Other properties:
|
||||
see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
|
||||
|
||||
Example demonstrate on AXG SoC:
|
||||
|
||||
sd_emmc_c_clkc: mmc@7000 {
|
||||
compatible = "amlogic,meson-axg-mmc-clkc", "syscon";
|
||||
reg = <0x0 0x7000 0x0 0x800>;
|
||||
};
|
||||
|
||||
nand-controller@7800 {
|
||||
compatible = "amlogic,meson-axg-nfc";
|
||||
reg = <0x0 0x7800 0x0 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>;
|
||||
|
||||
clocks = <&clkc CLKID_SD_EMMC_C>,
|
||||
<&sd_emmc_c_clkc CLKID_MMC_DIV>,
|
||||
<&sd_emmc_c_clkc CLKID_MMC_PHASE_RX>,
|
||||
<&sd_emmc_c_clkc CLKID_MMC_PHASE_TX>;
|
||||
clock-names = "core", "device", "rx", "tx";
|
||||
amlogic,mmc-syscon = <&sd_emmc_c_clkc>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&nand_pins>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
nand-on-flash-bbt;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
STMicroelectronics Flexible Memory Controller 2 (FMC2)
|
||||
NAND Interface
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
* st,stm32mp15-fmc2
|
||||
- reg: NAND flash controller memory areas.
|
||||
First region contains the register location.
|
||||
Regions 2 to 4 respectively contain the data, command,
|
||||
and address space for CS0.
|
||||
Regions 5 to 7 contain the same areas for CS1.
|
||||
- interrupts: The interrupt number
|
||||
- pinctrl-0: Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
|
||||
- clocks: The clock needed by the NAND flash controller
|
||||
|
||||
Optional properties:
|
||||
- resets: Reference to a reset controller asserting the FMC controller
|
||||
- dmas: DMA specifiers (see: dma/stm32-mdma.txt)
|
||||
- dma-names: Must be "tx", "rx" and "ecc"
|
||||
|
||||
* NAND device bindings:
|
||||
|
||||
Required properties:
|
||||
- reg: describes the CS lines assigned to the NAND device.
|
||||
|
||||
Optional properties:
|
||||
- nand-on-flash-bbt: see nand.txt
|
||||
- nand-ecc-strength: see nand.txt
|
||||
- nand-ecc-step-size: see nand.txt
|
||||
|
||||
The following ECC strength and step size are currently supported:
|
||||
- nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Hamming)
|
||||
- nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
|
||||
- nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8) (default)
|
||||
|
||||
Example:
|
||||
|
||||
fmc: nand-controller@58002000 {
|
||||
compatible = "st,stm32mp15-fmc2";
|
||||
reg = <0x58002000 0x1000>,
|
||||
<0x80000000 0x1000>,
|
||||
<0x88010000 0x1000>,
|
||||
<0x88020000 0x1000>,
|
||||
<0x81000000 0x1000>,
|
||||
<0x89010000 0x1000>,
|
||||
<0x89020000 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&rcc FMC_K>;
|
||||
resets = <&rcc FMC_R>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&fmc_pins_a>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
nand@0 {
|
||||
reg = <0>;
|
||||
nand-on-flash-bbt;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
||||
};
|
|
@ -9849,6 +9849,13 @@ F: drivers/media/platform/meson/ao-cec.c
|
|||
F: Documentation/devicetree/bindings/media/meson-ao-cec.txt
|
||||
T: git git://linuxtv.org/media_tree.git
|
||||
|
||||
MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS
|
||||
M: Liang Yang <liang.yang@amlogic.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Maintained
|
||||
F: drivers/mtd/nand/raw/meson_*
|
||||
F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
|
||||
|
||||
MICROBLAZE ARCHITECTURE
|
||||
M: Michal Simek <monstr@monstr.eu>
|
||||
W: http://www.monstr.eu/fdt/
|
||||
|
|
|
@ -541,4 +541,21 @@ config MTD_NAND_TEGRA
|
|||
is supported. Extra OOB bytes when using HW ECC are currently
|
||||
not supported.
|
||||
|
||||
config MTD_NAND_STM32_FMC2
|
||||
tristate "Support for NAND controller on STM32MP SoCs"
|
||||
depends on MACH_STM32MP157 || COMPILE_TEST
|
||||
help
|
||||
Enables support for NAND Flash chips on SoCs containing the FMC2
|
||||
NAND controller. This controller is found on STM32MP SoCs.
|
||||
The controller supports a maximum 8k page size and supports
|
||||
a maximum 8-bit correction error per sector of 512 bytes.
|
||||
|
||||
config MTD_NAND_MESON
|
||||
tristate "Support for NAND controller on Amlogic's Meson SoCs"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enables support for NAND controller on Amlogic's Meson SoCs.
|
||||
This controller is found on Meson SoCs.
|
||||
|
||||
endif # MTD_NAND
|
||||
|
|
|
@ -56,6 +56,8 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
|
|||
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
|
||||
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o
|
||||
|
||||
nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
|
||||
nand-objs += nand_onfi.o
|
||||
|
|
|
@ -876,23 +876,32 @@ static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
|
|||
{
|
||||
struct platform_device *pdev;
|
||||
struct atmel_pmecc *pmecc, **ptr;
|
||||
int ret;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev || !platform_get_drvdata(pdev))
|
||||
if (!pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
pmecc = platform_get_drvdata(pdev);
|
||||
if (!pmecc) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_put_device;
|
||||
}
|
||||
|
||||
ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
get_device(&pdev->dev);
|
||||
pmecc = platform_get_drvdata(pdev);
|
||||
if (!ptr) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_device;
|
||||
}
|
||||
|
||||
*ptr = pmecc;
|
||||
|
||||
devres_add(userdev, ptr);
|
||||
|
||||
return pmecc;
|
||||
|
||||
err_put_device:
|
||||
put_device(&pdev->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
|
||||
|
|
|
@ -37,9 +37,6 @@
|
|||
#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */
|
||||
#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */
|
||||
|
||||
/* MAP10 commands */
|
||||
#define DENALI_ERASE 0x01
|
||||
|
||||
#define DENALI_BANK(denali) ((denali)->active_bank << 24)
|
||||
|
||||
#define DENALI_INVALID_BANK -1
|
||||
|
@ -476,7 +473,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
|
|||
}
|
||||
|
||||
static int denali_pio_read(struct denali_nand_info *denali, void *buf,
|
||||
size_t size, int page, int raw)
|
||||
size_t size, int page)
|
||||
{
|
||||
u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
|
||||
uint32_t *buf32 = (uint32_t *)buf;
|
||||
|
@ -504,7 +501,7 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,
|
|||
}
|
||||
|
||||
static int denali_pio_write(struct denali_nand_info *denali,
|
||||
const void *buf, size_t size, int page, int raw)
|
||||
const void *buf, size_t size, int page)
|
||||
{
|
||||
u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
|
||||
const uint32_t *buf32 = (uint32_t *)buf;
|
||||
|
@ -525,16 +522,16 @@ static int denali_pio_write(struct denali_nand_info *denali,
|
|||
}
|
||||
|
||||
static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
|
||||
size_t size, int page, int raw, int write)
|
||||
size_t size, int page, int write)
|
||||
{
|
||||
if (write)
|
||||
return denali_pio_write(denali, buf, size, page, raw);
|
||||
return denali_pio_write(denali, buf, size, page);
|
||||
else
|
||||
return denali_pio_read(denali, buf, size, page, raw);
|
||||
return denali_pio_read(denali, buf, size, page);
|
||||
}
|
||||
|
||||
static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
|
||||
size_t size, int page, int raw, int write)
|
||||
size_t size, int page, int write)
|
||||
{
|
||||
dma_addr_t dma_addr;
|
||||
uint32_t irq_mask, irq_status, ecc_err_mask;
|
||||
|
@ -544,7 +541,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
|
|||
dma_addr = dma_map_single(denali->dev, buf, size, dir);
|
||||
if (dma_mapping_error(denali->dev, dma_addr)) {
|
||||
dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
|
||||
return denali_pio_xfer(denali, buf, size, page, raw, write);
|
||||
return denali_pio_xfer(denali, buf, size, page, write);
|
||||
}
|
||||
|
||||
if (write) {
|
||||
|
@ -598,9 +595,9 @@ static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
|
|||
denali->reg + TRANSFER_SPARE_REG);
|
||||
|
||||
if (denali->dma_avail)
|
||||
return denali_dma_xfer(denali, buf, size, page, raw, write);
|
||||
return denali_dma_xfer(denali, buf, size, page, write);
|
||||
else
|
||||
return denali_pio_xfer(denali, buf, size, page, raw, write);
|
||||
return denali_pio_xfer(denali, buf, size, page, write);
|
||||
}
|
||||
|
||||
static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
|
@ -754,9 +751,6 @@ static int denali_read_oob(struct nand_chip *chip, int page)
|
|||
static int denali_write_oob(struct nand_chip *chip, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
|
||||
denali_reset_irq(denali);
|
||||
|
||||
denali_oob_xfer(mtd, chip, page, 1);
|
||||
|
||||
|
@ -903,23 +897,6 @@ static int denali_waitfunc(struct nand_chip *chip)
|
|||
return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
|
||||
}
|
||||
|
||||
static int denali_erase(struct nand_chip *chip, int page)
|
||||
{
|
||||
struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
|
||||
uint32_t irq_status;
|
||||
|
||||
denali_reset_irq(denali);
|
||||
|
||||
denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page,
|
||||
DENALI_ERASE);
|
||||
|
||||
/* wait for erase to complete or failure to occur */
|
||||
irq_status = denali_wait_for_irq(denali,
|
||||
INTR__ERASE_COMP | INTR__ERASE_FAIL);
|
||||
|
||||
return irq_status & INTR__ERASE_COMP ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
|
||||
const struct nand_data_interface *conf)
|
||||
{
|
||||
|
@ -1244,7 +1221,6 @@ static int denali_attach_chip(struct nand_chip *chip)
|
|||
chip->ecc.write_page_raw = denali_write_page_raw;
|
||||
chip->ecc.read_oob = denali_read_oob;
|
||||
chip->ecc.write_oob = denali_write_oob;
|
||||
chip->legacy.erase = denali_erase;
|
||||
|
||||
ret = denali_multidev_fixup(denali);
|
||||
if (ret)
|
||||
|
|
|
@ -304,7 +304,6 @@ struct denali_nand_info {
|
|||
u32 irq_status; /* interrupts that have happened */
|
||||
int irq;
|
||||
void *buf; /* for syndrome layout conversion */
|
||||
dma_addr_t dma_addr;
|
||||
int dma_avail; /* can support DMA? */
|
||||
int devs_per_cs; /* devices connected in parallel */
|
||||
int oob_skip_bytes; /* number of bytes reserved for BBM */
|
||||
|
|
|
@ -109,25 +109,17 @@ static int denali_dt_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(denali->host))
|
||||
return PTR_ERR(denali->host);
|
||||
|
||||
/*
|
||||
* A single anonymous clock is supported for the backward compatibility.
|
||||
* New platforms should support all the named clocks.
|
||||
*/
|
||||
dt->clk = devm_clk_get(dev, "nand");
|
||||
if (IS_ERR(dt->clk))
|
||||
dt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(dt->clk)) {
|
||||
dev_err(dev, "no clk available\n");
|
||||
return PTR_ERR(dt->clk);
|
||||
}
|
||||
|
||||
dt->clk_x = devm_clk_get(dev, "nand_x");
|
||||
if (IS_ERR(dt->clk_x))
|
||||
dt->clk_x = NULL;
|
||||
return PTR_ERR(dt->clk_x);
|
||||
|
||||
dt->clk_ecc = devm_clk_get(dev, "ecc");
|
||||
if (IS_ERR(dt->clk_ecc))
|
||||
dt->clk_ecc = NULL;
|
||||
return PTR_ERR(dt->clk_ecc);
|
||||
|
||||
ret = clk_prepare_enable(dt->clk);
|
||||
if (ret)
|
||||
|
@ -141,19 +133,8 @@ static int denali_dt_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto out_disable_clk_x;
|
||||
|
||||
if (dt->clk_x) {
|
||||
denali->clk_rate = clk_get_rate(dt->clk);
|
||||
denali->clk_x_rate = clk_get_rate(dt->clk_x);
|
||||
} else {
|
||||
/*
|
||||
* Hardcode the clock rates for the backward compatibility.
|
||||
* This works for both SOCFPGA and UniPhier.
|
||||
*/
|
||||
dev_notice(dev,
|
||||
"necessary clock is missing. default clock rates are used.\n");
|
||||
denali->clk_rate = 50000000;
|
||||
denali->clk_x_rate = 200000000;
|
||||
}
|
||||
denali->clk_rate = clk_get_rate(dt->clk);
|
||||
denali->clk_x_rate = clk_get_rate(dt->clk_x);
|
||||
|
||||
ret = denali_init(denali);
|
||||
if (ret)
|
||||
|
|
|
@ -986,6 +986,19 @@ static const struct nand_controller_ops fsmc_nand_controller_ops = {
|
|||
.setup_data_interface = fsmc_setup_data_interface,
|
||||
};
|
||||
|
||||
/**
|
||||
* fsmc_nand_disable() - Disables the NAND bank
|
||||
* @host: The instance to disable
|
||||
*/
|
||||
static void fsmc_nand_disable(struct fsmc_nand_data *host)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(host->regs_va + FSMC_PC);
|
||||
val &= ~FSMC_ENABLE;
|
||||
writel(val, host->regs_va + FSMC_PC);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_nand_probe - Probe function
|
||||
* @pdev: platform device structure
|
||||
|
@ -1141,6 +1154,7 @@ release_dma_read_chan:
|
|||
if (host->mode == USE_DMA_ACCESS)
|
||||
dma_release_channel(host->read_dma_chan);
|
||||
disable_clk:
|
||||
fsmc_nand_disable(host);
|
||||
clk_disable_unprepare(host->clk);
|
||||
|
||||
return ret;
|
||||
|
@ -1155,6 +1169,7 @@ static int fsmc_nand_remove(struct platform_device *pdev)
|
|||
|
||||
if (host) {
|
||||
nand_release(&host->nand);
|
||||
fsmc_nand_disable(host);
|
||||
|
||||
if (host->mode == USE_DMA_ACCESS) {
|
||||
dma_release_channel(host->write_dma_chan);
|
||||
|
@ -1185,6 +1200,7 @@ static int fsmc_nand_resume(struct device *dev)
|
|||
clk_prepare_enable(host->clk);
|
||||
if (host->dev_timings)
|
||||
fsmc_nand_setup(host, host->dev_timings);
|
||||
nand_reset(&host->nand, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -281,12 +281,15 @@ static struct jz4780_bch *jz4780_bch_get(struct device_node *np)
|
|||
struct jz4780_bch *bch;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev || !platform_get_drvdata(pdev))
|
||||
if (!pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
get_device(&pdev->dev);
|
||||
|
||||
bch = platform_get_drvdata(pdev);
|
||||
if (!bch) {
|
||||
put_device(&pdev->dev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
clk_prepare_enable(bch->clk);
|
||||
|
||||
return bch;
|
||||
|
|
|
@ -2550,9 +2550,8 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
|
|||
}
|
||||
|
||||
/* Alloc the nand chip structure */
|
||||
marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) +
|
||||
(nsels *
|
||||
sizeof(struct marvell_nand_chip_sel)),
|
||||
marvell_nand = devm_kzalloc(dev,
|
||||
struct_size(marvell_nand, sels, nsels),
|
||||
GFP_KERNEL);
|
||||
if (!marvell_nand) {
|
||||
dev_err(dev, "could not allocate chip structure\n");
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -267,11 +267,15 @@ static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
|
|||
struct mtk_ecc *ecc;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev || !platform_get_drvdata(pdev))
|
||||
if (!pdev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
get_device(&pdev->dev);
|
||||
ecc = platform_get_drvdata(pdev);
|
||||
if (!ecc) {
|
||||
put_device(&pdev->dev);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
clk_prepare_enable(ecc->clk);
|
||||
mtk_ecc_hw_init(ecc);
|
||||
|
||||
|
|
|
@ -1451,8 +1451,7 @@ static int mtk_nfc_probe(struct platform_device *pdev)
|
|||
if (!nfc)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&nfc->controller.lock);
|
||||
init_waitqueue_head(&nfc->controller.wq);
|
||||
nand_controller_init(&nfc->controller);
|
||||
INIT_LIST_HEAD(&nfc->chips);
|
||||
nfc->controller.ops = &mtk_nfc_controller_ops;
|
||||
|
||||
|
|
|
@ -278,11 +278,8 @@ EXPORT_SYMBOL_GPL(nand_deselect_target);
|
|||
static void nand_release_device(struct nand_chip *chip)
|
||||
{
|
||||
/* Release the controller and the chip */
|
||||
spin_lock(&chip->controller->lock);
|
||||
chip->controller->active = NULL;
|
||||
chip->state = FL_READY;
|
||||
wake_up(&chip->controller->wq);
|
||||
spin_unlock(&chip->controller->lock);
|
||||
mutex_unlock(&chip->controller->lock);
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -330,58 +327,24 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
|
|||
return nand_block_bad(chip, ofs);
|
||||
}
|
||||
|
||||
/**
|
||||
* panic_nand_get_device - [GENERIC] Get chip for selected access
|
||||
* @chip: the nand chip descriptor
|
||||
* @new_state: the state which is requested
|
||||
*
|
||||
* Used when in panic, no locks are taken.
|
||||
*/
|
||||
static void panic_nand_get_device(struct nand_chip *chip, int new_state)
|
||||
{
|
||||
/* Hardware controller shared among independent devices */
|
||||
chip->controller->active = chip;
|
||||
chip->state = new_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_get_device - [GENERIC] Get chip for selected access
|
||||
* @chip: NAND chip structure
|
||||
* @new_state: the state which is requested
|
||||
*
|
||||
* Get the device and lock it for exclusive access
|
||||
* Lock the device and its controller for exclusive access
|
||||
*
|
||||
* Return: -EBUSY if the chip has been suspended, 0 otherwise
|
||||
*/
|
||||
static int
|
||||
nand_get_device(struct nand_chip *chip, int new_state)
|
||||
static int nand_get_device(struct nand_chip *chip)
|
||||
{
|
||||
spinlock_t *lock = &chip->controller->lock;
|
||||
wait_queue_head_t *wq = &chip->controller->wq;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
retry:
|
||||
spin_lock(lock);
|
||||
|
||||
/* Hardware controller shared among independent devices */
|
||||
if (!chip->controller->active)
|
||||
chip->controller->active = chip;
|
||||
|
||||
if (chip->controller->active == chip && chip->state == FL_READY) {
|
||||
chip->state = new_state;
|
||||
spin_unlock(lock);
|
||||
return 0;
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (new_state == FL_PM_SUSPENDED) {
|
||||
if (chip->controller->active->state == FL_PM_SUSPENDED) {
|
||||
chip->state = FL_PM_SUSPENDED;
|
||||
spin_unlock(lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(wq, &wait);
|
||||
spin_unlock(lock);
|
||||
schedule();
|
||||
remove_wait_queue(wq, &wait);
|
||||
goto retry;
|
||||
mutex_lock(&chip->controller->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -457,7 +420,7 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
|
|||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int chipnr, page, status, len;
|
||||
int chipnr, page, status, len, ret;
|
||||
|
||||
pr_debug("%s: to = 0x%08x, len = %i\n",
|
||||
__func__, (unsigned int)to, (int)ops->ooblen);
|
||||
|
@ -479,7 +442,9 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
|
|||
* if we don't do this. I have no clue why, but I seem to have 'fixed'
|
||||
* it in the doc2000 driver in August 1999. dwmw2.
|
||||
*/
|
||||
nand_reset(chip, chipnr);
|
||||
ret = nand_reset(chip, chipnr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nand_select_target(chip, chipnr);
|
||||
|
||||
|
@ -602,7 +567,10 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
|
|||
nand_erase_nand(chip, &einfo, 0);
|
||||
|
||||
/* Write bad block marker to OOB */
|
||||
nand_get_device(chip, FL_WRITING);
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nand_markbad_bbm(chip, ofs);
|
||||
nand_release_device(chip);
|
||||
}
|
||||
|
@ -3580,7 +3548,9 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
ops->mode != MTD_OPS_RAW)
|
||||
return -ENOTSUPP;
|
||||
|
||||
nand_get_device(chip, FL_READING);
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ops->datbuf)
|
||||
ret = nand_do_read_oob(chip, from, ops);
|
||||
|
@ -4099,9 +4069,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
struct mtd_oob_ops ops;
|
||||
int ret;
|
||||
|
||||
/* Grab the device */
|
||||
panic_nand_get_device(chip, FL_WRITING);
|
||||
|
||||
nand_select_target(chip, chipnr);
|
||||
|
||||
/* Wait for the device to get ready */
|
||||
|
@ -4132,7 +4099,9 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
|
||||
ops->retlen = 0;
|
||||
|
||||
nand_get_device(chip, FL_WRITING);
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (ops->mode) {
|
||||
case MTD_OPS_PLACE_OOB:
|
||||
|
@ -4154,23 +4123,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* single_erase - [GENERIC] NAND standard block erase command function
|
||||
* @chip: NAND chip object
|
||||
* @page: the page address of the block which will be erased
|
||||
*
|
||||
* Standard erase command for NAND chips. Returns NAND status.
|
||||
*/
|
||||
static int single_erase(struct nand_chip *chip, int page)
|
||||
{
|
||||
unsigned int eraseblock;
|
||||
|
||||
/* Send commands to erase a block */
|
||||
eraseblock = page >> (chip->phys_erase_shift - chip->page_shift);
|
||||
|
||||
return nand_erase_op(chip, eraseblock);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_erase - [MTD Interface] erase block(s)
|
||||
* @mtd: MTD device structure
|
||||
|
@ -4194,7 +4146,7 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
||||
int allowbbt)
|
||||
{
|
||||
int page, status, pages_per_block, ret, chipnr;
|
||||
int page, pages_per_block, ret, chipnr;
|
||||
loff_t len;
|
||||
|
||||
pr_debug("%s: start = 0x%012llx, len = %llu\n",
|
||||
|
@ -4205,7 +4157,9 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
|||
return -EINVAL;
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
nand_get_device(chip, FL_ERASING);
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Shift to get first page */
|
||||
page = (int)(instr->addr >> chip->page_shift);
|
||||
|
@ -4246,17 +4200,11 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
|||
(page + pages_per_block))
|
||||
chip->pagebuf = -1;
|
||||
|
||||
if (chip->legacy.erase)
|
||||
status = chip->legacy.erase(chip,
|
||||
page & chip->pagemask);
|
||||
else
|
||||
status = single_erase(chip, page & chip->pagemask);
|
||||
|
||||
/* See if block erase succeeded */
|
||||
if (status) {
|
||||
ret = nand_erase_op(chip, (page & chip->pagemask) >>
|
||||
(chip->phys_erase_shift - chip->page_shift));
|
||||
if (ret) {
|
||||
pr_debug("%s: failed erase, page 0x%08x\n",
|
||||
__func__, page);
|
||||
ret = -EIO;
|
||||
instr->fail_addr =
|
||||
((loff_t)page << chip->page_shift);
|
||||
goto erase_exit;
|
||||
|
@ -4298,7 +4246,7 @@ static void nand_sync(struct mtd_info *mtd)
|
|||
pr_debug("%s: called\n", __func__);
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
nand_get_device(chip, FL_SYNCING);
|
||||
WARN_ON(nand_get_device(chip));
|
||||
/* Release it and go back */
|
||||
nand_release_device(chip);
|
||||
}
|
||||
|
@ -4315,7 +4263,10 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
|
|||
int ret;
|
||||
|
||||
/* Select the NAND device */
|
||||
nand_get_device(chip, FL_READING);
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nand_select_target(chip, chipnr);
|
||||
|
||||
ret = nand_block_checkbad(chip, offs, 0);
|
||||
|
@ -4388,7 +4339,13 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
*/
|
||||
static int nand_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
return nand_get_device(mtd_to_nand(mtd), FL_PM_SUSPENDED);
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
chip->suspended = 1;
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4399,11 +4356,13 @@ static void nand_resume(struct mtd_info *mtd)
|
|||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if (chip->state == FL_PM_SUSPENDED)
|
||||
nand_release_device(chip);
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended)
|
||||
chip->suspended = 0;
|
||||
else
|
||||
pr_err("%s called for a chip which is not in suspended state\n",
|
||||
__func__);
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4413,7 +4372,7 @@ static void nand_resume(struct mtd_info *mtd)
|
|||
*/
|
||||
static void nand_shutdown(struct mtd_info *mtd)
|
||||
{
|
||||
nand_get_device(mtd_to_nand(mtd), FL_PM_SUSPENDED);
|
||||
nand_suspend(mtd);
|
||||
}
|
||||
|
||||
/* Set default functions */
|
||||
|
@ -5018,6 +4977,8 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
|||
/* Assume all dies are deselected when we enter nand_scan_ident(). */
|
||||
chip->cur_cs = -1;
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
/* Enforce the right timings for reset/detection */
|
||||
onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
|
||||
|
||||
|
@ -5060,11 +5021,15 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
|||
u8 id[2];
|
||||
|
||||
/* See comment in nand_get_flash_type for reset */
|
||||
nand_reset(chip, i);
|
||||
ret = nand_reset(chip, i);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
nand_select_target(chip, i);
|
||||
/* Send the command for reading device ID */
|
||||
nand_readid_op(chip, 0, id, sizeof(id));
|
||||
ret = nand_readid_op(chip, 0, id, sizeof(id));
|
||||
if (ret)
|
||||
break;
|
||||
/* Read manufacturer and device IDs */
|
||||
if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
|
||||
nand_deselect_target(chip);
|
||||
|
@ -5555,6 +5520,7 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
}
|
||||
if (!ecc->read_page)
|
||||
ecc->read_page = nand_read_page_hwecc_oob_first;
|
||||
/* fall through */
|
||||
|
||||
case NAND_ECC_HW:
|
||||
/* Use standard hwecc read page function? */
|
||||
|
@ -5574,6 +5540,7 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
ecc->read_subpage = nand_read_subpage;
|
||||
if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
|
||||
ecc->write_subpage = nand_write_subpage_hwecc;
|
||||
/* fall through */
|
||||
|
||||
case NAND_ECC_HW_SYNDROME:
|
||||
if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
|
||||
|
@ -5611,6 +5578,7 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
ecc->size, mtd->writesize);
|
||||
ecc->mode = NAND_ECC_SOFT;
|
||||
ecc->algo = NAND_ECC_HAMMING;
|
||||
/* fall through */
|
||||
|
||||
case NAND_ECC_SOFT:
|
||||
ret = nand_set_ecc_soft_ops(chip);
|
||||
|
@ -5717,9 +5685,6 @@ static int nand_scan_tail(struct nand_chip *chip)
|
|||
}
|
||||
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
|
||||
|
||||
/* Initialize state */
|
||||
chip->state = FL_READY;
|
||||
|
||||
/* Invalidate the pagebuffer reference */
|
||||
chip->pagebuf = -1;
|
||||
|
||||
|
|
|
@ -331,6 +331,7 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
|
|||
*/
|
||||
if (column == -1 && page_addr == -1)
|
||||
return;
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
/*
|
||||
|
@ -483,7 +484,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
|
|||
chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
|
||||
NAND_NCE | NAND_CTRL_CHANGE);
|
||||
|
||||
/* This applies to read commands */
|
||||
/* fall through - This applies to read commands */
|
||||
default:
|
||||
/*
|
||||
* If we don't have access to the busy pin, we apply the given
|
||||
|
|
|
@ -994,12 +994,9 @@ static int omap_wait(struct nand_chip *this)
|
|||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(this));
|
||||
unsigned long timeo = jiffies;
|
||||
int status, state = this->state;
|
||||
int status;
|
||||
|
||||
if (state == FL_ERASING)
|
||||
timeo += msecs_to_jiffies(400);
|
||||
else
|
||||
timeo += msecs_to_jiffies(20);
|
||||
timeo += msecs_to_jiffies(400);
|
||||
|
||||
writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command);
|
||||
while (time_before(jiffies, timeo)) {
|
||||
|
@ -2173,11 +2170,8 @@ static const struct nand_controller_ops omap_nand_controller_ops = {
|
|||
};
|
||||
|
||||
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
||||
static struct nand_controller omap_gpmc_controller = {
|
||||
.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
|
||||
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
|
||||
.ops = &omap_nand_controller_ops,
|
||||
};
|
||||
static struct nand_controller omap_gpmc_controller;
|
||||
static bool omap_gpmc_controller_initialized;
|
||||
|
||||
static int omap_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -2227,6 +2221,12 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|||
|
||||
info->phys_base = res->start;
|
||||
|
||||
if (!omap_gpmc_controller_initialized) {
|
||||
omap_gpmc_controller.ops = &omap_nand_controller_ops;
|
||||
nand_controller_init(&omap_gpmc_controller);
|
||||
omap_gpmc_controller_initialized = true;
|
||||
}
|
||||
|
||||
nand_chip->controller = &omap_gpmc_controller;
|
||||
|
||||
nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R;
|
||||
|
|
|
@ -369,8 +369,7 @@ static int r852_wait(struct nand_chip *chip)
|
|||
unsigned long timeout;
|
||||
u8 status;
|
||||
|
||||
timeout = jiffies + (chip->state == FL_ERASING ?
|
||||
msecs_to_jiffies(400) : msecs_to_jiffies(20));
|
||||
timeout = jiffies + msecs_to_jiffies(400);
|
||||
|
||||
while (time_before(jiffies, timeout))
|
||||
if (chip->legacy.dev_ready(chip))
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -104,6 +104,7 @@
|
|||
|
||||
struct tmio_nand {
|
||||
struct nand_chip chip;
|
||||
struct completion comp;
|
||||
|
||||
struct platform_device *dev;
|
||||
|
||||
|
@ -168,15 +169,11 @@ static int tmio_nand_dev_ready(struct nand_chip *chip)
|
|||
static irqreturn_t tmio_irq(int irq, void *__tmio)
|
||||
{
|
||||
struct tmio_nand *tmio = __tmio;
|
||||
struct nand_chip *nand_chip = &tmio->chip;
|
||||
|
||||
/* disable RDYREQ interrupt */
|
||||
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
|
||||
complete(&tmio->comp);
|
||||
|
||||
if (unlikely(!waitqueue_active(&nand_chip->controller->wq)))
|
||||
dev_warn(&tmio->dev->dev, "spurious interrupt\n");
|
||||
|
||||
wake_up(&nand_chip->controller->wq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -193,18 +190,18 @@ static int tmio_nand_wait(struct nand_chip *nand_chip)
|
|||
u8 status;
|
||||
|
||||
/* enable RDYREQ interrupt */
|
||||
|
||||
tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR);
|
||||
reinit_completion(&tmio->comp);
|
||||
tmio_iowrite8(0x81, tmio->fcr + FCR_IMR);
|
||||
|
||||
timeout = wait_event_timeout(nand_chip->controller->wq,
|
||||
tmio_nand_dev_ready(nand_chip),
|
||||
msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20));
|
||||
timeout = 400;
|
||||
timeout = wait_for_completion_timeout(&tmio->comp,
|
||||
msecs_to_jiffies(timeout));
|
||||
|
||||
if (unlikely(!tmio_nand_dev_ready(nand_chip))) {
|
||||
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
|
||||
dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n",
|
||||
nand_chip->state == FL_ERASING ? "erase" : "program",
|
||||
nand_chip->state == FL_ERASING ? 400 : 20);
|
||||
dev_warn(&tmio->dev->dev, "still busy after 400 ms\n");
|
||||
|
||||
} else if (unlikely(!timeout)) {
|
||||
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
|
||||
|
@ -378,6 +375,8 @@ static int tmio_probe(struct platform_device *dev)
|
|||
if (!tmio)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&tmio->comp);
|
||||
|
||||
tmio->dev = dev;
|
||||
|
||||
platform_set_drvdata(dev, tmio);
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4)
|
||||
#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4)
|
||||
|
||||
#define GD5FXGQ4UEXXG_REG_STATUS2 0xf0
|
||||
|
||||
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),
|
||||
|
@ -81,11 +83,83 @@ static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int gd5fxgq4uexxg_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = 64;
|
||||
region->length = 64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gd5fxgq4uexxg_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section)
|
||||
return -ERANGE;
|
||||
|
||||
/* Reserve 1 bytes for the BBM. */
|
||||
region->offset = 1;
|
||||
region->length = 63;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
u8 status2;
|
||||
struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
|
||||
&status2);
|
||||
int ret;
|
||||
|
||||
switch (status & STATUS_ECC_MASK) {
|
||||
case STATUS_ECC_NO_BITFLIPS:
|
||||
return 0;
|
||||
|
||||
case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
|
||||
/*
|
||||
* Read status2 register to determine a more fine grained
|
||||
* bit error status
|
||||
*/
|
||||
ret = spi_mem_exec_op(spinand->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* 4 ... 7 bits are flipped (1..4 can't be detected, so
|
||||
* report the maximum of 4 in this case
|
||||
*/
|
||||
/* bits sorted this way (3...0): ECCS1,ECCS0,ECCSE1,ECCSE0 */
|
||||
return ((status & STATUS_ECC_MASK) >> 2) |
|
||||
((status2 & STATUS_ECC_MASK) >> 4);
|
||||
|
||||
case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
|
||||
return 8;
|
||||
|
||||
case STATUS_ECC_UNCOR_ERROR:
|
||||
return -EBADMSG;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
|
||||
.ecc = gd5fxgq4xa_ooblayout_ecc,
|
||||
.free = gd5fxgq4xa_ooblayout_free,
|
||||
};
|
||||
|
||||
static const struct mtd_ooblayout_ops gd5fxgq4uexxg_ooblayout = {
|
||||
.ecc = gd5fxgq4uexxg_ooblayout_ecc,
|
||||
.free = gd5fxgq4uexxg_ooblayout_free,
|
||||
};
|
||||
|
||||
static const struct spinand_info gigadevice_spinand_table[] = {
|
||||
SPINAND_INFO("GD5F1GQ4xA", 0xF1,
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
|
||||
|
@ -114,6 +188,15 @@ static const struct spinand_info gigadevice_spinand_table[] = {
|
|||
0,
|
||||
SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
|
||||
gd5fxgq4xa_ecc_get_status)),
|
||||
SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&gd5fxgq4uexxg_ooblayout,
|
||||
gd5fxgq4uexxg_ecc_get_status)),
|
||||
};
|
||||
|
||||
static int gigadevice_spinand_detect(struct spinand_device *spinand)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/mtd/spinand.h>
|
||||
|
||||
#define SPINAND_MFR_MACRONIX 0xC2
|
||||
#define MACRONIX_ECCSR_MASK 0x0F
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
|
@ -55,7 +56,12 @@ static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
|
|||
SPI_MEM_OP_DUMMY(1, 1),
|
||||
SPI_MEM_OP_DATA_IN(1, eccsr, 1));
|
||||
|
||||
return spi_mem_exec_op(spinand->spimem, &op);
|
||||
int ret = spi_mem_exec_op(spinand->spimem, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*eccsr &= MACRONIX_ECCSR_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
|
||||
|
|
|
@ -25,19 +25,19 @@ static SPINAND_OP_VARIANTS(write_cache_variants,
|
|||
static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 7)
|
||||
if (section > 0)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = 128 + 16 * section;
|
||||
region->length = 16;
|
||||
region->offset = mtd->oobsize / 2;
|
||||
region->length = mtd->oobsize / 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 0)
|
||||
|
@ -45,17 +45,17 @@ static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section,
|
|||
|
||||
/* 2 bytes reserved for BBM */
|
||||
region->offset = 2;
|
||||
region->length = 126;
|
||||
region->length = (mtd->oobsize / 2) - 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = {
|
||||
.ecc = tc58cvg2s0h_ooblayout_ecc,
|
||||
.free = tc58cvg2s0h_ooblayout_free,
|
||||
static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = {
|
||||
.ecc = tc58cxgxsx_ooblayout_ecc,
|
||||
.free = tc58cxgxsx_ooblayout_free,
|
||||
};
|
||||
|
||||
static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand,
|
||||
static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand,
|
||||
u8 status)
|
||||
{
|
||||
struct nand_device *nand = spinand_to_nand(spinand);
|
||||
|
@ -94,15 +94,66 @@ static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand,
|
|||
}
|
||||
|
||||
static const struct spinand_info toshiba_spinand_table[] = {
|
||||
SPINAND_INFO("TC58CVG2S0H", 0xCD,
|
||||
/* 3.3V 1Gb */
|
||||
SPINAND_INFO("TC58CVG0S3", 0xC2,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 3.3V 2Gb */
|
||||
SPINAND_INFO("TC58CVG1S3", 0xCB,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 3.3V 4Gb */
|
||||
SPINAND_INFO("TC58CVG2S0", 0xCD,
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout,
|
||||
tc58cvg2s0h_ecc_get_status)),
|
||||
0,
|
||||
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 1.8V 1Gb */
|
||||
SPINAND_INFO("TC58CYG0S3", 0xB2,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 1.8V 2Gb */
|
||||
SPINAND_INFO("TC58CYG1S3", 0xBB,
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
/* 1.8V 4Gb */
|
||||
SPINAND_INFO("TC58CYG2S0", 0xBD,
|
||||
NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&tc58cxgxsx_ooblayout,
|
||||
tc58cxgxsx_ecc_get_status)),
|
||||
};
|
||||
|
||||
static int toshiba_spinand_detect(struct spinand_device *spinand)
|
||||
|
|
|
@ -16,13 +16,12 @@
|
|||
#ifndef __LINUX_MTD_RAWNAND_H
|
||||
#define __LINUX_MTD_RAWNAND_H
|
||||
|
||||
#include <linux/wait.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/flashchip.h>
|
||||
#include <linux/mtd/bbm.h>
|
||||
#include <linux/mtd/jedec.h>
|
||||
#include <linux/mtd/onfi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
@ -897,25 +896,17 @@ struct nand_controller_ops {
|
|||
/**
|
||||
* struct nand_controller - Structure used to describe a NAND controller
|
||||
*
|
||||
* @lock: protection lock
|
||||
* @active: the mtd device which holds the controller currently
|
||||
* @wq: wait queue to sleep on if a NAND operation is in
|
||||
* progress used instead of the per chip wait queue
|
||||
* when a hw controller is available.
|
||||
* @lock: lock used to serialize accesses to the NAND controller
|
||||
* @ops: NAND controller operations.
|
||||
*/
|
||||
struct nand_controller {
|
||||
spinlock_t lock;
|
||||
struct nand_chip *active;
|
||||
wait_queue_head_t wq;
|
||||
struct mutex lock;
|
||||
const struct nand_controller_ops *ops;
|
||||
};
|
||||
|
||||
static inline void nand_controller_init(struct nand_controller *nfc)
|
||||
{
|
||||
nfc->active = NULL;
|
||||
spin_lock_init(&nfc->lock);
|
||||
init_waitqueue_head(&nfc->wq);
|
||||
mutex_init(&nfc->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -936,7 +927,6 @@ static inline void nand_controller_init(struct nand_controller *nfc)
|
|||
* @waitfunc: hardware specific function for wait on ready.
|
||||
* @block_bad: check if a block is bad, using OOB markers
|
||||
* @block_markbad: mark a block bad
|
||||
* @erase: erase function
|
||||
* @set_features: set the NAND chip features
|
||||
* @get_features: get the NAND chip features
|
||||
* @chip_delay: chip dependent delay for transferring data from array to read
|
||||
|
@ -962,7 +952,6 @@ struct nand_legacy {
|
|||
int (*waitfunc)(struct nand_chip *chip);
|
||||
int (*block_bad)(struct nand_chip *chip, loff_t ofs);
|
||||
int (*block_markbad)(struct nand_chip *chip, loff_t ofs);
|
||||
int (*erase)(struct nand_chip *chip, int page);
|
||||
int (*set_features)(struct nand_chip *chip, int feature_addr,
|
||||
u8 *subfeature_para);
|
||||
int (*get_features)(struct nand_chip *chip, int feature_addr,
|
||||
|
@ -983,7 +972,6 @@ struct nand_legacy {
|
|||
* setting the read-retry mode. Mostly needed for MLC NAND.
|
||||
* @ecc: [BOARDSPECIFIC] ECC control structure
|
||||
* @buf_align: minimum buffer alignment required by a platform
|
||||
* @state: [INTERN] the current state of the NAND device
|
||||
* @oob_poi: "poison value buffer," used for laying out OOB data
|
||||
* before writing
|
||||
* @page_shift: [INTERN] number of address bits in a page (column
|
||||
|
@ -1034,6 +1022,9 @@ struct nand_legacy {
|
|||
* cur_cs < numchips. NAND Controller drivers should not
|
||||
* modify this value, but they're allowed to read it.
|
||||
* @read_retries: [INTERN] the number of read retry modes supported
|
||||
* @lock: lock protecting the suspended field. Also used to
|
||||
* serialize accesses to the NAND device.
|
||||
* @suspended: set to 1 when the device is suspended, 0 when it's not.
|
||||
* @bbt: [INTERN] bad block table pointer
|
||||
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash
|
||||
* lookup.
|
||||
|
@ -1088,7 +1079,8 @@ struct nand_chip {
|
|||
|
||||
int read_retries;
|
||||
|
||||
flstate_t state;
|
||||
struct mutex lock;
|
||||
unsigned int suspended : 1;
|
||||
|
||||
uint8_t *oob_poi;
|
||||
struct nand_controller *controller;
|
||||
|
|
Loading…
Reference in New Issue