Raw NAND core:
* Export nand_read_page_hwecc_oob_first() GPMC memory controller for OMAP2 NAND controller: * GPMC: - Add support for AM64 SoC and allow build on K3 platforms - Use a compatible match table when checking for NAND controller - Use platform_get_irq() to get the interrupt Raw NAND controller drivers: * OMAP2 NAND controller: - Document the missing 'rb-gpios' DT property - Drop unused variable - Fix force_8bit flag behaviour for DMA mode - Move to exec_op interface - Use platform_get_irq() to get the interrupt * Renesas: - Add new NAND controller driver with its bindings and MAINTAINERS entry * Onenand: - Remove redundant variable ooblen * MPC5121: - Remove unused variable in ads5121_select_chip() * GPMI: - Add ERR007117 protection for nfc_apply_timings - Remove explicit default gpmi clock setting for i.MX6 - Use platform_get_irq_byname() to get the interrupt - Remove unneeded variable * Ingenic: - JZ4740 needs 'oob_first' read page function * Davinci: - Rewrite function description - Avoid duplicated page read - Don't calculate ECC when reading page -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmHO9D0ACgkQJWrqGEe9 VoRglwf/SsGcY+7vlBM1VSbUMIF/j5/xuigHIW6AxiN/hu672GMVi7nP7aHg4t3m DpO5X6ngYFHyW79uksRfwSun/JF1mOqF4De9zhs+cy8hlV12tgEmUNB3cbv4kuFQ e/N8/mlNrNJ2oQ1C3G3HWe3sngh2vrkhuG49t7M2TJaGAo7V4RW3z9+glNpWMb7m YIj6gXPjPx7vuEWv80GBUcWNKBzNdRVkrQnj3C2I/ZCslnwYWBLBQxX/U9/KDkPH 7i7MgeknAlV0G/W4TfnNSAKiWOcmWdbmuJ/lOWv4+skdXoMb0/rlC6kvJqOLvbF6 2nUrc9yFTwkNfVTGJm5eWJ0yDUHMgA== =DQck -----END PGP SIGNATURE----- Merge tag 'nand/for-5.17' into mtd/next Raw NAND core: * Export nand_read_page_hwecc_oob_first() GPMC memory controller for OMAP2 NAND controller: * GPMC: - Add support for AM64 SoC and allow build on K3 platforms - Use a compatible match table when checking for NAND controller - Use platform_get_irq() to get the interrupt Raw NAND controller drivers: * OMAP2 NAND controller: - Document the missing 'rb-gpios' DT property - Drop unused variable - Fix force_8bit flag behaviour for DMA mode - Move to exec_op interface - Use platform_get_irq() to get the interrupt * Renesas: - Add new NAND controller driver with its bindings and MAINTAINERS entry * Onenand: - Remove redundant variable ooblen * MPC5121: - Remove unused variable in ads5121_select_chip() * GPMI: - Add ERR007117 protection for nfc_apply_timings - Remove explicit default gpmi clock setting for i.MX6 - Use platform_get_irq_byname() to get the interrupt - Remove unneeded variable * Ingenic: - JZ4740 needs 'oob_first' read page function * Davinci: - Rewrite function description - Avoid duplicated page read - Don't calculate ECC when reading page Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
commit
9ce47e43a0
@ -23,13 +23,20 @@ properties:
|
||||
items:
|
||||
- enum:
|
||||
- ti,am3352-gpmc
|
||||
- ti,am64-gpmc
|
||||
- ti,omap2420-gpmc
|
||||
- ti,omap2430-gpmc
|
||||
- ti,omap3430-gpmc
|
||||
- ti,omap4430-gpmc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: cfg
|
||||
- const: data
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -44,6 +51,9 @@ properties:
|
||||
items:
|
||||
- const: fck
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: DMA channel for GPMC NAND prefetch
|
||||
@ -133,6 +143,17 @@ required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: ti,am64-gpmc
|
||||
then:
|
||||
required:
|
||||
- reg-names
|
||||
- power-domains
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
61
Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
Normal file
61
Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
|
||||
|
||||
maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "nand-controller.yaml"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-nandc
|
||||
- const: renesas,rzn1-nandc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: APB host controller clock
|
||||
- description: External NAND bus clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: hclk
|
||||
- const: eclk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
|
||||
|
||||
nand-controller@40102000 {
|
||||
compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
|
||||
reg = <0x40102000 0x2000>;
|
||||
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
|
||||
clock-names = "hclk", "eclk";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -16,7 +16,10 @@ description:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,omap2-nand
|
||||
items:
|
||||
- enum:
|
||||
- ti,am64-nand
|
||||
- ti,omap2-nand
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -53,6 +56,11 @@ properties:
|
||||
enum: [8, 16]
|
||||
default: 8
|
||||
|
||||
rb-gpios:
|
||||
description:
|
||||
GPIO connection to R/B signal from NAND chip
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
"@[0-9a-f]+$":
|
||||
$ref: "/schemas/mtd/partitions/partition.yaml"
|
||||
|
@ -16275,6 +16275,14 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
|
||||
F: drivers/iio/adc/rzg2l_adc.c
|
||||
|
||||
RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
|
||||
F: drivers/mtd/nand/raw/renesas-nand-controller.c
|
||||
|
||||
RESET CONTROLLER FRAMEWORK
|
||||
M: Philipp Zabel <p.zabel@pengutronix.de>
|
||||
S: Maintained
|
||||
|
@ -237,6 +237,7 @@ struct gpmc_device {
|
||||
struct omap3_gpmc_regs context;
|
||||
int nirqs;
|
||||
unsigned int is_suspended:1;
|
||||
struct resource *data;
|
||||
};
|
||||
|
||||
static struct irq_domain *gpmc_irq_domain;
|
||||
@ -1456,12 +1457,18 @@ static void gpmc_mem_exit(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void gpmc_mem_init(void)
|
||||
static void gpmc_mem_init(struct gpmc_device *gpmc)
|
||||
{
|
||||
int cs;
|
||||
|
||||
gpmc_mem_root.start = GPMC_MEM_START;
|
||||
gpmc_mem_root.end = GPMC_MEM_END;
|
||||
if (!gpmc->data) {
|
||||
/* All legacy devices have same data IO window */
|
||||
gpmc_mem_root.start = GPMC_MEM_START;
|
||||
gpmc_mem_root.end = GPMC_MEM_END;
|
||||
} else {
|
||||
gpmc_mem_root.start = gpmc->data->start;
|
||||
gpmc_mem_root.end = gpmc->data->end;
|
||||
}
|
||||
|
||||
/* Reserve all regions that has been set up by bootloader */
|
||||
for (cs = 0; cs < gpmc_cs_num; cs++) {
|
||||
@ -1888,6 +1895,7 @@ static const struct of_device_id gpmc_dt_ids[] = {
|
||||
{ .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */
|
||||
{ .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */
|
||||
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
|
||||
{ .compatible = "ti,am64-gpmc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -2175,7 +2183,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
|
||||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(child, "ti,omap2-nand")) {
|
||||
if (of_match_node(omap_nand_ids, child)) {
|
||||
/* NAND specific setup */
|
||||
val = 8;
|
||||
of_property_read_u32(child, "nand-bus-width", &val);
|
||||
@ -2502,21 +2510,29 @@ static int gpmc_probe(struct platform_device *pdev)
|
||||
gpmc->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, gpmc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENOENT;
|
||||
|
||||
gpmc_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(gpmc_base))
|
||||
return PTR_ERR(gpmc_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource: irq\n");
|
||||
return -ENOENT;
|
||||
/* legacy DT */
|
||||
gpmc_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(gpmc_base))
|
||||
return PTR_ERR(gpmc_base);
|
||||
} else {
|
||||
gpmc_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(gpmc_base))
|
||||
return PTR_ERR(gpmc_base);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "couldn't get data reg resource\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
gpmc->data = res;
|
||||
}
|
||||
|
||||
gpmc->irq = res->start;
|
||||
gpmc->irq = platform_get_irq(pdev, 0);
|
||||
if (gpmc->irq < 0)
|
||||
return gpmc->irq;
|
||||
|
||||
gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
|
||||
if (IS_ERR(gpmc_l3_clk)) {
|
||||
@ -2562,7 +2578,7 @@ static int gpmc_probe(struct platform_device *pdev)
|
||||
dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
|
||||
GPMC_REVISION_MINOR(l));
|
||||
|
||||
gpmc_mem_init();
|
||||
gpmc_mem_init(gpmc);
|
||||
rc = gpmc_gpio_init(gpmc);
|
||||
if (rc)
|
||||
goto gpio_init_failed;
|
||||
|
@ -60,7 +60,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
int i, j, numblocks, len, scanlen;
|
||||
int startblock;
|
||||
loff_t from;
|
||||
size_t readlen, ooblen;
|
||||
size_t readlen;
|
||||
struct mtd_oob_ops ops;
|
||||
int rgn;
|
||||
|
||||
@ -69,7 +69,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
len = 2;
|
||||
|
||||
/* We need only read few bytes from the OOB area */
|
||||
scanlen = ooblen = 0;
|
||||
scanlen = 0;
|
||||
readlen = bd->len;
|
||||
|
||||
/* chip == -1 case only */
|
||||
|
@ -40,8 +40,9 @@ config MTD_NAND_AMS_DELTA
|
||||
|
||||
config MTD_NAND_OMAP2
|
||||
tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller"
|
||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
|
||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select OMAP_GPMC if ARCH_K3
|
||||
help
|
||||
Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
|
||||
and Keystone platforms.
|
||||
@ -461,6 +462,13 @@ config MTD_NAND_PL35X
|
||||
Enables support for PrimeCell SMC PL351 and PL353 NAND
|
||||
controller found on Zynq7000.
|
||||
|
||||
config MTD_NAND_RENESAS
|
||||
tristate "Renesas R-Car Gen3 & RZ/N1 NAND controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
Enables support for the NAND controller found on Renesas R-Car
|
||||
Gen3 and RZ/N1 SoC families.
|
||||
|
||||
comment "Misc"
|
||||
|
||||
config MTD_SM_COMMON
|
||||
|
@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_ARASAN) += arasan-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_ROCKCHIP) += rockchip-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_PL35X) += pl35x-nand-controller.o
|
||||
obj-$(CONFIG_MTD_NAND_RENESAS) += renesas-nand-controller.o
|
||||
|
||||
nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
|
||||
nand-objs += nand_onfi.o
|
||||
|
@ -371,77 +371,6 @@ correct:
|
||||
return corrected;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_read_page_hwecc_oob_first - hw ecc, read oob first
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Hardware ECC for large page chips, require OOB to be read first. For this
|
||||
* ECC mode, the write_page method is re-used from ECC_HW. These methods
|
||||
* read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
|
||||
* multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
|
||||
* the data area, by overwriting the NAND manufacturer bad block markings.
|
||||
*/
|
||||
static int nand_davinci_read_page_hwecc_oob_first(struct nand_chip *chip,
|
||||
uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int i, eccsize = chip->ecc.size, ret;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_code = chip->ecc.code_buf;
|
||||
uint8_t *ecc_calc = chip->ecc.calc_buf;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/* Read the OOB area first */
|
||||
ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
||||
chip->ecc.total);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
int stat;
|
||||
|
||||
chip->ecc.hwctl(chip, NAND_ECC_READ);
|
||||
|
||||
ret = nand_read_data_op(chip, p, eccsize, false, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
chip->ecc.calculate(chip, p, &ecc_calc[i]);
|
||||
|
||||
stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
|
||||
if (stat == -EBADMSG &&
|
||||
(chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
|
||||
/* check for empty pages with bitflips */
|
||||
stat = nand_check_erased_ecc_chunk(p, eccsize,
|
||||
&ecc_code[i],
|
||||
eccbytes, NULL, 0,
|
||||
chip->ecc.strength);
|
||||
}
|
||||
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* An ECC layout for using 4-bit ECC with small-page flash, storing
|
||||
@ -651,7 +580,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
|
||||
} else if (chunks == 4 || chunks == 8) {
|
||||
mtd_set_ooblayout(mtd,
|
||||
nand_get_large_page_ooblayout());
|
||||
chip->ecc.read_page = nand_davinci_read_page_hwecc_oob_first;
|
||||
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -713,14 +713,32 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
|
||||
(use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
|
||||
}
|
||||
|
||||
static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
||||
static int gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct gpmi_nfc_hardware_timing *hw = &this->hw;
|
||||
struct resources *r = &this->resources;
|
||||
void __iomem *gpmi_regs = r->gpmi_regs;
|
||||
unsigned int dll_wait_time_us;
|
||||
int ret;
|
||||
|
||||
clk_set_rate(r->clock[0], hw->clk_rate);
|
||||
/* Clock dividers do NOT guarantee a clean clock signal on its output
|
||||
* during the change of the divide factor on i.MX6Q/UL/SX. On i.MX7/8,
|
||||
* all clock dividers provide these guarantee.
|
||||
*/
|
||||
if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this))
|
||||
clk_disable_unprepare(r->clock[0]);
|
||||
|
||||
ret = clk_set_rate(r->clock[0], hw->clk_rate);
|
||||
if (ret) {
|
||||
dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) {
|
||||
ret = clk_prepare_enable(r->clock[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0);
|
||||
writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1);
|
||||
@ -739,6 +757,8 @@ static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
|
||||
|
||||
/* Wait for the DLL to settle. */
|
||||
udelay(dll_wait_time_us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
@ -971,16 +991,13 @@ static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
|
||||
{
|
||||
struct platform_device *pdev = this->pdev;
|
||||
const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
|
||||
struct resource *r;
|
||||
int err;
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
|
||||
if (!r) {
|
||||
dev_err(this->dev, "Can't get resource for %s\n", res_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
err = platform_get_irq_byname(pdev, res_name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this);
|
||||
err = devm_request_irq(this->dev, err, irq_h, 0, res_name, this);
|
||||
if (err)
|
||||
dev_err(this->dev, "error requesting BCH IRQ\n");
|
||||
|
||||
@ -1032,15 +1049,6 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
|
||||
r->clock[i] = clk;
|
||||
}
|
||||
|
||||
if (GPMI_IS_MX6(this))
|
||||
/*
|
||||
* Set the default value for the gpmi clock.
|
||||
*
|
||||
* If you want to use the ONFI nand which is in the
|
||||
* Synchronous Mode, you should change the clock as you need.
|
||||
*/
|
||||
clk_set_rate(r->clock[0], 22000000);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clock:
|
||||
@ -1425,7 +1433,6 @@ static int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct gpmi_nand_data *this = nand_get_controller_data(chip);
|
||||
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
||||
int ret;
|
||||
|
||||
dev_dbg(this->dev, "ecc write page.\n");
|
||||
|
||||
@ -1445,9 +1452,7 @@ static int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
|
||||
this->auxiliary_virt);
|
||||
}
|
||||
|
||||
ret = nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size);
|
||||
|
||||
return ret;
|
||||
return nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2278,7 +2283,9 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
|
||||
*/
|
||||
if (this->hw.must_apply_timings) {
|
||||
this->hw.must_apply_timings = false;
|
||||
gpmi_nfc_apply_timings(this);
|
||||
ret = gpmi_nfc_apply_timings(this);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs);
|
||||
|
@ -32,6 +32,7 @@ struct jz_soc_info {
|
||||
unsigned long addr_offset;
|
||||
unsigned long cmd_offset;
|
||||
const struct mtd_ooblayout_ops *oob_layout;
|
||||
bool oob_first;
|
||||
};
|
||||
|
||||
struct ingenic_nand_cs {
|
||||
@ -240,6 +241,9 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip)
|
||||
if (chip->bbt_options & NAND_BBT_USE_FLASH)
|
||||
chip->bbt_options |= NAND_BBT_NO_OOB;
|
||||
|
||||
if (nfc->soc_info->oob_first)
|
||||
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
|
||||
|
||||
/* For legacy reasons we use a different layout on the qi,lb60 board. */
|
||||
if (of_machine_is_compatible("qi,lb60"))
|
||||
mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops);
|
||||
@ -534,6 +538,7 @@ static const struct jz_soc_info jz4740_soc_info = {
|
||||
.data_offset = 0x00000000,
|
||||
.cmd_offset = 0x00008000,
|
||||
.addr_offset = 0x00010000,
|
||||
.oob_first = true,
|
||||
};
|
||||
|
||||
static const struct jz_soc_info jz4725b_soc_info = {
|
||||
|
@ -291,7 +291,6 @@ static int ads5121_chipselect_init(struct mtd_info *mtd)
|
||||
/* Control chips select signal on ADS5121 board */
|
||||
static void ads5121_select_chip(struct nand_chip *nand, int chip)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
|
||||
u8 v;
|
||||
|
||||
|
@ -3163,6 +3163,73 @@ static int nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_read_page_hwecc_oob_first - Hardware ECC page read with ECC
|
||||
* data read from OOB area
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Hardware ECC for large page chips, which requires the ECC data to be
|
||||
* extracted from the OOB before the actual data is read.
|
||||
*/
|
||||
int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
int i, eccsize = chip->ecc.size, ret;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_code = chip->ecc.code_buf;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/* Read the OOB area first */
|
||||
ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Move read cursor to start of page */
|
||||
ret = nand_change_read_column_op(chip, 0, NULL, 0, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
|
||||
chip->ecc.total);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
int stat;
|
||||
|
||||
chip->ecc.hwctl(chip, NAND_ECC_READ);
|
||||
|
||||
ret = nand_read_data_op(chip, p, eccsize, false, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
|
||||
if (stat == -EBADMSG &&
|
||||
(chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
|
||||
/* check for empty pages with bitflips */
|
||||
stat = nand_check_erased_ecc_chunk(p, eccsize,
|
||||
&ecc_code[i],
|
||||
eccbytes, NULL, 0,
|
||||
chip->ecc.strength);
|
||||
}
|
||||
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nand_read_page_hwecc_oob_first);
|
||||
|
||||
/**
|
||||
* nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
|
||||
* @chip: nand chip info structure
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/omap-dma.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
@ -148,7 +148,6 @@ struct omap_nand_info {
|
||||
int gpmc_cs;
|
||||
bool dev_ready;
|
||||
enum nand_io xfer_type;
|
||||
int devsize;
|
||||
enum omap_ecc ecc_opt;
|
||||
struct device_node *elm_of_node;
|
||||
|
||||
@ -164,6 +163,7 @@ struct omap_nand_info {
|
||||
u_char *buf;
|
||||
int buf_len;
|
||||
/* Interface to GPMC */
|
||||
void __iomem *fifo;
|
||||
struct gpmc_nand_regs reg;
|
||||
struct gpmc_nand_ops *ops;
|
||||
bool flash_bbt;
|
||||
@ -175,6 +175,11 @@ struct omap_nand_info {
|
||||
unsigned int nsteps_per_eccpg;
|
||||
unsigned int eccpg_size;
|
||||
unsigned int eccpg_bytes;
|
||||
void (*data_in)(struct nand_chip *chip, void *buf,
|
||||
unsigned int len, bool force_8bit);
|
||||
void (*data_out)(struct nand_chip *chip,
|
||||
const void *buf, unsigned int len,
|
||||
bool force_8bit);
|
||||
};
|
||||
|
||||
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
|
||||
@ -182,6 +187,13 @@ static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
|
||||
return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand);
|
||||
}
|
||||
|
||||
static void omap_nand_data_in(struct nand_chip *chip, void *buf,
|
||||
unsigned int len, bool force_8bit);
|
||||
|
||||
static void omap_nand_data_out(struct nand_chip *chip,
|
||||
const void *buf, unsigned int len,
|
||||
bool force_8bit);
|
||||
|
||||
/**
|
||||
* omap_prefetch_enable - configures and starts prefetch transfer
|
||||
* @cs: cs (chip select) number
|
||||
@ -241,169 +253,70 @@ static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_hwcontrol - hardware specific access to control-lines
|
||||
* @chip: NAND chip object
|
||||
* @cmd: command to device
|
||||
* @ctrl:
|
||||
* NAND_NCE: bit 0 -> don't care
|
||||
* NAND_CLE: bit 1 -> Command Latch
|
||||
* NAND_ALE: bit 2 -> Address Latch
|
||||
*
|
||||
* NOTE: boards may use different bits for these!!
|
||||
* omap_nand_data_in_pref - NAND data in using prefetch engine
|
||||
*/
|
||||
static void omap_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl)
|
||||
static void omap_nand_data_in_pref(struct nand_chip *chip, void *buf,
|
||||
unsigned int len, bool force_8bit)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
|
||||
if (cmd != NAND_CMD_NONE) {
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd, info->reg.gpmc_nand_command);
|
||||
|
||||
else if (ctrl & NAND_ALE)
|
||||
writeb(cmd, info->reg.gpmc_nand_address);
|
||||
|
||||
else /* NAND_NCE */
|
||||
writeb(cmd, info->reg.gpmc_nand_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_read_buf8 - read data from NAND controller into buffer
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
*/
|
||||
static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
|
||||
ioread8_rep(nand->legacy.IO_ADDR_R, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_write_buf8 - write buffer to NAND controller
|
||||
* @mtd: MTD device structure
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
*/
|
||||
static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
u_char *p = (u_char *)buf;
|
||||
bool status;
|
||||
|
||||
while (len--) {
|
||||
iowrite8(*p++, info->nand.legacy.IO_ADDR_W);
|
||||
/* wait until buffer is available for write */
|
||||
do {
|
||||
status = info->ops->nand_writebuffer_empty();
|
||||
} while (!status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_read_buf16 - read data from NAND controller into buffer
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
*/
|
||||
static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
|
||||
ioread16_rep(nand->legacy.IO_ADDR_R, buf, len / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_write_buf16 - write buffer to NAND controller
|
||||
* @mtd: MTD device structure
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
*/
|
||||
static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
u16 *p = (u16 *) buf;
|
||||
bool status;
|
||||
/* FIXME try bursts of writesw() or DMA ... */
|
||||
len >>= 1;
|
||||
|
||||
while (len--) {
|
||||
iowrite16(*p++, info->nand.legacy.IO_ADDR_W);
|
||||
/* wait until buffer is available for write */
|
||||
do {
|
||||
status = info->ops->nand_writebuffer_empty();
|
||||
} while (!status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_read_buf_pref - read data from NAND controller into buffer
|
||||
* @chip: NAND chip object
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
*/
|
||||
static void omap_read_buf_pref(struct nand_chip *chip, u_char *buf, int len)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
uint32_t r_count = 0;
|
||||
int ret = 0;
|
||||
u32 *p = (u32 *)buf;
|
||||
unsigned int pref_len;
|
||||
|
||||
/* take care of subpage reads */
|
||||
if (len % 4) {
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
omap_read_buf16(mtd, buf, len % 4);
|
||||
else
|
||||
omap_read_buf8(mtd, buf, len % 4);
|
||||
p = (u32 *) (buf + len % 4);
|
||||
len -= len % 4;
|
||||
if (force_8bit) {
|
||||
omap_nand_data_in(chip, buf, len, force_8bit);
|
||||
return;
|
||||
}
|
||||
|
||||
/* read 32-bit words using prefetch and remaining bytes normally */
|
||||
|
||||
/* configure and start prefetch transfer */
|
||||
pref_len = len - (len & 3);
|
||||
ret = omap_prefetch_enable(info->gpmc_cs,
|
||||
PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info);
|
||||
PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info);
|
||||
if (ret) {
|
||||
/* PFPW engine is busy, use cpu copy method */
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
omap_read_buf16(mtd, (u_char *)p, len);
|
||||
else
|
||||
omap_read_buf8(mtd, (u_char *)p, len);
|
||||
/* prefetch engine is busy, use CPU copy method */
|
||||
omap_nand_data_in(chip, buf, len, false);
|
||||
} else {
|
||||
do {
|
||||
r_count = readl(info->reg.gpmc_prefetch_status);
|
||||
r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
|
||||
r_count = r_count >> 2;
|
||||
ioread32_rep(info->nand.legacy.IO_ADDR_R, p, r_count);
|
||||
ioread32_rep(info->fifo, p, r_count);
|
||||
p += r_count;
|
||||
len -= r_count << 2;
|
||||
} while (len);
|
||||
/* disable and stop the PFPW engine */
|
||||
pref_len -= r_count << 2;
|
||||
} while (pref_len);
|
||||
/* disable and stop the Prefetch engine */
|
||||
omap_prefetch_reset(info->gpmc_cs, info);
|
||||
/* fetch any remaining bytes */
|
||||
if (len & 3)
|
||||
omap_nand_data_in(chip, p, len & 3, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_write_buf_pref - write buffer to NAND controller
|
||||
* @chip: NAND chip object
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
* omap_nand_data_out_pref - NAND data out using Write Posting engine
|
||||
*/
|
||||
static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf,
|
||||
int len)
|
||||
static void omap_nand_data_out_pref(struct nand_chip *chip,
|
||||
const void *buf, unsigned int len,
|
||||
bool force_8bit)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
uint32_t w_count = 0;
|
||||
int i = 0, ret = 0;
|
||||
u16 *p = (u16 *)buf;
|
||||
unsigned long tim, limit;
|
||||
u32 val;
|
||||
|
||||
if (force_8bit) {
|
||||
omap_nand_data_out(chip, buf, len, force_8bit);
|
||||
return;
|
||||
}
|
||||
|
||||
/* take care of subpage writes */
|
||||
if (len % 2 != 0) {
|
||||
writeb(*buf, info->nand.legacy.IO_ADDR_W);
|
||||
writeb(*(u8 *)buf, info->fifo);
|
||||
p = (u16 *)(buf + 1);
|
||||
len--;
|
||||
}
|
||||
@ -412,18 +325,15 @@ static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf,
|
||||
ret = omap_prefetch_enable(info->gpmc_cs,
|
||||
PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info);
|
||||
if (ret) {
|
||||
/* PFPW engine is busy, use cpu copy method */
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
omap_write_buf16(mtd, (u_char *)p, len);
|
||||
else
|
||||
omap_write_buf8(mtd, (u_char *)p, len);
|
||||
/* write posting engine is busy, use CPU copy method */
|
||||
omap_nand_data_out(chip, buf, len, false);
|
||||
} else {
|
||||
while (len) {
|
||||
w_count = readl(info->reg.gpmc_prefetch_status);
|
||||
w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
|
||||
w_count = w_count >> 1;
|
||||
for (i = 0; (i < w_count) && len; i++, len -= 2)
|
||||
iowrite16(*p++, info->nand.legacy.IO_ADDR_W);
|
||||
iowrite16(*p++, info->fifo);
|
||||
}
|
||||
/* wait for data to flushed-out before reset the prefetch */
|
||||
tim = 0;
|
||||
@ -451,15 +361,16 @@ static void omap_nand_dma_callback(void *data)
|
||||
|
||||
/*
|
||||
* omap_nand_dma_transfer: configure and start dma transfer
|
||||
* @mtd: MTD device structure
|
||||
* @chip: nand chip structure
|
||||
* @addr: virtual address in RAM of source/destination
|
||||
* @len: number of data bytes to be transferred
|
||||
* @is_write: flag for read/write operation
|
||||
*/
|
||||
static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
||||
unsigned int len, int is_write)
|
||||
static inline int omap_nand_dma_transfer(struct nand_chip *chip,
|
||||
const void *addr, unsigned int len,
|
||||
int is_write)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
|
||||
DMA_FROM_DEVICE;
|
||||
@ -521,49 +432,51 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
||||
out_copy_unmap:
|
||||
dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
|
||||
out_copy:
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
|
||||
: omap_write_buf16(mtd, (u_char *) addr, len);
|
||||
else
|
||||
is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len)
|
||||
: omap_write_buf8(mtd, (u_char *) addr, len);
|
||||
is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false)
|
||||
: omap_nand_data_out(chip, addr, len, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_read_buf_dma_pref - read data from NAND controller into buffer
|
||||
* @chip: NAND chip object
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
* omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch
|
||||
*/
|
||||
static void omap_read_buf_dma_pref(struct nand_chip *chip, u_char *buf,
|
||||
int len)
|
||||
static void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf,
|
||||
unsigned int len, bool force_8bit)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
if (force_8bit) {
|
||||
omap_nand_data_in(chip, buf, len, force_8bit);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len <= mtd->oobsize)
|
||||
omap_read_buf_pref(chip, buf, len);
|
||||
omap_nand_data_in_pref(chip, buf, len, false);
|
||||
else
|
||||
/* start transfer in DMA mode */
|
||||
omap_nand_dma_transfer(mtd, buf, len, 0x0);
|
||||
omap_nand_dma_transfer(chip, buf, len, 0x0);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_write_buf_dma_pref - write buffer to NAND controller
|
||||
* @chip: NAND chip object
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
* omap_nand_data_out_dma_pref - NAND data out using DMA and write posting
|
||||
*/
|
||||
static void omap_write_buf_dma_pref(struct nand_chip *chip, const u_char *buf,
|
||||
int len)
|
||||
static void omap_nand_data_out_dma_pref(struct nand_chip *chip,
|
||||
const void *buf, unsigned int len,
|
||||
bool force_8bit)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
|
||||
if (force_8bit) {
|
||||
omap_nand_data_out(chip, buf, len, force_8bit);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len <= mtd->oobsize)
|
||||
omap_write_buf_pref(chip, buf, len);
|
||||
omap_nand_data_out_pref(chip, buf, len, false);
|
||||
else
|
||||
/* start transfer in DMA mode */
|
||||
omap_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1);
|
||||
omap_nand_dma_transfer(chip, buf, len, 0x1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -587,13 +500,13 @@ static irqreturn_t omap_nand_irq(int this_irq, void *dev)
|
||||
bytes = info->buf_len;
|
||||
else if (!info->buf_len)
|
||||
bytes = 0;
|
||||
iowrite32_rep(info->nand.legacy.IO_ADDR_W, (u32 *)info->buf,
|
||||
iowrite32_rep(info->fifo, (u32 *)info->buf,
|
||||
bytes >> 2);
|
||||
info->buf = info->buf + bytes;
|
||||
info->buf_len -= bytes;
|
||||
|
||||
} else {
|
||||
ioread32_rep(info->nand.legacy.IO_ADDR_R, (u32 *)info->buf,
|
||||
ioread32_rep(info->fifo, (u32 *)info->buf,
|
||||
bytes >> 2);
|
||||
info->buf = info->buf + bytes;
|
||||
|
||||
@ -613,20 +526,17 @@ done:
|
||||
}
|
||||
|
||||
/*
|
||||
* omap_read_buf_irq_pref - read data from NAND controller into buffer
|
||||
* @chip: NAND chip object
|
||||
* @buf: buffer to store date
|
||||
* @len: number of bytes to read
|
||||
* omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ
|
||||
*/
|
||||
static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf,
|
||||
int len)
|
||||
static void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf,
|
||||
unsigned int len, bool force_8bit)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
struct mtd_info *mtd = nand_to_mtd(&info->nand);
|
||||
int ret = 0;
|
||||
|
||||
if (len <= mtd->oobsize) {
|
||||
omap_read_buf_pref(chip, buf, len);
|
||||
if (len <= mtd->oobsize || force_8bit) {
|
||||
omap_nand_data_in(chip, buf, len, force_8bit);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -637,9 +547,11 @@ static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf,
|
||||
/* configure and start prefetch transfer */
|
||||
ret = omap_prefetch_enable(info->gpmc_cs,
|
||||
PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
/* PFPW engine is busy, use cpu copy method */
|
||||
goto out_copy;
|
||||
omap_nand_data_in(chip, buf, len, false);
|
||||
return;
|
||||
}
|
||||
|
||||
info->buf_len = len;
|
||||
|
||||
@ -652,31 +564,23 @@ static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf,
|
||||
/* disable and stop the PFPW engine */
|
||||
omap_prefetch_reset(info->gpmc_cs, info);
|
||||
return;
|
||||
|
||||
out_copy:
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
omap_read_buf16(mtd, buf, len);
|
||||
else
|
||||
omap_read_buf8(mtd, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* omap_write_buf_irq_pref - write buffer to NAND controller
|
||||
* @chip: NAND chip object
|
||||
* @buf: data buffer
|
||||
* @len: number of bytes to write
|
||||
* omap_nand_data_out_irq_pref - NAND out using write posting and IRQ
|
||||
*/
|
||||
static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf,
|
||||
int len)
|
||||
static void omap_nand_data_out_irq_pref(struct nand_chip *chip,
|
||||
const void *buf, unsigned int len,
|
||||
bool force_8bit)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct omap_nand_info *info = mtd_to_omap(mtd);
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
struct mtd_info *mtd = nand_to_mtd(&info->nand);
|
||||
int ret = 0;
|
||||
unsigned long tim, limit;
|
||||
u32 val;
|
||||
|
||||
if (len <= mtd->oobsize) {
|
||||
omap_write_buf_pref(chip, buf, len);
|
||||
if (len <= mtd->oobsize || force_8bit) {
|
||||
omap_nand_data_out(chip, buf, len, force_8bit);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -687,9 +591,11 @@ static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf,
|
||||
/* configure and start prefetch transfer : size=24 */
|
||||
ret = omap_prefetch_enable(info->gpmc_cs,
|
||||
(PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
/* PFPW engine is busy, use cpu copy method */
|
||||
goto out_copy;
|
||||
omap_nand_data_out(chip, buf, len, false);
|
||||
return;
|
||||
}
|
||||
|
||||
info->buf_len = len;
|
||||
|
||||
@ -711,12 +617,6 @@ static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf,
|
||||
/* disable and stop the PFPW engine */
|
||||
omap_prefetch_reset(info->gpmc_cs, info);
|
||||
return;
|
||||
|
||||
out_copy:
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
omap_write_buf16(mtd, buf, len);
|
||||
else
|
||||
omap_write_buf8(mtd, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -981,50 +881,6 @@ static void omap_enable_hwecc(struct nand_chip *chip, int mode)
|
||||
writel(val, info->reg.gpmc_ecc_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_wait - wait until the command is done
|
||||
* @this: NAND Chip structure
|
||||
*
|
||||
* Wait function is called during Program and erase operations and
|
||||
* the way it is called from MTD layer, we should wait till the NAND
|
||||
* chip is ready after the programming/erase operation has completed.
|
||||
*
|
||||
* Erase can take up to 400ms and program up to 20ms according to
|
||||
* general NAND and SmartMedia specs
|
||||
*/
|
||||
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;
|
||||
|
||||
timeo += msecs_to_jiffies(400);
|
||||
|
||||
writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command);
|
||||
while (time_before(jiffies, timeo)) {
|
||||
status = readb(info->reg.gpmc_nand_data);
|
||||
if (status & NAND_STATUS_READY)
|
||||
break;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
status = readb(info->reg.gpmc_nand_data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_dev_ready - checks the NAND Ready GPIO line
|
||||
* @chip: NAND chip object
|
||||
*
|
||||
* Returns true if ready and false if busy.
|
||||
*/
|
||||
static int omap_dev_ready(struct nand_chip *chip)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
|
||||
return gpiod_get_value(info->ready_gpiod);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
|
||||
* @chip: NAND chip object
|
||||
@ -1543,8 +1399,8 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
|
||||
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
|
||||
|
||||
/* Write data */
|
||||
chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size);
|
||||
info->data_out(chip, buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size, false);
|
||||
|
||||
/* Update ecc vector from GPMC result registers */
|
||||
ret = omap_calculate_ecc_bch_multi(mtd,
|
||||
@ -1562,7 +1418,7 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
|
||||
}
|
||||
|
||||
/* Write ecc vector to OOB area */
|
||||
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
|
||||
info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
|
||||
|
||||
return nand_prog_page_end_op(chip);
|
||||
}
|
||||
@ -1607,8 +1463,8 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
|
||||
chip->ecc.hwctl(chip, NAND_ECC_WRITE);
|
||||
|
||||
/* Write data */
|
||||
chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size);
|
||||
info->data_out(chip, buf + (eccpg * info->eccpg_size),
|
||||
info->eccpg_size, false);
|
||||
|
||||
for (step = 0; step < info->nsteps_per_eccpg; step++) {
|
||||
unsigned int base_step = eccpg * info->nsteps_per_eccpg;
|
||||
@ -1641,7 +1497,7 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
|
||||
}
|
||||
|
||||
/* write OOB buffer to NAND device */
|
||||
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
|
||||
info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
|
||||
|
||||
return nand_prog_page_end_op(chip);
|
||||
}
|
||||
@ -1984,8 +1840,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
/* Re-populate low-level callbacks based on xfer modes */
|
||||
switch (info->xfer_type) {
|
||||
case NAND_OMAP_PREFETCH_POLLED:
|
||||
chip->legacy.read_buf = omap_read_buf_pref;
|
||||
chip->legacy.write_buf = omap_write_buf_pref;
|
||||
info->data_in = omap_nand_data_in_pref;
|
||||
info->data_out = omap_nand_data_out_pref;
|
||||
break;
|
||||
|
||||
case NAND_OMAP_POLLED:
|
||||
@ -2017,8 +1873,9 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
chip->legacy.read_buf = omap_read_buf_dma_pref;
|
||||
chip->legacy.write_buf = omap_write_buf_dma_pref;
|
||||
|
||||
info->data_in = omap_nand_data_in_dma_pref;
|
||||
info->data_out = omap_nand_data_out_dma_pref;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2049,9 +1906,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->legacy.read_buf = omap_read_buf_irq_pref;
|
||||
chip->legacy.write_buf = omap_write_buf_irq_pref;
|
||||
|
||||
info->data_in = omap_nand_data_in_irq_pref;
|
||||
info->data_out = omap_nand_data_out_irq_pref;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2217,8 +2073,105 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_nand_data_in(struct nand_chip *chip, void *buf,
|
||||
unsigned int len, bool force_8bit)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
u32 alignment = ((uintptr_t)buf | len) & 3;
|
||||
|
||||
if (force_8bit || (alignment & 1))
|
||||
ioread8_rep(info->fifo, buf, len);
|
||||
else if (alignment & 3)
|
||||
ioread16_rep(info->fifo, buf, len >> 1);
|
||||
else
|
||||
ioread32_rep(info->fifo, buf, len >> 2);
|
||||
}
|
||||
|
||||
static void omap_nand_data_out(struct nand_chip *chip,
|
||||
const void *buf, unsigned int len,
|
||||
bool force_8bit)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
u32 alignment = ((uintptr_t)buf | len) & 3;
|
||||
|
||||
if (force_8bit || (alignment & 1))
|
||||
iowrite8_rep(info->fifo, buf, len);
|
||||
else if (alignment & 3)
|
||||
iowrite16_rep(info->fifo, buf, len >> 1);
|
||||
else
|
||||
iowrite32_rep(info->fifo, buf, len >> 2);
|
||||
}
|
||||
|
||||
static int omap_nand_exec_instr(struct nand_chip *chip,
|
||||
const struct nand_op_instr *instr)
|
||||
{
|
||||
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
iowrite8(instr->ctx.cmd.opcode,
|
||||
info->reg.gpmc_nand_command);
|
||||
break;
|
||||
|
||||
case NAND_OP_ADDR_INSTR:
|
||||
for (i = 0; i < instr->ctx.addr.naddrs; i++) {
|
||||
iowrite8(instr->ctx.addr.addrs[i],
|
||||
info->reg.gpmc_nand_address);
|
||||
}
|
||||
break;
|
||||
|
||||
case NAND_OP_DATA_IN_INSTR:
|
||||
info->data_in(chip, instr->ctx.data.buf.in,
|
||||
instr->ctx.data.len,
|
||||
instr->ctx.data.force_8bit);
|
||||
break;
|
||||
|
||||
case NAND_OP_DATA_OUT_INSTR:
|
||||
info->data_out(chip, instr->ctx.data.buf.out,
|
||||
instr->ctx.data.len,
|
||||
instr->ctx.data.force_8bit);
|
||||
break;
|
||||
|
||||
case NAND_OP_WAITRDY_INSTR:
|
||||
ret = info->ready_gpiod ?
|
||||
nand_gpio_waitrdy(chip, info->ready_gpiod, instr->ctx.waitrdy.timeout_ms) :
|
||||
nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
if (instr->delay_ns)
|
||||
ndelay(instr->delay_ns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_nand_exec_op(struct nand_chip *chip,
|
||||
const struct nand_operation *op,
|
||||
bool check_only)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (check_only)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < op->ninstrs; i++) {
|
||||
int ret;
|
||||
|
||||
ret = omap_nand_exec_instr(chip, &op->instrs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nand_controller_ops omap_nand_controller_ops = {
|
||||
.attach_chip = omap_nand_attach_chip,
|
||||
.exec_op = omap_nand_exec_op,
|
||||
};
|
||||
|
||||
/* Shared among all NAND instances to synchronize access to the ECC Engine */
|
||||
@ -2233,6 +2186,7 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
int err;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *vaddr;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
|
||||
GFP_KERNEL);
|
||||
@ -2266,10 +2220,11 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
nand_chip->legacy.IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(nand_chip->legacy.IO_ADDR_R))
|
||||
return PTR_ERR(nand_chip->legacy.IO_ADDR_R);
|
||||
vaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(vaddr))
|
||||
return PTR_ERR(vaddr);
|
||||
|
||||
info->fifo = vaddr;
|
||||
info->phys_base = res->start;
|
||||
|
||||
if (!omap_gpmc_controller_initialized) {
|
||||
@ -2280,9 +2235,6 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
|
||||
nand_chip->controller = &omap_gpmc_controller;
|
||||
|
||||
nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R;
|
||||
nand_chip->legacy.cmd_ctrl = omap_hwcontrol;
|
||||
|
||||
info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(info->ready_gpiod)) {
|
||||
@ -2290,26 +2242,12 @@ static int omap_nand_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(info->ready_gpiod);
|
||||
}
|
||||
|
||||
/*
|
||||
* If RDY/BSY line is connected to OMAP then use the omap ready
|
||||
* function and the generic nand_wait function which reads the status
|
||||
* register after monitoring the RDY/BSY line. Otherwise use a standard
|
||||
* chip delay which is slightly more than tR (AC Timing) of the NAND
|
||||
* device and read status register until you get a failure or success
|
||||
*/
|
||||
if (info->ready_gpiod) {
|
||||
nand_chip->legacy.dev_ready = omap_dev_ready;
|
||||
nand_chip->legacy.chip_delay = 0;
|
||||
} else {
|
||||
nand_chip->legacy.waitfunc = omap_wait;
|
||||
nand_chip->legacy.chip_delay = 50;
|
||||
}
|
||||
|
||||
if (info->flash_bbt)
|
||||
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
|
||||
/* scan NAND device connected to chip controller */
|
||||
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
|
||||
/* default operations */
|
||||
info->data_in = omap_nand_data_in;
|
||||
info->data_out = omap_nand_data_out;
|
||||
|
||||
err = nand_scan(nand_chip, 1);
|
||||
if (err)
|
||||
@ -2352,10 +2290,7 @@ static int omap_nand_remove(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id omap_nand_ids[] = {
|
||||
{ .compatible = "ti,omap2-nand", },
|
||||
{},
|
||||
};
|
||||
/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
|
||||
MODULE_DEVICE_TABLE(of, omap_nand_ids);
|
||||
|
||||
static struct platform_driver omap_nand_driver = {
|
||||
|
@ -384,8 +384,8 @@ static irqreturn_t elm_isr(int this_irq, void *dev_id)
|
||||
static int elm_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct resource *irq;
|
||||
struct elm_info *info;
|
||||
int irq;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
@ -393,20 +393,18 @@ static int elm_probe(struct platform_device *pdev)
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "no irq resource defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
info->elm_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->elm_base))
|
||||
return PTR_ERR(info->elm_base);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
|
||||
pdev->name, info);
|
||||
ret = devm_request_irq(&pdev->dev, irq, elm_isr, 0,
|
||||
pdev->name, info);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failure requesting %pr\n", irq);
|
||||
dev_err(&pdev->dev, "failure requesting %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
1424
drivers/mtd/nand/raw/renesas-nand-controller.c
Normal file
1424
drivers/mtd/nand/raw/renesas-nand-controller.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1539,6 +1539,8 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
|
||||
bool force_8bit, bool check_only);
|
||||
int nand_write_data_op(struct nand_chip *chip, const void *buf,
|
||||
unsigned int len, bool force_8bit);
|
||||
int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page);
|
||||
|
||||
/* Scan and identify a NAND device */
|
||||
int nand_scan_with_ids(struct nand_chip *chip, unsigned int max_chips,
|
||||
|
@ -7,6 +7,7 @@
|
||||
#define _MTD_NAND_OMAP2_H
|
||||
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
#define GPMC_BCH_NUM_REMAINDER 8
|
||||
|
||||
@ -61,4 +62,11 @@ struct gpmc_nand_regs {
|
||||
void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
|
||||
void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct of_device_id omap_nand_ids[] = {
|
||||
{ .compatible = "ti,omap2-nand", },
|
||||
{ .compatible = "ti,am64-nand", },
|
||||
{},
|
||||
};
|
||||
|
||||
#endif /* _MTD_NAND_OMAP2_H */
|
||||
|
Loading…
Reference in New Issue
Block a user