From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 07:24:11 +0000
Subject: [PATCH] add stmac read mac form eeprom
---
kernel/drivers/mtd/nand/raw/davinci_nand.c | 394 ++++++++++++++++++++++++++++++++-----------------------
1 files changed, 227 insertions(+), 167 deletions(-)
diff --git a/kernel/drivers/mtd/nand/raw/davinci_nand.c b/kernel/drivers/mtd/nand/raw/davinci_nand.c
index 66d3d59..bfd3f44 100644
--- a/kernel/drivers/mtd/nand/raw/davinci_nand.c
+++ b/kernel/drivers/mtd/nand/raw/davinci_nand.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* davinci_nand.c - NAND Flash Driver for DaVinci family chips
*
@@ -7,27 +8,13 @@
* Sander Huijsen <Shuijsen@optelecom-nkf.com>
* Troy Kisky <troy.kisky@boundarydevices.com>
* Dirk Behme <Dirk.Behme@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
-#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
@@ -51,6 +38,7 @@
* outputs in a "wire-AND" configuration, with no per-chip signals.
*/
struct davinci_nand_info {
+ struct nand_controller controller;
struct nand_chip chip;
struct platform_device *pdev;
@@ -94,47 +82,6 @@
/*----------------------------------------------------------------------*/
/*
- * Access to hardware control lines: ALE, CLE, secondary chipselect.
- */
-
-static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
- unsigned int ctrl)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
- void __iomem *addr = info->current_cs;
- struct nand_chip *nand = mtd_to_nand(mtd);
-
- /* Did the control lines change? */
- if (ctrl & NAND_CTRL_CHANGE) {
- if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE)
- addr += info->mask_cle;
- else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE)
- addr += info->mask_ale;
-
- nand->IO_ADDR_W = addr;
- }
-
- if (cmd != NAND_CMD_NONE)
- iowrite8(cmd, nand->IO_ADDR_W);
-}
-
-static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
-
- info->current_cs = info->vaddr;
-
- /* maybe kick in a second chipselect */
- if (chip > 0)
- info->current_cs += info->mask_chipsel;
-
- info->chip.IO_ADDR_W = info->current_cs;
- info->chip.IO_ADDR_R = info->chip.IO_ADDR_W;
-}
-
-/*----------------------------------------------------------------------*/
-
-/*
* 1-bit hardware ECC ... context maintained for each core chipselect
*/
@@ -146,16 +93,16 @@
+ 4 * info->core_chipsel);
}
-static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode)
+static void nand_davinci_hwctl_1bit(struct nand_chip *chip, int mode)
{
struct davinci_nand_info *info;
uint32_t nandcfr;
unsigned long flags;
- info = to_davinci_nand(mtd);
+ info = to_davinci_nand(nand_to_mtd(chip));
/* Reset ECC hardware */
- nand_davinci_readecc_1bit(mtd);
+ nand_davinci_readecc_1bit(nand_to_mtd(chip));
spin_lock_irqsave(&davinci_nand_lock, flags);
@@ -170,10 +117,10 @@
/*
* Read hardware ECC value and pack into three bytes
*/
-static int nand_davinci_calculate_1bit(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_code)
+static int nand_davinci_calculate_1bit(struct nand_chip *chip,
+ const u_char *dat, u_char *ecc_code)
{
- unsigned int ecc_val = nand_davinci_readecc_1bit(mtd);
+ unsigned int ecc_val = nand_davinci_readecc_1bit(nand_to_mtd(chip));
unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4);
/* invert so that erased block ecc is correct */
@@ -185,10 +132,9 @@
return 0;
}
-static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
+static int nand_davinci_correct_1bit(struct nand_chip *chip, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
(read_ecc[2] << 16);
uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
@@ -222,7 +168,7 @@
/*
* 4-bit hardware ECC ... context maintained over entire AEMIF
*
- * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME
+ * This is a syndrome engine, but we avoid NAND_ECC_PLACEMENT_INTERLEAVED
* since that forces use of a problematic "infix OOB" layout.
* Among other things, it trashes manufacturer bad block markers.
* Also, and specific to this hardware, it ECC-protects the "prepad"
@@ -231,9 +177,9 @@
* OOB without recomputing ECC.
*/
-static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
+static void nand_davinci_hwctl_4bit(struct nand_chip *chip, int mode)
{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
+ struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
unsigned long flags;
u32 val;
@@ -266,10 +212,10 @@
}
/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */
-static int nand_davinci_calculate_4bit(struct mtd_info *mtd,
- const u_char *dat, u_char *ecc_code)
+static int nand_davinci_calculate_4bit(struct nand_chip *chip,
+ const u_char *dat, u_char *ecc_code)
{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
+ struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
u32 raw_ecc[4], *p;
unsigned i;
@@ -303,11 +249,11 @@
/* Correct up to 4 bits in data we just read, using state left in the
* hardware plus the ecc_code computed when it was first written.
*/
-static int nand_davinci_correct_4bit(struct mtd_info *mtd,
- u_char *data, u_char *ecc_code, u_char *null)
+static int nand_davinci_correct_4bit(struct nand_chip *chip, u_char *data,
+ u_char *ecc_code, u_char *null)
{
int i;
- struct davinci_nand_info *info = to_davinci_nand(mtd);
+ struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
unsigned short ecc10[8];
unsigned short *ecc16;
u32 syndrome[4];
@@ -425,51 +371,71 @@
return corrected;
}
-/*----------------------------------------------------------------------*/
-
-/*
- * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
- * how these chips are normally wired. This translates to both 8 and 16
- * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4).
+/**
+ * nand_davinci_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
*
- * For now we assume that configuration, or any other one which ignores
- * the two LSBs for NAND access ... so we can issue 32-bit reads/writes
- * and have that transparently morphed into multiple NAND operations.
+ * Hardware ECC for large page chips, which requires the ECC data to be
+ * extracted from the OOB before the actual data is read.
*/
-static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static int nand_davinci_read_page_hwecc_oob_first(struct nand_chip *chip,
+ uint8_t *buf,
+ int oob_required, int page)
{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ 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;
- if ((0x03 & ((uintptr_t)buf)) == 0 && (0x03 & len) == 0)
- ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
- else if ((0x01 & ((uintptr_t)buf)) == 0 && (0x01 & len) == 0)
- ioread16_rep(chip->IO_ADDR_R, buf, len >> 1);
- else
- ioread8_rep(chip->IO_ADDR_R, buf, len);
-}
+ /* Read the OOB area first */
+ ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+ if (ret)
+ return ret;
-static void nand_davinci_write_buf(struct mtd_info *mtd,
- const uint8_t *buf, int len)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
+ /* Move read cursor to start of page */
+ ret = nand_change_read_column_op(chip, 0, NULL, 0, false);
+ if (ret)
+ return ret;
- if ((0x03 & ((uintptr_t)buf)) == 0 && (0x03 & len) == 0)
- iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
- else if ((0x01 & ((uintptr_t)buf)) == 0 && (0x01 & len) == 0)
- iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1);
- else
- iowrite8_rep(chip->IO_ADDR_R, buf, len);
-}
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
-/*
- * Check hardware register for wait status. Returns 1 if device is ready,
- * 0 if it is still busy.
- */
-static int nand_davinci_dev_ready(struct mtd_info *mtd)
-{
- struct davinci_nand_info *info = to_davinci_nand(mtd);
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
- return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0);
+ 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;
}
/*----------------------------------------------------------------------*/
@@ -560,11 +526,11 @@
if (!of_property_read_string(pdev->dev.of_node,
"ti,davinci-ecc-mode", &mode)) {
if (!strncmp("none", mode, 4))
- pdata->ecc_mode = NAND_ECC_NONE;
+ pdata->engine_type = NAND_ECC_ENGINE_TYPE_NONE;
if (!strncmp("soft", mode, 4))
- pdata->ecc_mode = NAND_ECC_SOFT;
+ pdata->engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
if (!strncmp("hw", mode, 2))
- pdata->ecc_mode = NAND_ECC_HW;
+ pdata->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
}
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-ecc-bits", &prop))
@@ -615,22 +581,33 @@
if (IS_ERR(pdata))
return PTR_ERR(pdata);
- switch (info->chip.ecc.mode) {
- case NAND_ECC_NONE:
+ /* Use board-specific ECC config */
+ info->chip.ecc.engine_type = pdata->engine_type;
+ info->chip.ecc.placement = pdata->ecc_placement;
+
+ switch (info->chip.ecc.engine_type) {
+ case NAND_ECC_ENGINE_TYPE_NONE:
pdata->ecc_bits = 0;
break;
- case NAND_ECC_SOFT:
+ case NAND_ECC_ENGINE_TYPE_SOFT:
pdata->ecc_bits = 0;
/*
- * This driver expects Hamming based ECC when ecc_mode is set
- * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
- * avoid adding an extra ->ecc_algo field to
- * davinci_nand_pdata.
+ * This driver expects Hamming based ECC when engine_type is set
+ * to NAND_ECC_ENGINE_TYPE_SOFT. Force ecc.algo to
+ * NAND_ECC_ALGO_HAMMING to avoid adding an extra ->ecc_algo
+ * field to davinci_nand_pdata.
*/
- info->chip.ecc.algo = NAND_ECC_HAMMING;
+ info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
break;
- case NAND_ECC_HW:
+ case NAND_ECC_ENGINE_TYPE_ON_HOST:
if (pdata->ecc_bits == 4) {
+ int chunks = mtd->writesize / 512;
+
+ if (!chunks || mtd->oobsize < 16) {
+ dev_dbg(&info->pdev->dev, "too small\n");
+ return -EINVAL;
+ }
+
/*
* No sanity checks: CPUs must support this,
* and the chips may not use NAND_BUSWIDTH_16.
@@ -652,14 +629,35 @@
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
info->chip.ecc.bytes = 10;
info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
- info->chip.ecc.algo = NAND_ECC_BCH;
+ info->chip.ecc.algo = NAND_ECC_ALGO_BCH;
+
+ /*
+ * Update ECC layout if needed ... for 1-bit HW ECC, the
+ * default is OK, but it allocates 6 bytes when only 3
+ * are needed (for each 512 bytes). For 4-bit HW ECC,
+ * the default is not usable: 10 bytes needed, not 6.
+ *
+ * For small page chips, preserve the manufacturer's
+ * badblock marking data ... and make sure a flash BBT
+ * table marker fits in the free bytes.
+ */
+ if (chunks == 1) {
+ mtd_set_ooblayout(mtd,
+ &hwecc4_small_ooblayout_ops);
+ } else if (chunks == 4 || chunks == 8) {
+ mtd_set_ooblayout(mtd,
+ nand_get_large_page_ooblayout());
+ info->chip.ecc.read_page = nand_davinci_read_page_hwecc_oob_first;
+ } else {
+ return -EIO;
+ }
} else {
/* 1bit ecc hamming */
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
info->chip.ecc.correct = nand_davinci_correct_1bit;
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
info->chip.ecc.bytes = 3;
- info->chip.ecc.algo = NAND_ECC_HAMMING;
+ info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING;
}
info->chip.ecc.size = 512;
info->chip.ecc.strength = pdata->ecc_bits;
@@ -668,39 +666,111 @@
return -EINVAL;
}
- /*
- * Update ECC layout if needed ... for 1-bit HW ECC, the default
- * is OK, but it allocates 6 bytes when only 3 are needed (for
- * each 512 bytes). For the 4-bit HW ECC, that default is not
- * usable: 10 bytes are needed, not 6.
- */
- if (pdata->ecc_bits == 4) {
- int chunks = mtd->writesize / 512;
+ return ret;
+}
- if (!chunks || mtd->oobsize < 16) {
- dev_dbg(&info->pdev->dev, "too small\n");
- return -EINVAL;
- }
+static void nand_davinci_data_in(struct davinci_nand_info *info, void *buf,
+ unsigned int len, bool force_8bit)
+{
+ u32 alignment = ((uintptr_t)buf | len) & 3;
- /* For small page chips, preserve the manufacturer's
- * badblock marking data ... and make sure a flash BBT
- * table marker fits in the free bytes.
- */
- if (chunks == 1) {
- mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
- } else if (chunks == 4 || chunks == 8) {
- mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
- info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
- } else {
- return -EIO;
+ if (force_8bit || (alignment & 1))
+ ioread8_rep(info->current_cs, buf, len);
+ else if (alignment & 3)
+ ioread16_rep(info->current_cs, buf, len >> 1);
+ else
+ ioread32_rep(info->current_cs, buf, len >> 2);
+}
+
+static void nand_davinci_data_out(struct davinci_nand_info *info,
+ const void *buf, unsigned int len,
+ bool force_8bit)
+{
+ u32 alignment = ((uintptr_t)buf | len) & 3;
+
+ if (force_8bit || (alignment & 1))
+ iowrite8_rep(info->current_cs, buf, len);
+ else if (alignment & 3)
+ iowrite16_rep(info->current_cs, buf, len >> 1);
+ else
+ iowrite32_rep(info->current_cs, buf, len >> 2);
+}
+
+static int davinci_nand_exec_instr(struct davinci_nand_info *info,
+ const struct nand_op_instr *instr)
+{
+ unsigned int i, timeout_us;
+ u32 status;
+ int ret;
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ iowrite8(instr->ctx.cmd.opcode,
+ info->current_cs + info->mask_cle);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ for (i = 0; i < instr->ctx.addr.naddrs; i++) {
+ iowrite8(instr->ctx.addr.addrs[i],
+ info->current_cs + info->mask_ale);
}
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ nand_davinci_data_in(info, instr->ctx.data.buf.in,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ nand_davinci_data_out(info, instr->ctx.data.buf.out,
+ instr->ctx.data.len,
+ instr->ctx.data.force_8bit);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ timeout_us = instr->ctx.waitrdy.timeout_ms * 1000;
+ ret = readl_relaxed_poll_timeout(info->base + NANDFSR_OFFSET,
+ status, status & BIT(0), 100,
+ timeout_us);
+ if (ret)
+ return ret;
+
+ break;
}
- return ret;
+ if (instr->delay_ns)
+ ndelay(instr->delay_ns);
+
+ return 0;
+}
+
+static int davinci_nand_exec_op(struct nand_chip *chip,
+ const struct nand_operation *op,
+ bool check_only)
+{
+ struct davinci_nand_info *info = to_davinci_nand(nand_to_mtd(chip));
+ unsigned int i;
+
+ if (check_only)
+ return 0;
+
+ info->current_cs = info->vaddr + (op->cs * info->mask_chipsel);
+
+ for (i = 0; i < op->ninstrs; i++) {
+ int ret;
+
+ ret = davinci_nand_exec_instr(info, &op->instrs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static const struct nand_controller_ops davinci_nand_controller_ops = {
.attach_chip = davinci_nand_attach_chip,
+ .exec_op = davinci_nand_exec_op,
};
static int nand_davinci_probe(struct platform_device *pdev)
@@ -764,11 +834,6 @@
mtd->dev.parent = &pdev->dev;
nand_set_flash_node(&info->chip, pdev->dev.of_node);
- info->chip.IO_ADDR_R = vaddr;
- info->chip.IO_ADDR_W = vaddr;
- info->chip.chip_delay = 0;
- info->chip.select_chip = nand_davinci_select_chip;
-
/* options such as NAND_BBT_USE_FLASH */
info->chip.bbt_options = pdata->bbt_options;
/* options such as 16-bit widths */
@@ -785,17 +850,6 @@
info->mask_ale = pdata->mask_ale ? : MASK_ALE;
info->mask_cle = pdata->mask_cle ? : MASK_CLE;
- /* Set address of hardware control function */
- info->chip.cmd_ctrl = nand_davinci_hwcontrol;
- info->chip.dev_ready = nand_davinci_dev_ready;
-
- /* Speed up buffer I/O */
- info->chip.read_buf = nand_davinci_read_buf;
- info->chip.write_buf = nand_davinci_write_buf;
-
- /* Use board-specific ECC config */
- info->chip.ecc.mode = pdata->ecc_mode;
-
spin_lock_irq(&davinci_nand_lock);
/* put CSxNAND into NAND mode */
@@ -806,7 +860,9 @@
spin_unlock_irq(&davinci_nand_lock);
/* Scan to find existence of the device(s) */
- info->chip.dummy_controller.ops = &davinci_nand_controller_ops;
+ nand_controller_init(&info->controller);
+ info->controller.ops = &davinci_nand_controller_ops;
+ info->chip.controller = &info->controller;
ret = nand_scan(&info->chip, pdata->mask_chipsel ? 2 : 1);
if (ret < 0) {
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
@@ -835,13 +891,17 @@
static int nand_davinci_remove(struct platform_device *pdev)
{
struct davinci_nand_info *info = platform_get_drvdata(pdev);
+ struct nand_chip *chip = &info->chip;
+ int ret;
spin_lock_irq(&davinci_nand_lock);
- if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
+ if (info->chip.ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED)
ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock);
- nand_release(&info->chip);
+ ret = mtd_device_unregister(nand_to_mtd(chip));
+ WARN_ON(ret);
+ nand_cleanup(chip);
return 0;
}
--
Gitblit v1.6.2