Changelog: https://cdn.kernel.org/pub/linux/kernel/v6.x/ChangeLog-6.12.78 Removed upstreamed: generic/pending-6.12/620-net-sfp-improve-Huawei-MA5671a-fixup.patch[1] generic/pending-6.12/704-net-phy-register-phy-led_triggers-during-probe-to-av.patch[2] airoha/patches-6.12/014-01-v6.13-net-airoha-fix-PSE-memory-configuration-in-airoha_fe.patch[3] airoha/patches-6.12/014-02-v6.13-net-airoha-read-default-PSE-reserved-pages-value-bef.patch[4] airoha/patches-6.12/031-01-v6.13-net-airoha-Read-completion-queue-data-in-airoha_qdma.patch[5] airoha/patches-6.12/128-v7.1-net-airoha-Remove-airoha_dev_stop-in-airoha_remove.patch[6] Manually rebased: airoha/patches-6.12/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch All patches automatically rebased. 1. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.12.78&id=bbdf6d378e1ddbeadc04c57b182aadefcc3aa917 2. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.12.78&id=241cd64cf2e32b28ead151b1795cd8fef2b6e482 3. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.12.78&id=a04ac7bc97afe313e10ae4c73797c668dee47c5c 4. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.12.78&id=a9ed47c3663219e20406d566f02809de05373a42 5. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.12.78&id=4cba4373abac7ba27fdb33057a29b92efa8fd15d 6. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.12.78&id=652ec118d8dc1b088e685d5562995b6665463771 Signed-off-by: John Audia <therealgraysky@proton.me> Link: https://github.com/openwrt/openwrt/pull/22607 [Fix 048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol] Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
300 lines
8.6 KiB
Diff
300 lines
8.6 KiB
Diff
From 3bfb22cecfe6b6f0d8ee56ef4b533cf68599c5d9 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
|
|
Date: Wed, 21 May 2025 10:03:25 +0200
|
|
Subject: [PATCH] mtd: rawnand: brcmnand: legacy exec_op implementation
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Commit 3c8260ce7663 ("mtd: rawnand: brcmnand: exec_op implementation")
|
|
removed legacy interface functions, breaking < v5.0 controllers support.
|
|
In order to fix older controllers we need to add an alternative exec_op
|
|
implementation which doesn't rely on low level registers.
|
|
|
|
Fixes: 3c8260ce7663 ("mtd: rawnand: brcmnand: exec_op implementation")
|
|
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
|
|
Reviewed-by: David Regan <dregan@broadcom.com>
|
|
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
|
|
Reviewed-by: William Zhang <william.zhang@broadcom.com>
|
|
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
|
---
|
|
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 222 ++++++++++++++++++++++-
|
|
1 file changed, 215 insertions(+), 7 deletions(-)
|
|
|
|
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
|
|
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
|
|
@@ -65,6 +65,7 @@ module_param(wp_on, int, 0444);
|
|
#define CMD_PARAMETER_READ 0x0e
|
|
#define CMD_PARAMETER_CHANGE_COL 0x0f
|
|
#define CMD_LOW_LEVEL_OP 0x10
|
|
+#define CMD_NOT_SUPPORTED 0xff
|
|
|
|
struct brcm_nand_dma_desc {
|
|
u32 next_desc;
|
|
@@ -199,6 +200,30 @@ static const u16 flash_dma_regs_v4[] = {
|
|
[FLASH_DMA_CURRENT_DESC_EXT] = 0x34,
|
|
};
|
|
|
|
+/* Native command conversion for legacy controllers (< v5.0) */
|
|
+static const u8 native_cmd_conv[] = {
|
|
+ [NAND_CMD_READ0] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_READ1] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_RNDOUT] = CMD_PARAMETER_CHANGE_COL,
|
|
+ [NAND_CMD_PAGEPROG] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_READOOB] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_ERASE1] = CMD_BLOCK_ERASE,
|
|
+ [NAND_CMD_STATUS] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_SEQIN] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_RNDIN] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_READID] = CMD_DEVICE_ID_READ,
|
|
+ [NAND_CMD_ERASE2] = CMD_NULL,
|
|
+ [NAND_CMD_PARAM] = CMD_PARAMETER_READ,
|
|
+ [NAND_CMD_GET_FEATURES] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_SET_FEATURES] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_RESET] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_READSTART] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_READCACHESEQ] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_READCACHEEND] = CMD_NOT_SUPPORTED,
|
|
+ [NAND_CMD_RNDOUTSTART] = CMD_NULL,
|
|
+ [NAND_CMD_CACHEDPROG] = CMD_NOT_SUPPORTED,
|
|
+};
|
|
+
|
|
/* Controller feature flags */
|
|
enum {
|
|
BRCMNAND_HAS_1K_SECTORS = BIT(0),
|
|
@@ -237,6 +262,12 @@ struct brcmnand_controller {
|
|
/* List of NAND hosts (one for each chip-select) */
|
|
struct list_head host_list;
|
|
|
|
+ /* Functions to be called from exec_op */
|
|
+ int (*check_instr)(struct nand_chip *chip,
|
|
+ const struct nand_operation *op);
|
|
+ int (*exec_instr)(struct nand_chip *chip,
|
|
+ const struct nand_operation *op);
|
|
+
|
|
/* EDU info, per-transaction */
|
|
const u16 *edu_offsets;
|
|
void __iomem *edu_base;
|
|
@@ -2476,18 +2507,190 @@ static int brcmnand_op_is_reset(const st
|
|
return 0;
|
|
}
|
|
|
|
+static int brcmnand_check_instructions(struct nand_chip *chip,
|
|
+ const struct nand_operation *op)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int brcmnand_exec_instructions(struct nand_chip *chip,
|
|
+ const struct nand_operation *op)
|
|
+{
|
|
+ struct brcmnand_host *host = nand_get_controller_data(chip);
|
|
+ unsigned int i;
|
|
+ int ret = 0;
|
|
+
|
|
+ for (i = 0; i < op->ninstrs; i++) {
|
|
+ ret = brcmnand_exec_instr(host, i, op);
|
|
+ if (ret)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int brcmnand_check_instructions_legacy(struct nand_chip *chip,
|
|
+ const struct nand_operation *op)
|
|
+{
|
|
+ const struct nand_op_instr *instr;
|
|
+ unsigned int i;
|
|
+ u8 cmd;
|
|
+
|
|
+ for (i = 0; i < op->ninstrs; i++) {
|
|
+ instr = &op->instrs[i];
|
|
+
|
|
+ switch (instr->type) {
|
|
+ case NAND_OP_CMD_INSTR:
|
|
+ cmd = native_cmd_conv[instr->ctx.cmd.opcode];
|
|
+ if (cmd == CMD_NOT_SUPPORTED)
|
|
+ return -EOPNOTSUPP;
|
|
+ break;
|
|
+ case NAND_OP_ADDR_INSTR:
|
|
+ case NAND_OP_DATA_IN_INSTR:
|
|
+ case NAND_OP_WAITRDY_INSTR:
|
|
+ break;
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int brcmnand_exec_instructions_legacy(struct nand_chip *chip,
|
|
+ const struct nand_operation *op)
|
|
+{
|
|
+ struct mtd_info *mtd = nand_to_mtd(chip);
|
|
+ struct brcmnand_host *host = nand_get_controller_data(chip);
|
|
+ struct brcmnand_controller *ctrl = host->ctrl;
|
|
+ const struct nand_op_instr *instr;
|
|
+ unsigned int i, j;
|
|
+ u8 cmd = CMD_NULL, last_cmd = CMD_NULL;
|
|
+ int ret = 0;
|
|
+ u64 last_addr;
|
|
+
|
|
+ for (i = 0; i < op->ninstrs; i++) {
|
|
+ instr = &op->instrs[i];
|
|
+
|
|
+ if (instr->type == NAND_OP_CMD_INSTR) {
|
|
+ cmd = native_cmd_conv[instr->ctx.cmd.opcode];
|
|
+ if (cmd == CMD_NOT_SUPPORTED) {
|
|
+ dev_err(ctrl->dev, "unsupported cmd=%d\n",
|
|
+ instr->ctx.cmd.opcode);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ break;
|
|
+ }
|
|
+ } else if (instr->type == NAND_OP_ADDR_INSTR) {
|
|
+ u64 addr = 0;
|
|
+
|
|
+ if (cmd == CMD_NULL)
|
|
+ continue;
|
|
+
|
|
+ if (instr->ctx.addr.naddrs > 8) {
|
|
+ dev_err(ctrl->dev, "unsupported naddrs=%u\n",
|
|
+ instr->ctx.addr.naddrs);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ for (j = 0; j < instr->ctx.addr.naddrs; j++)
|
|
+ addr |= (instr->ctx.addr.addrs[j]) << (j << 3);
|
|
+
|
|
+ if (cmd == CMD_BLOCK_ERASE)
|
|
+ addr <<= chip->page_shift;
|
|
+ else if (cmd == CMD_PARAMETER_CHANGE_COL)
|
|
+ addr &= ~((u64)(FC_BYTES - 1));
|
|
+
|
|
+ brcmnand_set_cmd_addr(mtd, addr);
|
|
+ brcmnand_send_cmd(host, cmd);
|
|
+ last_addr = addr;
|
|
+ last_cmd = cmd;
|
|
+ cmd = CMD_NULL;
|
|
+ brcmnand_waitfunc(chip);
|
|
+
|
|
+ if (last_cmd == CMD_PARAMETER_READ ||
|
|
+ last_cmd == CMD_PARAMETER_CHANGE_COL) {
|
|
+ /* Copy flash cache word-wise */
|
|
+ u32 *flash_cache = (u32 *)ctrl->flash_cache;
|
|
+
|
|
+ brcmnand_soc_data_bus_prepare(ctrl->soc, true);
|
|
+
|
|
+ /*
|
|
+ * Must cache the FLASH_CACHE now, since changes in
|
|
+ * SECTOR_SIZE_1K may invalidate it
|
|
+ */
|
|
+ for (j = 0; j < FC_WORDS; j++)
|
|
+ /*
|
|
+ * Flash cache is big endian for parameter pages, at
|
|
+ * least on STB SoCs
|
|
+ */
|
|
+ flash_cache[j] = be32_to_cpu(brcmnand_read_fc(ctrl, j));
|
|
+
|
|
+ brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
|
|
+ }
|
|
+ } else if (instr->type == NAND_OP_DATA_IN_INSTR) {
|
|
+ u8 *in = instr->ctx.data.buf.in;
|
|
+
|
|
+ if (last_cmd == CMD_DEVICE_ID_READ) {
|
|
+ u32 val;
|
|
+
|
|
+ if (instr->ctx.data.len > 8) {
|
|
+ dev_err(ctrl->dev, "unsupported len=%u\n",
|
|
+ instr->ctx.data.len);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ for (j = 0; j < instr->ctx.data.len; j++) {
|
|
+ if (j == 0)
|
|
+ val = brcmnand_read_reg(ctrl, BRCMNAND_ID);
|
|
+ else if (j == 4)
|
|
+ val = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT);
|
|
+
|
|
+ in[j] = (val >> (24 - ((j % 4) << 3))) & 0xff;
|
|
+ }
|
|
+ } else if (last_cmd == CMD_PARAMETER_READ ||
|
|
+ last_cmd == CMD_PARAMETER_CHANGE_COL) {
|
|
+ u64 addr;
|
|
+ u32 offs;
|
|
+
|
|
+ for (j = 0; j < instr->ctx.data.len; j++) {
|
|
+ addr = last_addr + j;
|
|
+ offs = addr & (FC_BYTES - 1);
|
|
+
|
|
+ if (j > 0 && offs == 0)
|
|
+ nand_change_read_column_op(chip, addr, NULL, 0,
|
|
+ false);
|
|
+
|
|
+ in[j] = ctrl->flash_cache[offs];
|
|
+ }
|
|
+ }
|
|
+ } else if (instr->type == NAND_OP_WAITRDY_INSTR) {
|
|
+ ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
|
|
+ if (ret)
|
|
+ break;
|
|
+ } else {
|
|
+ dev_err(ctrl->dev, "unsupported instruction type: %d\n", instr->type);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int brcmnand_exec_op(struct nand_chip *chip,
|
|
const struct nand_operation *op,
|
|
bool check_only)
|
|
{
|
|
struct brcmnand_host *host = nand_get_controller_data(chip);
|
|
+ struct brcmnand_controller *ctrl = host->ctrl;
|
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
|
u8 *status;
|
|
- unsigned int i;
|
|
int ret = 0;
|
|
|
|
if (check_only)
|
|
- return 0;
|
|
+ return ctrl->check_instr(chip, op);
|
|
|
|
if (brcmnand_op_is_status(op)) {
|
|
status = op->instrs[1].ctx.data.buf.in;
|
|
@@ -2511,11 +2714,7 @@ static int brcmnand_exec_op(struct nand_
|
|
if (op->deassert_wp)
|
|
brcmnand_wp(mtd, 0);
|
|
|
|
- for (i = 0; i < op->ninstrs; i++) {
|
|
- ret = brcmnand_exec_instr(host, i, op);
|
|
- if (ret)
|
|
- break;
|
|
- }
|
|
+ ret = ctrl->exec_instr(chip, op);
|
|
|
|
if (op->deassert_wp)
|
|
brcmnand_wp(mtd, 1);
|
|
@@ -3128,6 +3327,15 @@ int brcmnand_probe(struct platform_devic
|
|
if (ret)
|
|
goto err;
|
|
|
|
+ /* Only v5.0+ controllers have low level ops support */
|
|
+ if (ctrl->nand_version >= 0x0500) {
|
|
+ ctrl->check_instr = brcmnand_check_instructions;
|
|
+ ctrl->exec_instr = brcmnand_exec_instructions;
|
|
+ } else {
|
|
+ ctrl->check_instr = brcmnand_check_instructions_legacy;
|
|
+ ctrl->exec_instr = brcmnand_exec_instructions_legacy;
|
|
+ }
|
|
+
|
|
/*
|
|
* Most chips have this cache at a fixed offset within 'nand' block.
|
|
* Some must specify this region separately.
|