From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr0-f196.google.com ([209.85.128.196]) by merlin.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1csX9k-0003gl-96 for linux-mtd@lists.infradead.org; Mon, 27 Mar 2017 16:06:10 +0000 Received: by mail-wr0-f196.google.com with SMTP id 20so14576200wrx.0 for ; Mon, 27 Mar 2017 09:05:47 -0700 (PDT) From: Jan Glauber To: Boris Brezillon , Richard Weinberger , David Woodhouse , Brian Norris , Marek Vasut , Cyrille Pitchen Cc: linux-mtd@lists.infradead.org, Jan Glauber Subject: [RFC PATCH 2/2] nand: cavium: Nand flash controller for Cavium ARM64 SOCs Date: Mon, 27 Mar 2017 18:05:24 +0200 Message-Id: <20170327160524.29019-3-jglauber@cavium.com> In-Reply-To: <20170327160524.29019-1-jglauber@cavium.com> References: <20170327160524.29019-1-jglauber@cavium.com> List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Add a driver for the nand flash controller as found on Cavium's ARM64 SOCs. The nand flash controller can support up to 8 chips and is presented as a PCI device. It uses a DMA engine to transfer data between the nand and L2/DRAM and a programmable command queue to issue multiple nand flash commands together. Signed-off-by: Jan Glauber --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/cavium_nand.c | 1160 ++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/cavium_nand.h | 231 ++++++++ 4 files changed, 1398 insertions(+) create mode 100644 drivers/mtd/nand/cavium_nand.c create mode 100644 drivers/mtd/nand/cavium_nand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 6d4d567..354e88f 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -575,4 +575,10 @@ config MTD_NAND_MTK Enables support for NAND controller on MTK SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs. +config MTD_NAND_CAVIUM + tristate "Support for NAND on Cavium Octeon TX SOCs" + depends on PCI && HAS_DMA && (ARM64 || COMPILE_TEST) + help + Enables support for NAND Flash found on Cavium Octeon TX SOCs. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 19a66e4..0ac23ef 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -60,5 +60,6 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o mtk_ecc.o +obj-$(CONFIG_MTD_NAND_CAVIUM) += cavium_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/cavium_nand.c b/drivers/mtd/nand/cavium_nand.c new file mode 100644 index 0000000..b3ab447 --- /dev/null +++ b/drivers/mtd/nand/cavium_nand.c @@ -0,0 +1,1160 @@ +/* + * Cavium cn8xxx NAND flash controller (NDF) driver. + * + * Copyright (C) 2017 Cavium Inc. + * Authors: Jan Glauber + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cavium_nand.h" + +#define MAX_NAND_NAME_LEN 64 +#define NAND_MAX_PAGESIZE 2048 +#define NAND_MAX_OOBSIZE 64 + +/* NAND chip related information */ +struct cvm_nand_chip { + struct list_head node; + struct nand_chip nand; + int cs; /* chip select 0..7 */ + struct ndf_set_tm_par_cmd timings; /* timing parameters */ + int selected_page; + bool oob_access; +}; + +struct cvm_nand_buf { + int dmabuflen; + u8 *dmabuf; + dma_addr_t dmaaddr; + + int data_len; /* Number of bytes in the data buffer */ + int data_index; /* Current read index */ +}; + +/* NAND flash controller (NDF) related information */ +struct cvm_nfc { + struct nand_hw_control controller; + struct device *dev; + void __iomem *base; + struct list_head chips; + int selected_chip; /* Currently selected NAND chip number */ + struct clk *clk; /* System clock */ + + /* + * Status is separate from cvm_nand_buf because + * it can be used in parallel and during init. + */ + u8 *stat; + dma_addr_t stat_addr; + bool use_status; + + struct cvm_nand_buf buf; +}; + +static inline struct cvm_nand_chip *to_cvm_nand(struct nand_chip *nand) +{ + return container_of(nand, struct cvm_nand_chip, nand); +} + +static inline struct cvm_nfc *to_cvm_nfc(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct cvm_nfc, controller); +} + +/* default parameters used for probing chips */ +static int default_onfi_timing = 0; +static int default_width = 1; /* 8 bit */ +static int default_page_size = 2048; +static struct ndf_set_tm_par_cmd default_timing_parms; + +/* + * Get the number of bits required to encode the column bits. This + * does not include bits required for the OOB area. + */ +static int ndf_get_column_bits(struct nand_chip *nand) +{ + int page_size; + + if (!nand) + page_size = default_page_size; + else + page_size = nand->onfi_params.byte_per_page; + return get_bitmask_order(page_size - 1); +} + +irqreturn_t cvm_nfc_isr(int irq, void *dev_id) +{ + struct cvm_nfc *tn = dev_id; + + wake_up(&tn->controller.wq); + return IRQ_HANDLED; +} + +/* + * Read a single byte from the temporary buffer. Used after READID + * to get the NAND information and for STATUS. + */ +static u8 cvm_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct cvm_nfc *tn = to_cvm_nfc(nand->controller); + + if (tn->use_status) + return *tn->stat; + + if (tn->buf.data_index < tn->buf.data_len) + return tn->buf.dmabuf[tn->buf.data_index++]; + else + dev_err(tn->dev, "No data to read\n"); + + return 0xff; +} + +/* + * Read a number of pending bytes from the temporary buffer. Used + * to get page and OOB data. + */ +static void cvm_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct cvm_nfc *tn = to_cvm_nfc(nand->controller); + + if (len > tn->buf.data_len - tn->buf.data_index) { + dev_err(tn->dev, "Not enough data for read of %d bytes\n", len); + return; + } + + memcpy(buf, tn->buf.dmabuf + tn->buf.data_index, len); + tn->buf.data_index += len; +} + +static void cvm_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct cvm_nfc *tn = to_cvm_nfc(nand->controller); + + memcpy(tn->buf.dmabuf + tn->buf.data_len, buf, len); + tn->buf.data_len += len; +} + +/* Overwrite default function to avoid sync abort on chip = -1. */ +static void cvm_nand_select_chip(struct mtd_info *mtd, int chip) +{ + return; +} + +static inline int timing_to_cycle(u32 timing, unsigned long clock) +{ + unsigned int ns; + int margin = 2; + + ns = DIV_ROUND_UP(timing, 1000); + + clock /= 1000000; /* no rounding needed since clock is multiple of 1MHz */ + ns *= clock; + return DIV_ROUND_UP(ns, 1000) + margin; +} + +static void set_timings(struct ndf_set_tm_par_cmd *tp, + const struct nand_sdr_timings *timings, + unsigned long sclk) +{ + /* scaled coprocessor-cycle values */ + u32 sWH, sCLS, sCLH, sALS, sALH, sRP, sREH, sWB, sWC; + + tp->tim_mult = 0; + sWH = timing_to_cycle(timings->tWH_min, sclk); + sCLS = timing_to_cycle(timings->tCLS_min, sclk); + sCLH = timing_to_cycle(timings->tCLH_min, sclk); + sALS = timing_to_cycle(timings->tALS_min, sclk); + sALH = timing_to_cycle(timings->tALH_min, sclk); + sRP = timing_to_cycle(timings->tRP_min, sclk); + sREH = timing_to_cycle(timings->tREH_min, sclk); + sWB = timing_to_cycle(timings->tWB_max, sclk); + sWC = timing_to_cycle(timings->tWC_min, sclk); + + tp->tm_par1 = sWH; + tp->tm_par2 = sCLH; + tp->tm_par3 = sRP + 1; + tp->tm_par4 = sCLS - sWH; + tp->tm_par5 = sWC - sWH + 1; + tp->tm_par6 = sWB; + tp->tm_par7 = 0; + + /* TODO: comment paramter re-use */ + + pr_debug("%s: tim_par: mult: %d p1: %d p2: %d p3: %d\n", + __func__, tp->tim_mult, tp->tm_par1, tp->tm_par2, tp->tm_par3); + pr_debug(" p4: %d p5: %d p6: %d p7: %d\n", + tp->tm_par4, tp->tm_par5, tp->tm_par6, tp->tm_par7); + +} + +static int set_default_timings(struct cvm_nfc *tn, + const struct nand_sdr_timings *timings) +{ + unsigned long sclk = clk_get_rate(tn->clk); + + set_timings(&default_timing_parms, timings, sclk); + return 0; +} + +static int cvm_nfc_chip_set_timings(struct cvm_nand_chip *chip, + const struct nand_sdr_timings *timings) +{ + struct cvm_nfc *tn = to_cvm_nfc(chip->nand.controller); + unsigned long sclk = clk_get_rate(tn->clk); + + set_timings(&chip->timings, timings, sclk); + return 0; +} + +/* How many bytes are free in the NFD_CMD queue? */ +static int ndf_cmd_queue_free(struct cvm_nfc *tn) +{ + u64 ndf_misc; + + ndf_misc = readq(tn->base + NDF_MISC); + return FIELD_GET(NDF_MISC_FR_BYTE, ndf_misc); +} + +/* Submit a command to the NAND command queue. */ +static int ndf_submit(struct cvm_nfc *tn, union ndf_cmd *cmd) +{ + int opcode = cmd->val[0] & 0xf; + + switch (opcode) { + /* All these commands fit in one 64bit word */ + case NDF_OP_NOP: + case NDF_OP_SET_TM_PAR: + case NDF_OP_WAIT: + case NDF_OP_CHIP_EN_DIS: + case NDF_OP_CLE_CMD: + case NDF_OP_WR_CMD: + case NDF_OP_RD_CMD: + case NDF_OP_RD_EDO_CMD: + case NDF_OP_BUS_ACQ_REL: + if (ndf_cmd_queue_free(tn) < 8) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + break; + case NDF_OP_ALE_CMD: /* ALE commands take either one or two 64bit words */ + if (cmd->u.ale_cmd.adr_byte_num < 5) { + if (ndf_cmd_queue_free(tn) < 8) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + } else { + if (ndf_cmd_queue_free(tn) < 16) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + writeq(cmd->val[1], tn->base + NDF_CMD); + } + break; + case NDF_OP_WAIT_STATUS: /* Wait status commands take two 64bit words */ + if (ndf_cmd_queue_free(tn) < 16) + goto full; + writeq(cmd->val[0], tn->base + NDF_CMD); + writeq(cmd->val[1], tn->base + NDF_CMD); + break; + default: + dev_err(tn->dev, "ndf_submit: unknown command: %u\n", opcode); + return -EINVAL; + } + return 0; +full: + dev_err(tn->dev, "ndf_submit: no space left in command queue\n"); + return -ENOMEM; +} + +/* + * Wait for the ready/busy signal. First wait for busy to be valid, + * then wait for busy to de-assert. + */ +static int ndf_wait_for_busy_done(struct cvm_nfc *tn) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wait.opcode = NDF_OP_WAIT; + cmd.u.wait.r_b = 1; + cmd.u.wait.wlen = 6; + + if (ndf_submit(tn, &cmd)) + return -ENOMEM; + return 0; +} + +static bool ndf_dma_done(struct cvm_nfc *tn) +{ + u64 dma_cfg, ndf_int; + + /* Check DMA done bit */ + ndf_int = readq(tn->base + NDF_INT); + if (!(ndf_int & NDF_INT_DMA_DONE)) + return false; + + /* Enable bit should be clear after a transfer */ + dma_cfg = readq(tn->base + NDF_DMA_CFG); + if (dma_cfg & NDF_DMA_CFG_EN) + return false; + return true; +} + +static int ndf_wait(struct cvm_nfc *tn) +{ + long time_left; + + /* enable all IRQ types */ + writeq(0xff, tn->base + NDF_INT_ENA_W1S); + time_left = wait_event_timeout(tn->controller.wq, + ndf_dma_done(tn), 250); + writeq(0xff, tn->base + NDF_INT_ENA_W1C); + + if (!time_left) { + dev_err(tn->dev, "ndf_wait: timeout error\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int ndf_wait_idle(struct cvm_nfc *tn) +{ + u64 val; + + return readq_poll_timeout(tn->base + NDF_ST_REG, val, + val & NDF_ST_REG_EXE_IDLE, 100, 100000); +} + +/* Issue set timing parameters */ +static int ndf_queue_cmd_timing(struct cvm_nfc *tn, + struct ndf_set_tm_par_cmd *timings) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.set_tm_par.opcode = NDF_OP_SET_TM_PAR; + cmd.u.set_tm_par.tim_mult = timings->tim_mult; + cmd.u.set_tm_par.tm_par1 = timings->tm_par1; + cmd.u.set_tm_par.tm_par2 = timings->tm_par2; + cmd.u.set_tm_par.tm_par3 = timings->tm_par3; + cmd.u.set_tm_par.tm_par4 = timings->tm_par4; + cmd.u.set_tm_par.tm_par5 = timings->tm_par5; + cmd.u.set_tm_par.tm_par6 = timings->tm_par6; + cmd.u.set_tm_par.tm_par7 = timings->tm_par7; + return ndf_submit(tn, &cmd); +} + +/* Issue bus acquire or release */ +static int ndf_queue_cmd_bus(struct cvm_nfc *tn, int direction) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.bus_acq_rel.opcode = NDF_OP_BUS_ACQ_REL; + cmd.u.bus_acq_rel.direction = direction; + return ndf_submit(tn, &cmd); +} + +/* Issue chip select or deselect */ +static int ndf_queue_cmd_chip(struct cvm_nfc *tn, int enable, int chip, + int width) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.chip_en_dis.opcode = NDF_OP_CHIP_EN_DIS; + cmd.u.chip_en_dis.chip = chip; + cmd.u.chip_en_dis.enable = enable; + cmd.u.chip_en_dis.bus_width = width; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_wait(struct cvm_nfc *tn, int parm) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wait.opcode = NDF_OP_WAIT; + cmd.u.wait.wlen = parm; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_cle(struct cvm_nfc *tn, int command) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.cle_cmd.opcode = NDF_OP_CLE_CMD; + cmd.u.cle_cmd.cmd_data = command; + cmd.u.cle_cmd.clen1 = 4; + cmd.u.cle_cmd.clen2 = 1; + cmd.u.cle_cmd.clen3 = 2; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_ale(struct cvm_nfc *tn, int addr_bytes, + struct nand_chip *nand, u64 addr, int page_size) +{ + struct cvm_nand_chip *cvm_nand = (nand) ? to_cvm_nand(nand) : NULL; + int column = addr & (page_size - 1); + u64 row = addr >> ndf_get_column_bits(nand); + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.ale_cmd.opcode = NDF_OP_ALE_CMD; + cmd.u.ale_cmd.adr_byte_num = addr_bytes; + + /* set column bit for OOB area, assume OOB follows page */ + if (cvm_nand && cvm_nand->oob_access) + column |= page_size; + + if (addr_bytes == 1) { + cmd.u.ale_cmd.adr_byt1 = addr & 0xff; + } else if (addr_bytes == 2) { + cmd.u.ale_cmd.adr_byt1 = addr & 0xff; + cmd.u.ale_cmd.adr_byt2 = (addr >> 8) & 0xff; + } else if (addr_bytes == 4) { + cmd.u.ale_cmd.adr_byt1 = column & 0xff; + cmd.u.ale_cmd.adr_byt2 = (column >> 8) & 0xff; + cmd.u.ale_cmd.adr_byt3 = row & 0xff; + cmd.u.ale_cmd.adr_byt4 = (row >> 8) & 0xff; + } else if (addr_bytes > 4) { + cmd.u.ale_cmd.adr_byt1 = column & 0xff; + cmd.u.ale_cmd.adr_byt2 = (column >> 8) & 0xff; + cmd.u.ale_cmd.adr_byt3 = row & 0xff; + cmd.u.ale_cmd.adr_byt4 = (row >> 8) & 0xff; + /* row bits above 16 */ + cmd.u.ale_cmd.adr_byt5 = (row >> 16) & 0xff; + cmd.u.ale_cmd.adr_byt6 = (row >> 24) & 0xff; + cmd.u.ale_cmd.adr_byt7 = (row >> 32) & 0xff; + cmd.u.ale_cmd.adr_byt8 = (row >> 40) & 0xff; + } + + cmd.u.ale_cmd.alen1 = 3; + cmd.u.ale_cmd.alen2 = 1; + cmd.u.ale_cmd.alen3 = 5; + cmd.u.ale_cmd.alen4 = 2; + return ndf_submit(tn, &cmd); +} + +static int ndf_queue_cmd_write(struct cvm_nfc *tn, int len) +{ + union ndf_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wr_cmd.opcode = NDF_OP_WR_CMD; + cmd.u.wr_cmd.data = len; + cmd.u.wr_cmd.wlen1 = 3; + cmd.u.wr_cmd.wlen2 = 1; + return ndf_submit(tn, &cmd); +} + +static int ndf_build_pre_cmd(struct cvm_nfc *tn, int cmd1, + int addr_bytes, u64 addr, int cmd2) +{ + struct nand_chip *nand = tn->controller.active; + struct cvm_nand_chip *cvm_nand; + struct ndf_set_tm_par_cmd *timings; + int width, page_size, rc; + + /* Also called before chip probing is finished */ + if (!nand) { + timings = &default_timing_parms; + page_size = default_page_size; + width = default_width; + } else { + cvm_nand = to_cvm_nand(nand); + timings = &cvm_nand->timings; + page_size = nand->onfi_params.byte_per_page; + if (nand->onfi_params.features & ONFI_FEATURE_16_BIT_BUS) + width = 2; + else + width = 1; + } + + rc = ndf_queue_cmd_timing(tn, timings); + if (rc) + return rc; + + rc = ndf_queue_cmd_bus(tn, NDF_BUS_ACQUIRE); + if (rc) + return rc; + + rc = ndf_queue_cmd_chip(tn, 1, tn->selected_chip, width); + if (rc) + return rc; + + rc = ndf_queue_cmd_wait(tn, 1); + if (rc) + return rc; + + rc = ndf_queue_cmd_cle(tn, cmd1); + if (rc) + return rc; + + if (addr_bytes) { + rc = ndf_queue_cmd_ale(tn, addr_bytes, nand, addr, page_size); + if (rc) + return rc; + } + + /* CLE 2 */ + if (cmd2) { + rc = ndf_queue_cmd_cle(tn, cmd2); + if (rc) + return rc; + } + return 0; +} + +static int ndf_build_post_cmd(struct cvm_nfc *tn) +{ + int rc; + + /* Deselect chip */ + rc = ndf_queue_cmd_chip(tn, 0, 0, 0); + if (rc) + return rc; + + rc = ndf_queue_cmd_wait(tn, 2); + if (rc) + return rc; + + /* Release bus */ + rc = ndf_queue_cmd_bus(tn, 0); + if (rc) + return rc; + + rc = ndf_queue_cmd_wait(tn, 2); + if (rc) + return rc; + + /* Write 1 to clear all interrupt bits before starting DMA */ + writeq(0xff, tn->base + NDF_INT); + + /* + * Last action is ringing the doorbell with number of bus + * acquire-releases cycles (currently 1). + */ + writeq(1, tn->base + NDF_DRBELL); + return 0; +} + +/* Setup the NAND DMA engine for a transfer. */ +static void ndf_setup_dma(struct cvm_nfc *tn, int is_write, + dma_addr_t bus_addr, int len) +{ + u64 dma_cfg; + + dma_cfg = FIELD_PREP(NDF_DMA_CFG_RW, is_write) | + FIELD_PREP(NDF_DMA_CFG_SIZE, (len >> 3) - 1); + dma_cfg |= NDF_DMA_CFG_EN; + writeq(dma_cfg, tn->base + NDF_DMA_CFG); + writeq(bus_addr, tn->base + NDF_DMA_ADR); +} + +static int cvm_nand_reset(struct cvm_nfc *tn) +{ + int rc; + + rc = ndf_build_pre_cmd(tn, NAND_CMD_RESET, 0, 0, 0); + if (rc) + return rc; + + rc = ndf_wait_for_busy_done(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn); + if (rc) + return rc; + return 0; +} + +static int cvm_nand_set_features(struct mtd_info *mtd, + struct nand_chip *chip, int feature_addr, + u8 *subfeature_para) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct cvm_nfc *tn = to_cvm_nfc(nand->controller); + int rc; + + rc = ndf_build_pre_cmd(tn, NAND_CMD_SET_FEATURES, 1, feature_addr, 0); + if (rc) + return rc; + + memcpy(tn->buf.dmabuf, subfeature_para, 4); + memset(tn->buf.dmabuf + 4, 0, 4); + + rc = ndf_queue_cmd_write(tn, 8); + if (rc) + return rc; + + ndf_setup_dma(tn, 0, tn->buf.dmaaddr, 8); + + rc = ndf_wait_for_busy_done(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn); + if (rc) + return rc; + return 0; +} + +static int ndf_read(struct cvm_nfc *tn, int cmd1, int addr_bytes, u64 addr, + int cmd2, int len) +{ + dma_addr_t bus_addr = (cmd1 != NAND_CMD_STATUS) ? + tn->buf.dmaaddr : tn->stat_addr; + struct nand_chip *nand = tn->controller.active; + int timing_mode, bytes, rc; + union ndf_cmd cmd; + u64 start, end; + + if (!nand) + timing_mode = default_onfi_timing; + else + timing_mode = nand->onfi_params.async_timing_mode; + + /* Build the command and address cycles */ + rc = ndf_build_pre_cmd(tn, cmd1, addr_bytes, addr, cmd2); + if (rc) + return rc; + + /* This waits for some time, then waits for busy to be de-asserted. */ + rc = ndf_wait_for_busy_done(tn); + if (rc) + return rc; + + memset(&cmd, 0, sizeof(cmd)); + cmd.u.wait.opcode = NDF_OP_WAIT; + cmd.u.wait.wlen = 3; /* tRR is 15 cycles, this is 16 so its ok */ + rc = ndf_submit(tn, &cmd); + if (rc) + return rc; + rc = ndf_submit(tn, &cmd); + if (rc) + return rc; + + memset(&cmd, 0, sizeof(cmd)); + if (timing_mode == ONFI_TIMING_MODE_4 || + timing_mode == ONFI_TIMING_MODE_5) + cmd.u.rd_cmd.opcode = NDF_OP_RD_EDO_CMD; + else + cmd.u.rd_cmd.opcode = NDF_OP_RD_CMD; + cmd.u.rd_cmd.data = len; + cmd.u.rd_cmd.rlen1 = 7; + cmd.u.rd_cmd.rlen2 = 3; + cmd.u.rd_cmd.rlen3 = 1; + cmd.u.rd_cmd.rlen4 = 7; + rc = ndf_submit(tn, &cmd); + if (rc) + return rc; + + start = (u64) bus_addr; + ndf_setup_dma(tn, 0, bus_addr, len); + + rc = ndf_build_post_cmd(tn); + if (rc) + return rc; + + /* Wait for the DMA to complete */ + rc = ndf_wait(tn); + if (rc) + return rc; + + end = readq(tn->base + NDF_DMA_ADR); + bytes = end - start; + + /* Make sure NDF is really done */ + rc = ndf_wait_idle(tn); + if (rc) { + dev_err(tn->dev, "poll idle failed\n"); + return rc; + } + return bytes; +} + +/* + * Read a page from NAND. If the buffer has room, the out of band + * data will be included. + */ +int ndf_page_read(struct cvm_nfc *tn, u64 addr, int len) +{ + int rc; + + memset(tn->buf.dmabuf, 0xff, len); + rc = ndf_read(tn, NAND_CMD_READ0, 4, addr, NAND_CMD_READSTART, len); + if (rc) + return rc; + + return rc; +} + +/* Erase a NAND block */ +static int ndf_block_erase(struct cvm_nfc *tn, u64 addr) +{ + struct nand_chip *nand = tn->controller.active; + int row, rc; + + row = addr >> ndf_get_column_bits(nand); + rc = ndf_build_pre_cmd(tn, NAND_CMD_ERASE1, 2, row, NAND_CMD_ERASE2); + if (rc) + return rc; + + /* Wait for R_B to signal erase is complete */ + rc = ndf_wait_for_busy_done(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn); + if (rc) + return rc; + + /* Wait until the command queue is idle */ + return ndf_wait_idle(tn); +} + +/* + * Write a page (or less) to NAND. + */ +static int ndf_page_write(struct cvm_nfc *tn, u64 addr) +{ + int len, rc; + + len = tn->buf.data_len - tn->buf.data_index; + WARN_ON_ONCE(len & 0x7); + + ndf_setup_dma(tn, 1, tn->buf.dmaaddr + tn->buf.data_index, len); + rc = ndf_build_pre_cmd(tn, NAND_CMD_SEQIN, 4, addr, 0); + if (rc) + return rc; + + rc = ndf_queue_cmd_write(tn, len); + if (rc) + return rc; + + rc = ndf_queue_cmd_cle(tn, NAND_CMD_PAGEPROG); + if (rc) + return rc; + + /* Wait for R_B to signal program is complete */ + rc = ndf_wait_for_busy_done(tn); + if (rc) + return rc; + + rc = ndf_build_post_cmd(tn); + if (rc) + return rc; + + /* Wait for the DMA to complete */ + rc = ndf_wait(tn); + if (rc) + return rc; + + /* Data transfer is done but NDF is not, it is waiting for R/B# */ + return ndf_wait_idle(tn); +} + +static void cvm_nand_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct cvm_nand_chip *cvm_nand = to_cvm_nand(nand); + struct cvm_nfc *tn = to_cvm_nfc(nand->controller); + int rc; + + tn->selected_chip = cvm_nand->cs; + if (tn->selected_chip < 0 || tn->selected_chip >= NAND_MAX_CHIPS) { + dev_err(tn->dev, "invalid chip select\n"); + return; + } + + tn->use_status = false; + cvm_nand->oob_access = false; + + switch (command) { + case NAND_CMD_READID: + tn->buf.data_index = 0; + memset(tn->buf.dmabuf, 0xff, 8); + rc = ndf_read(tn, command, 1, column, 0, 8); + if (rc < 0) + dev_err(tn->dev, "READID failed with %d\n", rc); + else + tn->buf.data_len = rc; + break; + + case NAND_CMD_READOOB: + cvm_nand->oob_access = true; + tn->buf.data_index = 0; + tn->buf.data_len = ndf_page_read(tn, + (page_addr << nand->page_shift) + 0x800, + mtd->oobsize); + + if (tn->buf.data_len < mtd->oobsize) { + dev_err(tn->dev, "READOOB failed with %d\n", + tn->buf.data_len); + tn->buf.data_len = 0; + } + break; + + case NAND_CMD_READ0: + tn->buf.data_index = 0; + tn->buf.data_len = ndf_page_read(tn, + column + (page_addr << nand->page_shift), + (1 << nand->page_shift) + mtd->oobsize); + if (tn->buf.data_len < (1 << nand->page_shift) + mtd->oobsize) { + dev_err(tn->dev, "READ0 failed with %d\n", + tn->buf.data_len); + tn->buf.data_len = 0; + } + break; + + case NAND_CMD_STATUS: + tn->use_status = true; + memset(tn->stat, 0xff, 8); + rc = ndf_read(tn, command, 0, 0, 0, 8); + if (rc < 0) + dev_err(tn->dev, "STATUS failed with %d\n", rc); + break; + + case NAND_CMD_RESET: + tn->buf.data_index = 0; + tn->buf.data_len = 0; + memset(tn->buf.dmabuf, 0xff, tn->buf.dmabuflen); + rc = cvm_nand_reset(tn); + if (rc < 0) + dev_err(tn->dev, "RESET failed with %d\n", rc); + break; + + case NAND_CMD_PARAM: + tn->buf.data_index = column; + memset(tn->buf.dmabuf, 0xff, tn->buf.dmabuflen); + rc = ndf_read(tn, command, 1, 0, 0, 2048); + if (rc < 0) + dev_err(tn->dev, "PARAM failed with %d\n", rc); + else + tn->buf.data_len = rc; + break; + + case NAND_CMD_RNDOUT: + tn->buf.data_index = column; + break; + + case NAND_CMD_ERASE1: + if (ndf_block_erase(tn, page_addr << nand->page_shift)) + dev_err(tn->dev, "ERASE1 failed\n"); + break; + + case NAND_CMD_ERASE2: + /* We do all erase processing in the first command, so ignore + * this one. + */ + break; + + case NAND_CMD_SEQIN: + if (column == mtd->writesize) + cvm_nand->oob_access = true; + tn->buf.data_index = column; + tn->buf.data_len = column; + cvm_nand->selected_page = page_addr; + break; + + case NAND_CMD_PAGEPROG: + rc = ndf_page_write(tn, + cvm_nand->selected_page << nand->page_shift); + if (rc) + dev_err(tn->dev, "PAGEPROG failed with %d\n", rc); + break; + + default: + WARN_ON_ONCE(1); + dev_err(tn->dev, "unhandled nand cmd: %x\n", command); + } +} + +static int cvm_nfc_chip_init_timings(struct cvm_nand_chip *chip, + struct device_node *np) +{ + const struct nand_sdr_timings *timings; + int ret, mode; + + mode = onfi_get_async_timing_mode(&chip->nand); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + mode = chip->nand.onfi_timing_mode_default; + } else { + u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; + + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + feature[0] = mode; + ret = chip->nand.onfi_set_features(&chip->nand.mtd, &chip->nand, + ONFI_FEATURE_ADDR_TIMING_MODE, + feature); + if (ret) + return ret; + } + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + return cvm_nfc_chip_set_timings(chip, timings); +} + +static int cvm_nfc_chip_init(struct cvm_nfc *tn, struct device *dev, + struct device_node *np) +{ + struct cvm_nand_chip *chip; + struct nand_chip *nand; + struct mtd_info *mtd; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + ret = of_property_read_u32(np, "reg", &chip->cs); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", ret); + return ret; + } + + if (chip->cs >= NAND_MAX_CHIPS) { + dev_err(dev, "invalid reg value: %u (max CS = 7)\n", chip->cs); + return -EINVAL; + } + + nand = &chip->nand; + nand->controller = &tn->controller; + + nand_set_flash_node(nand, np); + + nand->select_chip = cvm_nand_select_chip; + nand->cmdfunc = cvm_nand_cmdfunc; + nand->onfi_set_features = cvm_nand_set_features; + nand->read_byte = cvm_nand_read_byte; + nand->read_buf = cvm_nand_read_buf; + nand->write_buf = cvm_nand_write_buf; + + mtd = nand_to_mtd(nand); + mtd->dev.parent = dev; + + /* TODO: support more then 1 chip */ + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + ret = cvm_nfc_chip_init_timings(chip, np); + if (ret) { + dev_err(dev, "could not configure chip timings: %d\n", ret); + return ret; + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + return ret; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "failed to register mtd device: %d\n", ret); + nand_release(mtd); + return ret; + } + + list_add_tail(&chip->node, &tn->chips); + return 0; +} + +static int cvm_nfc_chips_init(struct cvm_nfc *tn, struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *nand_np; + int nr_chips = of_get_child_count(np); + int ret; + + if (nr_chips > NAND_MAX_CHIPS) { + dev_err(dev, "too many NAND chips: %d\n", nr_chips); + return -EINVAL; + } + + if (!nr_chips) { + dev_err(dev, "no DT NAND chips found\n"); + return -ENODEV; + } + + pr_info("%s: scanning %d chips DTs\n", __func__, nr_chips); + + for_each_child_of_node(np, nand_np) { + ret = cvm_nfc_chip_init(tn, dev, nand_np); + if (ret) { + of_node_put(nand_np); + return ret; + } + } + return 0; +} + +/* Reset NFC and initialize registers. */ +static int cvm_nfc_init(struct cvm_nfc *tn) +{ + const struct nand_sdr_timings *timings; + u64 ndf_misc; + int rc; + + /* Initialize values and reset the fifo */ + ndf_misc = readq(tn->base + NDF_MISC); + + ndf_misc &= ~NDF_MISC_EX_DIS; + ndf_misc |= (NDF_MISC_BT_DIS | NDF_MISC_RST_FF); + writeq(ndf_misc, tn->base + NDF_MISC); + + /* Bring the fifo out of reset */ + ndf_misc &= ~(NDF_MISC_RST_FF); + + /* Maximum of co-processor cycles for glitch filtering */ + ndf_misc |= FIELD_PREP(NDF_MISC_WAIT_CNT, 0x3f); + + writeq(ndf_misc, tn->base + NDF_MISC); + + /* Set timing paramters to onfi mode 0 for probing */ + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + rc = set_default_timings(tn, timings); + if (rc) + return rc; + + return 0; +} + +static int cvm_nfc_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct cvm_nfc *tn; + int ret; + + tn = devm_kzalloc(dev, sizeof(*tn), GFP_KERNEL); + if (!tn) + return -ENOMEM; + + tn->dev = dev; + spin_lock_init(&tn->controller.lock); + init_waitqueue_head(&tn->controller.wq); + INIT_LIST_HEAD(&tn->chips); + + memset(tn->buf.dmabuf, 0xff, tn->buf.dmabuflen); + + pci_set_drvdata(pdev, tn); + ret = pcim_enable_device(pdev); + if (ret) + return ret; + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) + return ret; + tn->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!tn->base) + return -EINVAL; + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); + if (ret < 0) + return ret; + ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), + cvm_nfc_isr, 0, "nand-flash-controller", tn); + if (ret) + return ret; + + tn->clk = devm_clk_get(dev, NULL); + if (IS_ERR(tn->clk)) + return PTR_ERR(tn->clk); + + ret = clk_prepare_enable(tn->clk); + if (ret) + return ret; + + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + dev_err(dev, "64 bit DMA mask not available\n"); + + tn->buf.dmabuflen = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; + tn->buf.dmabuf = dmam_alloc_coherent(dev, tn->buf.dmabuflen, + &tn->buf.dmaaddr, GFP_KERNEL); + if (!tn->buf.dmabuf) { + ret = -ENOMEM; + goto error; + } + + tn->stat = dmam_alloc_coherent(dev, 8, &tn->stat_addr, GFP_KERNEL); + if (!tn->stat) { + ret = -ENOMEM; + goto error; + } + + cvm_nfc_init(tn); + ret = cvm_nfc_chips_init(tn, dev); + if (ret) { + dev_err(dev, "failed to init nand chips\n"); + goto error; + } + dev_info(&pdev->dev, "probed\n"); + return 0; + +error: + clk_disable_unprepare(tn->clk); + return ret; +} + +static void cvm_nfc_remove(struct pci_dev *pdev) +{ + struct cvm_nfc *tn = pci_get_drvdata(pdev); + struct cvm_nand_chip *chip; + + while (!list_empty(&tn->chips)) { + chip = list_first_entry(&tn->chips, struct cvm_nand_chip, + node); + nand_release(&chip->nand.mtd); + list_del(&chip->node); + } + clk_disable_unprepare(tn->clk); +} + +static const struct pci_device_id cvm_nfc_pci_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa04f) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, cvm_nfc_pci_id_table); + +static struct pci_driver cvm_nfc_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = cvm_nfc_pci_id_table, + .probe = cvm_nfc_probe, + .remove = cvm_nfc_remove, +}; + +module_pci_driver(cvm_nfc_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jan Glauber "); +MODULE_DESCRIPTION("Cavium Inc. cvm NAND driver"); diff --git a/drivers/mtd/nand/cavium_nand.h b/drivers/mtd/nand/cavium_nand.h new file mode 100644 index 0000000..7030c57 --- /dev/null +++ b/drivers/mtd/nand/cavium_nand.h @@ -0,0 +1,231 @@ +#ifndef _CAVIUM_NAND_H +#define _CAVIUM_NAND_H + +#include + +/* + * The NDF_CMD queue takes commands between 16 - 128 bit. + * All commands must be 16 bit aligned and are little endian. + * WAIT_STATUS commands must be 64 bit aligned. + * Commands are selected by the 4 bit opcode. + * + * Available Commands: + * + * 16 Bit: + * NOP + * WAIT + * BUS_ACQ, BUS_REL + * CHIP_EN, CHIP_DIS + * + * 32 Bit: + * CLE_CMD + * RD_CMD, RD_EDO_CMD + * WR_CMD + * + * 64 Bit: + * SET_TM_PAR + * + * 96 Bit: + * ALE_CMD + * + * 128 Bit: + * WAIT_STATUS, WAIT_STATUS_ALE + */ + +/* NDF Register offsets */ +#define NDF_CMD 0x0 +#define NDF_MISC 0x8 +#define NDF_ECC_CNT 0x10 +#define NDF_DRBELL 0x30 +#define NDF_ST_REG 0x38 /* status */ +#define NDF_INT 0x40 +#define NDF_INT_W1S 0x48 +#define NDF_DMA_CFG 0x50 +#define NDF_DMA_ADR 0x58 +#define NDF_INT_ENA_W1C 0x60 +#define NDF_INT_ENA_W1S 0x68 + +/* NDF command opcodes */ +#define NDF_OP_NOP 0x0 +#define NDF_OP_SET_TM_PAR 0x1 +#define NDF_OP_WAIT 0x2 +#define NDF_OP_CHIP_EN_DIS 0x3 +#define NDF_OP_CLE_CMD 0x4 +#define NDF_OP_ALE_CMD 0x5 +#define NDF_OP_WR_CMD 0x8 +#define NDF_OP_RD_CMD 0x9 +#define NDF_OP_RD_EDO_CMD 0xa +#define NDF_OP_WAIT_STATUS 0xb /* same opcode for WAIT_STATUS_ALE */ +#define NDF_OP_BUS_ACQ_REL 0xf + +#define NDF_BUS_ACQUIRE 1 +#define NDF_BUS_RELEASE 0 + +struct ndf_nop_cmd { + u16 opcode : 4; + u16 nop : 12; +}; + +struct ndf_wait_cmd { + u16 opcode : 4; + u16 r_b : 1; /* wait for one cycle or PBUS_WAIT deassert */ + u16 : 3; + u16 wlen : 3; /* timing parameter select */ + u16 : 5; +}; + +struct ndf_bus_cmd { + u16 opcode : 4; + u16 direction : 4; /* 1 = acquire, 0 = release */ + u16 : 8; +}; + +struct ndf_chip_cmd { + u16 opcode : 4; + u16 chip : 3; /* select chip, 0 = disable */ + u16 enable : 1; /* 1 = enable, 0 = disable */ + u16 bus_width : 2; /* 10 = 16 bit, 01 = 8 bit */ + u16 : 6; +}; + +struct ndf_cle_cmd { + u32 opcode : 4; + u32 : 4; + u32 cmd_data : 8; /* command sent to the PBUS AD pins */ + u32 clen1 : 3; /* time between PBUS CLE and WE asserts */ + u32 clen2 : 3; /* time WE remains asserted */ + u32 clen3 : 3; /* time between WE deassert and CLE */ + u32 : 7; +}; + +/* RD_EDO_CMD uses the same layout as RD_CMD */ +struct ndf_rd_cmd { + u32 opcode : 4; + u32 data : 16; /* data bytes */ + u32 rlen1 : 3; + u32 rlen2 : 3; + u32 rlen3 : 3; + u32 rlen4 : 3; +}; + +struct ndf_wr_cmd { + u32 opcode : 4; + u32 data : 16; /* data bytes */ + u32 : 4; + u32 wlen1 : 3; + u32 wlen2 : 3; + u32 : 3; +}; + +struct ndf_set_tm_par_cmd { + u64 opcode : 4; + u64 tim_mult : 4; /* multiplier for the seven paramters */ + u64 tm_par1 : 8; /* --> Following are the 7 timing parameters that */ + u64 tm_par2 : 8; /* specify the number of coprocessor cycles. */ + u64 tm_par3 : 8; /* A value of zero means one cycle. */ + u64 tm_par4 : 8; /* All values are scaled by tim_mult */ + u64 tm_par5 : 8; /* using tim_par * (2 ^ tim_mult). */ + u64 tm_par6 : 8; + u64 tm_par7 : 8; +}; + +struct ndf_ale_cmd { + u32 opcode : 4; + u32 : 4; + u32 adr_byte_num: 4; /* number of address bytes to be sent */ + u32 : 4; + u32 alen1 : 3; + u32 alen2 : 3; + u32 alen3 : 3; + u32 alen4 : 3; + u32 : 4; + u8 adr_byt1; + u8 adr_byt2; + u8 adr_byt3; + u8 adr_byt4; + u8 adr_byt5; + u8 adr_byt6; + u8 adr_byt7; + u8 adr_byt8; +}; + +struct ndf_wait_status_cmd { + u32 opcode : 4; + u32 : 4; + u32 data : 8; /* data */ + u32 clen1 : 3; + u32 clen2 : 3; + u32 clen3 : 3; + u32 : 8; + u32 ale_ind : 8; /* set to 5 to select WAIT_STATUS_ALE command */ + u32 adr_byte_num: 4; /* ALE only: number of address bytes to be sent */ + u32 : 4; + u32 alen1 : 3; /* ALE only */ + u32 alen2 : 3; /* ALE only */ + u32 alen3 : 3; /* ALE only */ + u32 alen4 : 3; /* ALE only */ + u32 : 4; + u8 adr_byt[4]; /* ALE only */ + u32 nine : 4; /* set to 9 */ + u32 and_mask : 8; + u32 comp_byte : 8; + u32 rlen1 : 3; + u32 rlen2 : 3; + u32 rlen3 : 3; + u32 rlen4 : 3; +}; + +union ndf_cmd { + u64 val[2]; + union { + struct ndf_nop_cmd nop; + struct ndf_wait_cmd wait; + struct ndf_bus_cmd bus_acq_rel; + struct ndf_chip_cmd chip_en_dis; + struct ndf_cle_cmd cle_cmd; + struct ndf_rd_cmd rd_cmd; + struct ndf_wr_cmd wr_cmd; + struct ndf_set_tm_par_cmd set_tm_par; + struct ndf_ale_cmd ale_cmd; + struct ndf_wait_status_cmd wait_status; + } u; +}; + +#define NDF_MISC_MB_DIS BIT_ULL(27) /* Disable multi-bit error hangs */ +#define NDF_MISC_NBR_HWM GENMASK_ULL(26, 24) /* High watermark for NBR FIFO or load/store operations */ +#define NDF_MISC_WAIT_CNT GENMASK_ULL(23, 18) /* Wait input filter count */ +#define NDF_MISC_FR_BYTE GENMASK_ULL(17, 7) /* Unfilled NFD_CMD queue bytes */ +#define NDF_MISC_RD_DONE BIT_ULL(6) /* Set by HW when it reads the last 8 bytes of NDF_CMD */ +#define NDF_MISC_RD_VAL BIT_ULL(5) /* Set by HW when it reads. SW read of NDF_CMD clears it */ +#define NDF_MISC_RD_CMD BIT_ULL(4) /* Let HW read NDF_CMD queue. Cleared on SW NDF_CMD write */ +#define NDF_MISC_BT_DIS BIT_ULL(2) /* Boot disable */ +#define NDF_MISC_EX_DIS BIT_ULL(1) /* Stop comand execution after completing command queue */ +#define NDF_MISC_RST_FF BIT_ULL(0) /* Reset fifo */ + +#define NDF_INT_DMA_DONE BIT_ULL(7) /* DMA request complete */ +#define NDF_INT_OVFR BIT_ULL(6) /* NDF_CMD write when queue is full */ +#define NDF_INT_ECC_MULT BIT_ULL(5) /* Multi-bit ECC error detected */ +#define NDF_INT_ECC_1BIT BIT_ULL(4) /* Single-bit ECC error detected and fixed */ +#define NDF_INT_SM_BAD BIT_ULL(3) /* State machine is in bad state */ +#define NDF_INT_WDOG BIT_ULL(2) /* Watchdog timer expired during command execution */ +#define NDF_INT_FULL BIT_ULL(1) /* NDF_CMD queue is full */ +#define NDF_INT_EMPTY BIT_ULL(0) /* NDF_CMD queue is empty */ + +#define NDF_DMA_CFG_EN BIT_ULL(63) /* DMA engine enable */ +#define NDF_DMA_CFG_RW BIT_ULL(62) /* Read or write */ +#define NDF_DMA_CFG_CLR BIT_ULL(61) /* Terminates DMA and clears enable bit */ +#define NDF_DMA_CFG_SWAP32 BIT_ULL(59) /* 32-bit swap enable */ +#define NDF_DMA_CFG_SWAP16 BIT_ULL(58) /* 16-bit swap enable */ +#define NDF_DMA_CFG_SWAP8 BIT_ULL(57) /* 8-bit swap enable */ +#define NDF_DMA_CFG_CMD_BE BIT_ULL(56) /* Endian mode */ +#define NDF_DMA_CFG_SIZE GENMASK_ULL(55, 36) /* Number of 64 bit transfers */ + +#define NDF_ST_REG_EXE_IDLE BIT_ULL(15) /* Command execution status idle */ +#define NDF_ST_REG_EXE_SM GENMASK_ULL(14, 11) /* Command execution SM states */ +#define NDF_ST_REG_BT_SM GENMASK_ULL(10, 7) /* DMA and load SM states */ +#define NDF_ST_REG_RD_FF_BAD BIT_ULL(6) /* Queue read-back SM bad state */ +#define NDF_ST_REG_RD_FF GENMASK_ULL(5, 4) /* Queue read-back SM states */ +#define NDF_ST_REG_MAIN_BAD BIT_ULL(3) /* Main SM is in a bad state */ +#define NDF_ST_REG_MAIN_SM GENMASK_ULL(2, 0) /* Main SM states */ + +#endif -- 2.9.0.rc0.21.g7777322