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:
Miquel Raynal 2021-12-31 13:31:23 +01:00
commit 9ce47e43a0
18 changed files with 1911 additions and 414 deletions

View File

@ -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:

View 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>;
};

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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 = {

View File

@ -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;

View File

@ -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

View File

@ -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 = {

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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 */