Raw NAND core changes:
* Rework of_get_nand_bus_width() * Remove of_get_nand_on_flash_bbt() wrapper * Protect access to rawnand devices while in suspend * bindings: Document the wp-gpios property Rax NAND controller driver changes: * atmel: Fix refcount issue in atmel_nand_controller_init * nandsim: - Add NS_PAGE_BYTE_SHIFT macro to replace the repeat pattern - Merge repeat codes in ns_switch_state - Replace overflow check with kzalloc to single kcalloc * rockchip: Fix platform_get_irq.cocci warning * stm32_fmc2: Add NAND Write Protect support * pl353: Set the nand chip node as the flash node * brcmnand: Fix sparse warnings in bcma_nand * omap_elm: Remove redundant variable 'errors' * gpmi: - Support fast edo timings for mx28 - Validate controller clock rate - Fix controller timings setting * brcmnand: - Add BCMA shim - BCMA controller uses command shift of 0 - Allow platform data instantation - Add platform data structure for BCMA - Allow working without interrupts - Move OF operations out of brcmnand_init_cs() - Avoid pdev in brcmnand_init_cs() - Allow SoC to provide I/O operations - Assign soc as early as possible Onenand changes: * Check for error irq -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmI021sACgkQJWrqGEe9 VoTEcggAiFXD+oR0VXTsykiNDiopsAUZwLPGuqk8gvD4ozOYCoAEoYrtnBM6Uybz W6Hu6Eow/ri1H+uVygUw3RYa4TpxrZrmZnJ1YimXxZjbLYjgE3FS9vzh2l4Bu3yo fkkQH+nFvk9qVIK8qolAny+LWl37gkSnCd6mPPksYaG5Ds1n1ZgyTZVUz5TOWAjG QAWUQQfO1iu7+u4CXa9JRTkCf55bT6v6c9Ryq6MA+ok6jVRN6Cj9WhxHtCB5vmOH Ndmu4V8BqaNKg39ltolqSPuwt3GEh707LRr+YakfnaOM7Sf8E/evn/THSkHwY/yn bAjpU1gvS13nJ++s8nHwIhHKhoYjTg== =4Uke -----END PGP SIGNATURE----- Merge tag 'nand/for-5.18' into mtd/next Raw NAND core changes: * Rework of_get_nand_bus_width() * Remove of_get_nand_on_flash_bbt() wrapper * Protect access to rawnand devices while in suspend * bindings: Document the wp-gpios property Rax NAND controller driver changes: * atmel: Fix refcount issue in atmel_nand_controller_init * nandsim: - Add NS_PAGE_BYTE_SHIFT macro to replace the repeat pattern - Merge repeat codes in ns_switch_state - Replace overflow check with kzalloc to single kcalloc * rockchip: Fix platform_get_irq.cocci warning * stm32_fmc2: Add NAND Write Protect support * pl353: Set the nand chip node as the flash node * brcmnand: Fix sparse warnings in bcma_nand * omap_elm: Remove redundant variable 'errors' * gpmi: - Support fast edo timings for mx28 - Validate controller clock rate - Fix controller timings setting * brcmnand: - Add BCMA shim - BCMA controller uses command shift of 0 - Allow platform data instantation - Add platform data structure for BCMA - Allow working without interrupts - Move OF operations out of brcmnand_init_cs() - Avoid pdev in brcmnand_init_cs() - Allow SoC to provide I/O operations - Assign soc as early as possible Onenand changes: * Check for error irq Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
commit
6cadd424ab
|
@ -116,6 +116,13 @@ patternProperties:
|
|||
Ready/Busy pins. Active state refers to the NAND ready state and
|
||||
should be set to GPIOD_ACTIVE_HIGH unless the signal is inverted.
|
||||
|
||||
wp-gpios:
|
||||
description:
|
||||
Contains one GPIO descriptor for the Write Protect pin.
|
||||
Active state refers to the NAND Write Protect state and should be
|
||||
set to GPIOD_ACTIVE_LOW unless the signal is inverted.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
|
|
|
@ -4026,6 +4026,7 @@ L: linux-mtd@lists.infradead.org
|
|||
L: bcm-kernel-feedback-list@broadcom.com
|
||||
S: Maintained
|
||||
F: drivers/mtd/nand/raw/brcmnand/
|
||||
F: include/linux/platform_data/brcmnand.h
|
||||
|
||||
BROADCOM STB PCIE DRIVER
|
||||
M: Jim Quinlan <jim2101024@gmail.com>
|
||||
|
|
|
@ -7,18 +7,28 @@
|
|||
|
||||
#include "bcma_private.h"
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/brcmnand.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
|
||||
/* Alternate NAND controller driver name in order to allow both bcm47xxnflash
|
||||
* and bcma_brcmnand to be built into the same kernel image.
|
||||
*/
|
||||
static const char *bcma_nflash_alt_name = "bcma_brcmnand";
|
||||
|
||||
struct platform_device bcma_nflash_dev = {
|
||||
.name = "bcma_nflash",
|
||||
.num_resources = 0,
|
||||
};
|
||||
|
||||
static const char *probes[] = { "bcm47xxpart", NULL };
|
||||
|
||||
/* Initialize NAND flash access */
|
||||
int bcma_nflash_init(struct bcma_drv_cc *cc)
|
||||
{
|
||||
struct bcma_bus *bus = cc->core->bus;
|
||||
u32 reg;
|
||||
|
||||
if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 &&
|
||||
cc->core->id.rev != 38) {
|
||||
|
@ -33,8 +43,16 @@ int bcma_nflash_init(struct bcma_drv_cc *cc)
|
|||
|
||||
cc->nflash.present = true;
|
||||
if (cc->core->id.rev == 38 &&
|
||||
(cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT))
|
||||
(cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT)) {
|
||||
cc->nflash.boot = true;
|
||||
/* Determine the chip select that is being used */
|
||||
reg = bcma_cc_read32(cc, BCMA_CC_NAND_CS_NAND_SELECT) & 0xff;
|
||||
cc->nflash.brcmnand_info.chip_select = ffs(reg) - 1;
|
||||
cc->nflash.brcmnand_info.part_probe_types = probes;
|
||||
cc->nflash.brcmnand_info.ecc_stepsize = 512;
|
||||
cc->nflash.brcmnand_info.ecc_strength = 1;
|
||||
bcma_nflash_dev.name = bcma_nflash_alt_name;
|
||||
}
|
||||
|
||||
/* Prepare platform device, but don't register it yet. It's too early,
|
||||
* malloc (required by device_private_init) is not available yet. */
|
||||
|
|
|
@ -53,7 +53,12 @@ static int generic_onenand_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
info->onenand.mmcontrol = pdata ? pdata->mmcontrol : NULL;
|
||||
info->onenand.irq = platform_get_irq(pdev, 0);
|
||||
|
||||
err = platform_get_irq(pdev, 0);
|
||||
if (err < 0)
|
||||
goto out_iounmap;
|
||||
|
||||
info->onenand.irq = err;
|
||||
|
||||
info->mtd.dev.parent = &pdev->dev;
|
||||
info->mtd.priv = &info->onenand;
|
||||
|
|
|
@ -210,6 +210,19 @@ config MTD_NAND_BRCMNAND
|
|||
originally designed for Set-Top Box but is used on various BCM7xxx,
|
||||
BCM3xxx, BCM63xxx, iProc/Cygnus and more.
|
||||
|
||||
if MTD_NAND_BRCMNAND
|
||||
|
||||
config MTD_NAND_BRCMNAND_BCMA
|
||||
tristate "Broadcom BCMA NAND controller"
|
||||
depends on BCMA_NFLASH
|
||||
depends on BCMA
|
||||
help
|
||||
Enables the BRCMNAND controller over BCMA on BCM47186/BCM5358 SoCs.
|
||||
The glue driver will take care of performing the low-level I/O
|
||||
operations to interface the BRCMNAND controller over the BCMA bus.
|
||||
|
||||
endif # MTD_NAND_BRCMNAND
|
||||
|
||||
config MTD_NAND_BCM47XXNFLASH
|
||||
tristate "BCM4706 BCMA NAND controller"
|
||||
depends on BCMA_NFLASH
|
||||
|
|
|
@ -2060,13 +2060,15 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
|||
nc->mck = of_clk_get(dev->parent->of_node, 0);
|
||||
if (IS_ERR(nc->mck)) {
|
||||
dev_err(dev, "Failed to retrieve MCK clk\n");
|
||||
return PTR_ERR(nc->mck);
|
||||
ret = PTR_ERR(nc->mck);
|
||||
goto out_release_dma;
|
||||
}
|
||||
|
||||
np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
|
||||
if (!np) {
|
||||
dev_err(dev, "Missing or invalid atmel,smc property\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto out_release_dma;
|
||||
}
|
||||
|
||||
nc->smc = syscon_node_to_regmap(np);
|
||||
|
@ -2074,10 +2076,16 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
|
|||
if (IS_ERR(nc->smc)) {
|
||||
ret = PTR_ERR(nc->smc);
|
||||
dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
|
||||
return ret;
|
||||
goto out_release_dma;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_release_dma:
|
||||
if (nc->dmac)
|
||||
dma_release_channel(nc->dmac);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -6,3 +6,5 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63138_nand.o
|
|||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm6368_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMA) += bcma_nand.o
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright © 2021 Broadcom
|
||||
*/
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/bcma/bcma_driver_chipcommon.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "brcmnand.h"
|
||||
|
||||
struct brcmnand_bcma_soc {
|
||||
struct brcmnand_soc soc;
|
||||
struct bcma_drv_cc *cc;
|
||||
};
|
||||
|
||||
static inline bool brcmnand_bcma_needs_swapping(u32 offset)
|
||||
{
|
||||
switch (offset) {
|
||||
case BCMA_CC_NAND_SPARE_RD0:
|
||||
case BCMA_CC_NAND_SPARE_RD4:
|
||||
case BCMA_CC_NAND_SPARE_RD8:
|
||||
case BCMA_CC_NAND_SPARE_RD12:
|
||||
case BCMA_CC_NAND_SPARE_WR0:
|
||||
case BCMA_CC_NAND_SPARE_WR4:
|
||||
case BCMA_CC_NAND_SPARE_WR8:
|
||||
case BCMA_CC_NAND_SPARE_WR12:
|
||||
case BCMA_CC_NAND_DEVID:
|
||||
case BCMA_CC_NAND_DEVID_X:
|
||||
case BCMA_CC_NAND_SPARE_RD16:
|
||||
case BCMA_CC_NAND_SPARE_RD20:
|
||||
case BCMA_CC_NAND_SPARE_RD24:
|
||||
case BCMA_CC_NAND_SPARE_RD28:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct brcmnand_bcma_soc *to_bcma_soc(struct brcmnand_soc *soc)
|
||||
{
|
||||
return container_of(soc, struct brcmnand_bcma_soc, soc);
|
||||
}
|
||||
|
||||
static u32 brcmnand_bcma_read_reg(struct brcmnand_soc *soc, u32 offset)
|
||||
{
|
||||
struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
|
||||
u32 val;
|
||||
|
||||
/* Offset into the NAND block and deal with the flash cache separately */
|
||||
if (offset == BRCMNAND_NON_MMIO_FC_ADDR)
|
||||
offset = BCMA_CC_NAND_CACHE_DATA;
|
||||
else
|
||||
offset += BCMA_CC_NAND_REVISION;
|
||||
|
||||
val = bcma_cc_read32(sc->cc, offset);
|
||||
|
||||
/* Swap if necessary */
|
||||
if (brcmnand_bcma_needs_swapping(offset))
|
||||
val = be32_to_cpu((__force __be32)val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void brcmnand_bcma_write_reg(struct brcmnand_soc *soc, u32 val,
|
||||
u32 offset)
|
||||
{
|
||||
struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
|
||||
|
||||
/* Offset into the NAND block */
|
||||
if (offset == BRCMNAND_NON_MMIO_FC_ADDR)
|
||||
offset = BCMA_CC_NAND_CACHE_DATA;
|
||||
else
|
||||
offset += BCMA_CC_NAND_REVISION;
|
||||
|
||||
/* Swap if necessary */
|
||||
if (brcmnand_bcma_needs_swapping(offset))
|
||||
val = (__force u32)cpu_to_be32(val);
|
||||
|
||||
bcma_cc_write32(sc->cc, offset, val);
|
||||
}
|
||||
|
||||
static struct brcmnand_io_ops brcmnand_bcma_io_ops = {
|
||||
.read_reg = brcmnand_bcma_read_reg,
|
||||
.write_reg = brcmnand_bcma_write_reg,
|
||||
};
|
||||
|
||||
static void brcmnand_bcma_prepare_data_bus(struct brcmnand_soc *soc, bool prepare,
|
||||
bool is_param)
|
||||
{
|
||||
struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
|
||||
|
||||
/* Reset the cache address to ensure we are already accessing the
|
||||
* beginning of a sub-page.
|
||||
*/
|
||||
bcma_cc_write32(sc->cc, BCMA_CC_NAND_CACHE_ADDR, 0);
|
||||
}
|
||||
|
||||
static int brcmnand_bcma_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
|
||||
struct brcmnand_bcma_soc *soc;
|
||||
|
||||
soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
|
||||
if (!soc)
|
||||
return -ENOMEM;
|
||||
|
||||
soc->cc = container_of(nflash, struct bcma_drv_cc, nflash);
|
||||
soc->soc.prepare_data_bus = brcmnand_bcma_prepare_data_bus;
|
||||
soc->soc.ops = &brcmnand_bcma_io_ops;
|
||||
|
||||
if (soc->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
|
||||
dev_err(&pdev->dev, "Use bcm47xxnflash for 4706!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return brcmnand_probe(pdev, &soc->soc);
|
||||
}
|
||||
|
||||
static struct platform_driver brcmnand_bcma_nand_driver = {
|
||||
.probe = brcmnand_bcma_nand_probe,
|
||||
.remove = brcmnand_remove,
|
||||
.driver = {
|
||||
.name = "bcma_brcmnand",
|
||||
.pm = &brcmnand_pm_ops,
|
||||
}
|
||||
};
|
||||
module_platform_driver(brcmnand_bcma_nand_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Broadcom");
|
||||
MODULE_DESCRIPTION("NAND controller driver glue for BCMA chips");
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/brcmnand.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
|
@ -207,13 +209,15 @@ enum {
|
|||
|
||||
struct brcmnand_host;
|
||||
|
||||
static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key);
|
||||
|
||||
struct brcmnand_controller {
|
||||
struct device *dev;
|
||||
struct nand_controller controller;
|
||||
void __iomem *nand_base;
|
||||
void __iomem *nand_fc; /* flash cache */
|
||||
void __iomem *flash_dma_base;
|
||||
unsigned int irq;
|
||||
int irq;
|
||||
unsigned int dma_irq;
|
||||
int nand_version;
|
||||
|
||||
|
@ -592,15 +596,29 @@ enum {
|
|||
INTFC_CTLR_READY = BIT(31),
|
||||
};
|
||||
|
||||
static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA)
|
||||
return static_branch_unlikely(&brcmnand_soc_has_ops_key);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
|
||||
{
|
||||
if (brcmnand_non_mmio_ops(ctrl))
|
||||
return brcmnand_soc_read(ctrl->soc, offs);
|
||||
return brcmnand_readl(ctrl->nand_base + offs);
|
||||
}
|
||||
|
||||
static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
|
||||
u32 val)
|
||||
{
|
||||
brcmnand_writel(val, ctrl->nand_base + offs);
|
||||
if (brcmnand_non_mmio_ops(ctrl))
|
||||
brcmnand_soc_write(ctrl->soc, val, offs);
|
||||
else
|
||||
brcmnand_writel(val, ctrl->nand_base + offs);
|
||||
}
|
||||
|
||||
static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
|
||||
|
@ -766,13 +784,18 @@ static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
|
|||
|
||||
static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
|
||||
{
|
||||
if (brcmnand_non_mmio_ops(ctrl))
|
||||
return brcmnand_soc_read(ctrl->soc, BRCMNAND_NON_MMIO_FC_ADDR);
|
||||
return __raw_readl(ctrl->nand_fc + word * 4);
|
||||
}
|
||||
|
||||
static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
|
||||
int word, u32 val)
|
||||
{
|
||||
__raw_writel(val, ctrl->nand_fc + word * 4);
|
||||
if (brcmnand_non_mmio_ops(ctrl))
|
||||
brcmnand_soc_write(ctrl->soc, val, BRCMNAND_NON_MMIO_FC_ADDR);
|
||||
else
|
||||
__raw_writel(val, ctrl->nand_fc + word * 4);
|
||||
}
|
||||
|
||||
static inline void edu_writel(struct brcmnand_controller *ctrl,
|
||||
|
@ -897,6 +920,12 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
|
|||
|
||||
static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
|
||||
{
|
||||
/* Kludge for the BCMA-based NAND controller which does not actually
|
||||
* shift the command
|
||||
*/
|
||||
if (ctrl->nand_version == 0x0304 && brcmnand_non_mmio_ops(ctrl))
|
||||
return 0;
|
||||
|
||||
if (ctrl->nand_version < 0x0602)
|
||||
return 24;
|
||||
return 0;
|
||||
|
@ -1592,7 +1621,7 @@ static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip)
|
|||
bool err = false;
|
||||
int sts;
|
||||
|
||||
if (mtd->oops_panic_write) {
|
||||
if (mtd->oops_panic_write || ctrl->irq < 0) {
|
||||
/* switch to interrupt polling and PIO mode */
|
||||
disable_ctrl_irqs(ctrl);
|
||||
sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY,
|
||||
|
@ -2750,33 +2779,27 @@ static const struct nand_controller_ops brcmnand_controller_ops = {
|
|||
.attach_chip = brcmnand_attach_chip,
|
||||
};
|
||||
|
||||
static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
||||
static int brcmnand_init_cs(struct brcmnand_host *host,
|
||||
const char * const *part_probe_types)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
struct platform_device *pdev = host->pdev;
|
||||
struct device *dev = ctrl->dev;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *chip;
|
||||
int ret;
|
||||
u16 cfg_offs;
|
||||
|
||||
ret = of_property_read_u32(dn, "reg", &host->cs);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get chip-select\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mtd = nand_to_mtd(&host->chip);
|
||||
chip = &host->chip;
|
||||
|
||||
nand_set_flash_node(chip, dn);
|
||||
nand_set_controller_data(chip, host);
|
||||
mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
|
||||
mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d",
|
||||
host->cs);
|
||||
if (!mtd->name)
|
||||
return -ENOMEM;
|
||||
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
mtd->dev.parent = dev;
|
||||
|
||||
chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
|
||||
chip->legacy.cmdfunc = brcmnand_cmdfunc;
|
||||
|
@ -2810,7 +2833,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
|
||||
if (ret)
|
||||
nand_cleanup(chip);
|
||||
|
||||
|
@ -2979,17 +3002,15 @@ static int brcmnand_edu_setup(struct platform_device *pdev)
|
|||
|
||||
int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
||||
{
|
||||
struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
struct brcmnand_controller *ctrl;
|
||||
struct brcmnand_host *host;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
/* We only support device-tree instantiation */
|
||||
if (!dn)
|
||||
return -ENODEV;
|
||||
|
||||
if (!of_match_node(brcmnand_of_match, dn))
|
||||
if (dn && !of_match_node(brcmnand_of_match, dn))
|
||||
return -ENODEV;
|
||||
|
||||
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
||||
|
@ -2998,6 +3019,13 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
|
||||
dev_set_drvdata(dev, ctrl);
|
||||
ctrl->dev = dev;
|
||||
ctrl->soc = soc;
|
||||
|
||||
/* Enable the static key if the soc provides I/O operations indicating
|
||||
* that a non-memory mapped IO access path must be used
|
||||
*/
|
||||
if (brcmnand_soc_has_ops(ctrl->soc))
|
||||
static_branch_enable(&brcmnand_soc_has_ops_key);
|
||||
|
||||
init_completion(&ctrl->done);
|
||||
init_completion(&ctrl->dma_done);
|
||||
|
@ -3009,7 +3037,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
/* NAND register range */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ctrl->nand_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ctrl->nand_base))
|
||||
if (IS_ERR(ctrl->nand_base) && !brcmnand_soc_has_ops(soc))
|
||||
return PTR_ERR(ctrl->nand_base);
|
||||
|
||||
/* Enable clock before using NAND registers */
|
||||
|
@ -3126,40 +3154,33 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
}
|
||||
|
||||
/* IRQ */
|
||||
ctrl->irq = platform_get_irq(pdev, 0);
|
||||
if ((int)ctrl->irq < 0) {
|
||||
dev_err(dev, "no IRQ defined\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
ctrl->irq = platform_get_irq_optional(pdev, 0);
|
||||
if (ctrl->irq > 0) {
|
||||
/*
|
||||
* Some SoCs integrate this controller (e.g., its interrupt bits) in
|
||||
* interesting ways
|
||||
*/
|
||||
if (soc) {
|
||||
ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
|
||||
DRV_NAME, ctrl);
|
||||
|
||||
/*
|
||||
* Some SoCs integrate this controller (e.g., its interrupt bits) in
|
||||
* interesting ways
|
||||
*/
|
||||
if (soc) {
|
||||
ctrl->soc = soc;
|
||||
|
||||
ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
|
||||
DRV_NAME, ctrl);
|
||||
|
||||
/* Enable interrupt */
|
||||
ctrl->soc->ctlrdy_ack(ctrl->soc);
|
||||
ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
|
||||
} else {
|
||||
/* Use standard interrupt infrastructure */
|
||||
ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
|
||||
DRV_NAME, ctrl);
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't allocate IRQ %d: error %d\n",
|
||||
ctrl->irq, ret);
|
||||
goto err;
|
||||
/* Enable interrupt */
|
||||
ctrl->soc->ctlrdy_ack(ctrl->soc);
|
||||
ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
|
||||
} else {
|
||||
/* Use standard interrupt infrastructure */
|
||||
ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
|
||||
DRV_NAME, ctrl);
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't allocate IRQ %d: error %d\n",
|
||||
ctrl->irq, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
if (of_device_is_compatible(child, "brcm,nandcs")) {
|
||||
struct brcmnand_host *host;
|
||||
|
||||
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host) {
|
||||
|
@ -3170,7 +3191,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
host->pdev = pdev;
|
||||
host->ctrl = ctrl;
|
||||
|
||||
ret = brcmnand_init_cs(host, child);
|
||||
ret = of_property_read_u32(child, "reg", &host->cs);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get chip-select\n");
|
||||
devm_kfree(dev, host);
|
||||
continue;
|
||||
}
|
||||
|
||||
nand_set_flash_node(&host->chip, child);
|
||||
|
||||
ret = brcmnand_init_cs(host, NULL);
|
||||
if (ret) {
|
||||
devm_kfree(dev, host);
|
||||
continue; /* Try all chip-selects */
|
||||
|
@ -3180,6 +3210,32 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|||
}
|
||||
}
|
||||
|
||||
if (!list_empty(&ctrl->host_list))
|
||||
return 0;
|
||||
|
||||
if (!pd) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* If we got there we must have been probing via platform data */
|
||||
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
host->pdev = pdev;
|
||||
host->ctrl = ctrl;
|
||||
host->cs = pd->chip_select;
|
||||
host->chip.ecc.size = pd->ecc_stepsize;
|
||||
host->chip.ecc.strength = pd->ecc_strength;
|
||||
|
||||
ret = brcmnand_init_cs(host, pd->part_probe_types);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
list_add_tail(&host->node, &ctrl->host_list);
|
||||
|
||||
/* No chip-selects could initialize properly */
|
||||
if (list_empty(&ctrl->host_list)) {
|
||||
ret = -ENODEV;
|
||||
|
|
|
@ -11,12 +11,25 @@
|
|||
|
||||
struct platform_device;
|
||||
struct dev_pm_ops;
|
||||
struct brcmnand_io_ops;
|
||||
|
||||
/* Special register offset constant to intercept a non-MMIO access
|
||||
* to the flash cache register space. This is intentionally large
|
||||
* not to overlap with an existing offset.
|
||||
*/
|
||||
#define BRCMNAND_NON_MMIO_FC_ADDR 0xffffffff
|
||||
|
||||
struct brcmnand_soc {
|
||||
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
|
||||
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
|
||||
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
|
||||
bool is_param);
|
||||
const struct brcmnand_io_ops *ops;
|
||||
};
|
||||
|
||||
struct brcmnand_io_ops {
|
||||
u32 (*read_reg)(struct brcmnand_soc *soc, u32 offset);
|
||||
void (*write_reg)(struct brcmnand_soc *soc, u32 val, u32 offset);
|
||||
};
|
||||
|
||||
static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc,
|
||||
|
@ -58,6 +71,22 @@ static inline void brcmnand_writel(u32 val, void __iomem *addr)
|
|||
writel_relaxed(val, addr);
|
||||
}
|
||||
|
||||
static inline bool brcmnand_soc_has_ops(struct brcmnand_soc *soc)
|
||||
{
|
||||
return soc && soc->ops && soc->ops->read_reg && soc->ops->write_reg;
|
||||
}
|
||||
|
||||
static inline u32 brcmnand_soc_read(struct brcmnand_soc *soc, u32 offset)
|
||||
{
|
||||
return soc->ops->read_reg(soc, offset);
|
||||
}
|
||||
|
||||
static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val,
|
||||
u32 offset)
|
||||
{
|
||||
soc->ops->write_reg(soc, val, offset);
|
||||
}
|
||||
|
||||
int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
|
||||
int brcmnand_remove(struct platform_device *pdev);
|
||||
|
||||
|
|
|
@ -644,10 +644,11 @@ err_out:
|
|||
* RDN_DELAY = ----------------------- {3}
|
||||
* RP
|
||||
*/
|
||||
static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
||||
const struct nand_sdr_timings *sdr)
|
||||
static int gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
||||
const struct nand_sdr_timings *sdr)
|
||||
{
|
||||
struct gpmi_nfc_hardware_timing *hw = &this->hw;
|
||||
struct resources *r = &this->resources;
|
||||
unsigned int dll_threshold_ps = this->devdata->max_chain_delay;
|
||||
unsigned int period_ps, reference_period_ps;
|
||||
unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles;
|
||||
|
@ -656,21 +657,33 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
|||
int sample_delay_ps, sample_delay_factor;
|
||||
u16 busy_timeout_cycles;
|
||||
u8 wrn_dly_sel;
|
||||
unsigned long clk_rate, min_rate;
|
||||
|
||||
if (sdr->tRC_min >= 30000) {
|
||||
/* ONFI non-EDO modes [0-3] */
|
||||
hw->clk_rate = 22000000;
|
||||
min_rate = 0;
|
||||
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
|
||||
} else if (sdr->tRC_min >= 25000) {
|
||||
/* ONFI EDO mode 4 */
|
||||
hw->clk_rate = 80000000;
|
||||
min_rate = 22000000;
|
||||
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
||||
} else {
|
||||
/* ONFI EDO mode 5 */
|
||||
hw->clk_rate = 100000000;
|
||||
min_rate = 80000000;
|
||||
wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
|
||||
}
|
||||
|
||||
clk_rate = clk_round_rate(r->clock[0], hw->clk_rate);
|
||||
if (clk_rate <= min_rate) {
|
||||
dev_err(this->dev, "clock setting: expected %ld, got %ld\n",
|
||||
hw->clk_rate, clk_rate);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
hw->clk_rate = clk_rate;
|
||||
/* SDR core timings are given in picoseconds */
|
||||
period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate);
|
||||
|
||||
|
@ -711,6 +724,7 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
|||
hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) |
|
||||
BM_GPMI_CTRL1_DLL_ENABLE |
|
||||
(use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
||||
|
@ -766,14 +780,15 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
|
|||
{
|
||||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||
const struct nand_sdr_timings *sdr;
|
||||
int ret;
|
||||
|
||||
/* Retrieve required NAND timings */
|
||||
sdr = nand_get_sdr_timings(conf);
|
||||
if (IS_ERR(sdr))
|
||||
return PTR_ERR(sdr);
|
||||
|
||||
/* Only MX6 GPMI controller can reach EDO timings */
|
||||
if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this))
|
||||
/* Only MX28/MX6 GPMI controller can reach EDO timings */
|
||||
if (sdr->tRC_min <= 25000 && !GPMI_IS_MX28(this) && !GPMI_IS_MX6(this))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Stop here if this call was just a check */
|
||||
|
@ -781,7 +796,9 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
|
|||
return 0;
|
||||
|
||||
/* Do the actual derivation of the controller timings */
|
||||
gpmi_nfc_compute_timings(this, sdr);
|
||||
ret = gpmi_nfc_compute_timings(this, sdr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
this->hw.must_apply_timings = true;
|
||||
|
||||
|
|
|
@ -338,16 +338,19 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
|
|||
*
|
||||
* Return: -EBUSY if the chip has been suspended, 0 otherwise
|
||||
*/
|
||||
static int nand_get_device(struct nand_chip *chip)
|
||||
static void nand_get_device(struct nand_chip *chip)
|
||||
{
|
||||
mutex_lock(&chip->lock);
|
||||
if (chip->suspended) {
|
||||
/* Wait until the device is resumed. */
|
||||
while (1) {
|
||||
mutex_lock(&chip->lock);
|
||||
if (!chip->suspended) {
|
||||
mutex_lock(&chip->controller->lock);
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&chip->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_lock(&chip->controller->lock);
|
||||
|
||||
return 0;
|
||||
wait_event(chip->resume_wq, !chip->suspended);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -576,9 +579,7 @@ 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 */
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
nand_get_device(chip);
|
||||
|
||||
ret = nand_markbad_bbm(chip, ofs);
|
||||
nand_release_device(chip);
|
||||
|
@ -3826,9 +3827,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
ops->mode != MTD_OPS_RAW)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
nand_get_device(chip);
|
||||
|
||||
if (!ops->datbuf)
|
||||
ret = nand_do_read_oob(chip, from, ops);
|
||||
|
@ -4415,13 +4414,11 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
ops->retlen = 0;
|
||||
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
nand_get_device(chip);
|
||||
|
||||
switch (ops->mode) {
|
||||
case MTD_OPS_PLACE_OOB:
|
||||
|
@ -4481,9 +4478,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
|
|||
return -EIO;
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
nand_get_device(chip);
|
||||
|
||||
/* Shift to get first page */
|
||||
page = (int)(instr->addr >> chip->page_shift);
|
||||
|
@ -4570,7 +4565,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 */
|
||||
WARN_ON(nand_get_device(chip));
|
||||
nand_get_device(chip);
|
||||
/* Release it and go back */
|
||||
nand_release_device(chip);
|
||||
}
|
||||
|
@ -4587,9 +4582,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
|
|||
int ret;
|
||||
|
||||
/* Select the NAND device */
|
||||
ret = nand_get_device(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
nand_get_device(chip);
|
||||
|
||||
nand_select_target(chip, chipnr);
|
||||
|
||||
|
@ -4660,6 +4653,8 @@ static void nand_resume(struct mtd_info *mtd)
|
|||
__func__);
|
||||
}
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
wake_up_all(&chip->resume_wq);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5274,25 +5269,24 @@ static void of_get_nand_ecc_legacy_user_config(struct nand_chip *chip)
|
|||
user_conf->placement = of_get_rawnand_ecc_placement_legacy(dn);
|
||||
}
|
||||
|
||||
static int of_get_nand_bus_width(struct device_node *np)
|
||||
static int of_get_nand_bus_width(struct nand_chip *chip)
|
||||
{
|
||||
struct device_node *dn = nand_get_flash_node(chip);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (of_property_read_u32(np, "nand-bus-width", &val))
|
||||
return 8;
|
||||
ret = of_property_read_u32(dn, "nand-bus-width", &val);
|
||||
if (ret == -EINVAL)
|
||||
/* Buswidth defaults to 8 if the property does not exist .*/
|
||||
return 0;
|
||||
else if (ret)
|
||||
return ret;
|
||||
|
||||
switch (val) {
|
||||
case 8:
|
||||
case 16:
|
||||
return val;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static bool of_get_nand_on_flash_bbt(struct device_node *np)
|
||||
{
|
||||
return of_property_read_bool(np, "nand-on-flash-bbt");
|
||||
if (val == 16)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
else if (val != 8)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_get_nand_secure_regions(struct nand_chip *chip)
|
||||
|
@ -5368,17 +5362,19 @@ static int rawnand_dt_init(struct nand_chip *chip)
|
|||
{
|
||||
struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip));
|
||||
struct device_node *dn = nand_get_flash_node(chip);
|
||||
int ret;
|
||||
|
||||
if (!dn)
|
||||
return 0;
|
||||
|
||||
if (of_get_nand_bus_width(dn) == 16)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
ret = of_get_nand_bus_width(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (of_property_read_bool(dn, "nand-is-boot-medium"))
|
||||
chip->options |= NAND_IS_BOOT_MEDIUM;
|
||||
|
||||
if (of_get_nand_on_flash_bbt(dn))
|
||||
if (of_property_read_bool(dn, "nand-on-flash-bbt"))
|
||||
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
|
||||
of_get_nand_ecc_user_config(nand);
|
||||
|
@ -5437,6 +5433,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
|
|||
chip->cur_cs = -1;
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
init_waitqueue_head(&chip->resume_wq);
|
||||
|
||||
/* Enforce the right timings for reset/detection */
|
||||
chip->current_interface_config = nand_get_reset_interface_config();
|
||||
|
|
|
@ -201,6 +201,9 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
|
|||
/* Calculate the OOB offset in flash RAM image by (row, column) address */
|
||||
#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
|
||||
|
||||
/* Calculate the byte shift in the next page to access */
|
||||
#define NS_PAGE_BYTE_SHIFT(ns) ((ns)->regs.column + (ns)->regs.off)
|
||||
|
||||
/* After a command is input, the simulator goes to one of the following states */
|
||||
#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */
|
||||
#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */
|
||||
|
@ -979,15 +982,8 @@ static int ns_read_error(unsigned int page_no)
|
|||
|
||||
static int ns_setup_wear_reporting(struct mtd_info *mtd)
|
||||
{
|
||||
size_t mem;
|
||||
|
||||
wear_eb_count = div_u64(mtd->size, mtd->erasesize);
|
||||
mem = wear_eb_count * sizeof(unsigned long);
|
||||
if (mem / sizeof(unsigned long) != wear_eb_count) {
|
||||
NS_ERR("Too many erase blocks for wear reporting\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
erase_block_wear = kzalloc(mem, GFP_KERNEL);
|
||||
erase_block_wear = kcalloc(wear_eb_count, sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!erase_block_wear) {
|
||||
NS_ERR("Too many erase blocks for wear reporting\n");
|
||||
return -ENOMEM;
|
||||
|
@ -1389,7 +1385,7 @@ static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
|
|||
*/
|
||||
static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
|
||||
{
|
||||
return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
|
||||
return NS_GET_PAGE(ns)->byte + NS_PAGE_BYTE_SHIFT(ns);
|
||||
}
|
||||
|
||||
static int ns_do_read_error(struct nandsim *ns, int num)
|
||||
|
@ -1415,7 +1411,7 @@ static void ns_do_bit_flips(struct nandsim *ns, int num)
|
|||
ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
|
||||
NS_WARN("read_page: flipping bit %d in page %d "
|
||||
"reading from %d ecc: corrected=%u failed=%u\n",
|
||||
pos, ns->regs.row, ns->regs.column + ns->regs.off,
|
||||
pos, ns->regs.row, NS_PAGE_BYTE_SHIFT(ns),
|
||||
nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
|
||||
}
|
||||
}
|
||||
|
@ -1437,7 +1433,7 @@ static void ns_read_page(struct nandsim *ns, int num)
|
|||
ssize_t tx;
|
||||
|
||||
NS_DBG("read_page: page %d written, reading from %d\n",
|
||||
ns->regs.row, ns->regs.column + ns->regs.off);
|
||||
ns->regs.row, NS_PAGE_BYTE_SHIFT(ns));
|
||||
if (ns_do_read_error(ns, num))
|
||||
return;
|
||||
pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
|
||||
|
@ -1458,7 +1454,7 @@ static void ns_read_page(struct nandsim *ns, int num)
|
|||
memset(ns->buf.byte, 0xFF, num);
|
||||
} else {
|
||||
NS_DBG("read_page: page %d allocated, reading from %d\n",
|
||||
ns->regs.row, ns->regs.column + ns->regs.off);
|
||||
ns->regs.row, NS_PAGE_BYTE_SHIFT(ns));
|
||||
if (ns_do_read_error(ns, num))
|
||||
return;
|
||||
memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
|
||||
|
@ -1509,7 +1505,7 @@ static int ns_prog_page(struct nandsim *ns, int num)
|
|||
int all;
|
||||
|
||||
NS_DBG("prog_page: writing page %d\n", ns->regs.row);
|
||||
pg_off = ns->file_buf + ns->regs.column + ns->regs.off;
|
||||
pg_off = ns->file_buf + NS_PAGE_BYTE_SHIFT(ns);
|
||||
off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
|
||||
if (!test_bit(ns->regs.row, ns->pages_written)) {
|
||||
all = 1;
|
||||
|
@ -1598,7 +1594,7 @@ static int ns_do_state_action(struct nandsim *ns, uint32_t action)
|
|||
NS_ERR("do_state_action: column number is too large\n");
|
||||
break;
|
||||
}
|
||||
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
|
||||
num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns);
|
||||
ns_read_page(ns, num);
|
||||
|
||||
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
|
||||
|
@ -1666,7 +1662,7 @@ static int ns_do_state_action(struct nandsim *ns, uint32_t action)
|
|||
return -1;
|
||||
}
|
||||
|
||||
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
|
||||
num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns);
|
||||
if (num != ns->regs.count) {
|
||||
NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
|
||||
ns->regs.count, num);
|
||||
|
@ -1738,14 +1734,6 @@ static void ns_switch_state(struct nandsim *ns)
|
|||
"state: %s, nxstate: %s\n",
|
||||
ns_get_state_name(ns->state),
|
||||
ns_get_state_name(ns->nxstate));
|
||||
|
||||
/* See, whether we need to do some action */
|
||||
if ((ns->state & ACTION_MASK) &&
|
||||
ns_do_state_action(ns, ns->state) < 0) {
|
||||
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* We don't yet know which operation we perform.
|
||||
|
@ -1762,12 +1750,13 @@ static void ns_switch_state(struct nandsim *ns)
|
|||
|
||||
if (ns_find_operation(ns, 0))
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ns->state & ACTION_MASK) &&
|
||||
ns_do_state_action(ns, ns->state) < 0) {
|
||||
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
|
||||
return;
|
||||
}
|
||||
/* See, whether we need to do some action */
|
||||
if ((ns->state & ACTION_MASK) &&
|
||||
ns_do_state_action(ns, ns->state) < 0) {
|
||||
ns_switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
|
||||
return;
|
||||
}
|
||||
|
||||
/* For 16x devices column means the page offset in words */
|
||||
|
@ -1817,7 +1806,7 @@ static void ns_switch_state(struct nandsim *ns)
|
|||
switch (NS_STATE(ns->state)) {
|
||||
case STATE_DATAIN:
|
||||
case STATE_DATAOUT:
|
||||
ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
|
||||
ns->regs.num = ns->geom.pgszoob - NS_PAGE_BYTE_SHIFT(ns);
|
||||
break;
|
||||
|
||||
case STATE_DATAOUT_ID:
|
||||
|
|
|
@ -282,7 +282,7 @@ static void elm_start_processing(struct elm_info *info,
|
|||
static void elm_error_correction(struct elm_info *info,
|
||||
struct elm_errorvec *err_vec)
|
||||
{
|
||||
int i, j, errors = 0;
|
||||
int i, j;
|
||||
int offset;
|
||||
u32 reg_val;
|
||||
|
||||
|
@ -312,8 +312,6 @@ static void elm_error_correction(struct elm_info *info,
|
|||
/* Update error location register */
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
errors += err_vec[i].error_count;
|
||||
} else {
|
||||
err_vec[i].error_uncorrectable = true;
|
||||
}
|
||||
|
|
|
@ -1062,7 +1062,7 @@ static int pl35x_nand_chip_init(struct pl35x_nandc *nfc,
|
|||
chip->controller = &nfc->controller;
|
||||
mtd = nand_to_mtd(chip);
|
||||
mtd->dev.parent = nfc->dev;
|
||||
nand_set_flash_node(chip, nfc->dev->of_node);
|
||||
nand_set_flash_node(chip, np);
|
||||
if (!mtd->name) {
|
||||
mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
|
||||
"%s", PL35X_NANDC_DRIVER_NAME);
|
||||
|
|
|
@ -1403,7 +1403,6 @@ static int rk_nfc_probe(struct platform_device *pdev)
|
|||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no NFC irq resource\n");
|
||||
ret = -EINVAL;
|
||||
goto clk_disable;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
@ -231,6 +232,7 @@ struct stm32_fmc2_timings {
|
|||
|
||||
struct stm32_fmc2_nand {
|
||||
struct nand_chip chip;
|
||||
struct gpio_desc *wp_gpio;
|
||||
struct stm32_fmc2_timings timings;
|
||||
int ncs;
|
||||
int cs_used[FMC2_MAX_CE];
|
||||
|
@ -1747,6 +1749,18 @@ static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = {
|
|||
.setup_interface = stm32_fmc2_nfc_setup_interface,
|
||||
};
|
||||
|
||||
static void stm32_fmc2_nfc_wp_enable(struct stm32_fmc2_nand *nand)
|
||||
{
|
||||
if (nand->wp_gpio)
|
||||
gpiod_set_value(nand->wp_gpio, 1);
|
||||
}
|
||||
|
||||
static void stm32_fmc2_nfc_wp_disable(struct stm32_fmc2_nand *nand)
|
||||
{
|
||||
if (nand->wp_gpio)
|
||||
gpiod_set_value(nand->wp_gpio, 0);
|
||||
}
|
||||
|
||||
static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
|
||||
struct device_node *dn)
|
||||
{
|
||||
|
@ -1785,6 +1799,18 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
|
|||
nand->cs_used[i] = cs;
|
||||
}
|
||||
|
||||
nand->wp_gpio = devm_gpiod_get_from_of_node(nfc->dev, dn,
|
||||
"wp-gpios", 0,
|
||||
GPIOD_OUT_HIGH, "wp");
|
||||
if (IS_ERR(nand->wp_gpio)) {
|
||||
ret = PTR_ERR(nand->wp_gpio);
|
||||
if (ret != -ENOENT)
|
||||
return dev_err_probe(nfc->dev, ret,
|
||||
"failed to request WP GPIO\n");
|
||||
|
||||
nand->wp_gpio = NULL;
|
||||
}
|
||||
|
||||
nand_set_flash_node(&nand->chip, dn);
|
||||
|
||||
return 0;
|
||||
|
@ -1956,10 +1982,12 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
|||
chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE |
|
||||
NAND_USES_DMA;
|
||||
|
||||
stm32_fmc2_nfc_wp_disable(nand);
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
ret = nand_scan(chip, nand->ncs);
|
||||
if (ret)
|
||||
goto err_release_dma;
|
||||
goto err_wp_enable;
|
||||
|
||||
ret = mtd_device_register(mtd, NULL, 0);
|
||||
if (ret)
|
||||
|
@ -1972,6 +2000,9 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
|||
err_nand_cleanup:
|
||||
nand_cleanup(chip);
|
||||
|
||||
err_wp_enable:
|
||||
stm32_fmc2_nfc_wp_enable(nand);
|
||||
|
||||
err_release_dma:
|
||||
if (nfc->dma_ecc_ch)
|
||||
dma_release_channel(nfc->dma_ecc_ch);
|
||||
|
@ -2012,15 +2043,20 @@ static int stm32_fmc2_nfc_remove(struct platform_device *pdev)
|
|||
|
||||
clk_disable_unprepare(nfc->clk);
|
||||
|
||||
stm32_fmc2_nfc_wp_enable(nand);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev);
|
||||
struct stm32_fmc2_nand *nand = &nfc->nand;
|
||||
|
||||
clk_disable_unprepare(nfc->clk);
|
||||
|
||||
stm32_fmc2_nfc_wp_enable(nand);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
|
@ -2042,6 +2078,8 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
|
|||
|
||||
stm32_fmc2_nfc_init(nfc);
|
||||
|
||||
stm32_fmc2_nfc_wp_disable(nand);
|
||||
|
||||
for (chip_cs = 0; chip_cs < FMC2_MAX_CE; chip_cs++) {
|
||||
if (!(nfc->cs_assigned & BIT(chip_cs)))
|
||||
continue;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#define LINUX_BCMA_DRIVER_CC_H_
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/brcmnand.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
/** ChipCommon core registers. **/
|
||||
|
@ -599,6 +600,10 @@ struct bcma_sflash {
|
|||
|
||||
#ifdef CONFIG_BCMA_NFLASH
|
||||
struct bcma_nflash {
|
||||
/* Must be the fist member for the brcmnand driver to
|
||||
* de-reference that structure.
|
||||
*/
|
||||
struct brcmnand_platform_data brcmnand_info;
|
||||
bool present;
|
||||
bool boot; /* This is the flash the SoC boots from */
|
||||
};
|
||||
|
|
|
@ -1240,6 +1240,7 @@ struct nand_secure_region {
|
|||
* @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
|
||||
* @resume_wq: wait queue to sleep if rawnand is in suspended state.
|
||||
* @cur_cs: Currently selected target. -1 means no target selected, otherwise we
|
||||
* should always have cur_cs >= 0 && cur_cs < nanddev_ntargets().
|
||||
* NAND Controller drivers should not modify this value, but they're
|
||||
|
@ -1294,6 +1295,7 @@ struct nand_chip {
|
|||
/* Internals */
|
||||
struct mutex lock;
|
||||
unsigned int suspended : 1;
|
||||
wait_queue_head_t resume_wq;
|
||||
int cur_cs;
|
||||
int read_retries;
|
||||
struct nand_secure_region *secure_regions;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef BRCMNAND_PLAT_DATA_H
|
||||
#define BRCMNAND_PLAT_DATA_H
|
||||
|
||||
struct brcmnand_platform_data {
|
||||
int chip_select;
|
||||
const char * const *part_probe_types;
|
||||
unsigned int ecc_stepsize;
|
||||
unsigned int ecc_strength;
|
||||
};
|
||||
|
||||
#endif /* BRCMNAND_PLAT_DATA_H */
|
Loading…
Reference in New Issue