2017-08-17 12:08:10

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 00/16] Add QCOM QPIC NAND support

* v5:

1. Removed the patches already applied to linux-next and rebased the
remaining patches on [3]
2. Addressed the review comments in v4 and Added Archit Reviewed
by tag.

[3] http://git.infradead.org/l2-mtd.git/shortlog/refs/heads/nand/next

* v4:

1. Added Acked-by from Rob for DT documentation patches
2. Removed ipq8074 compatible string from ipq4019 DT example.
2. Used the BIT macro for NAND_CMD_VLD bits and consistent names
as suggested by Boris in v3.

* v3:

1. Removed the patches already applied to linux-next and
rebased the remaining patches on [1]
2. Reordered the patches and put the BAM DMA changes [2]
dependent patches and compatible string patches in last
3. Removed the register offsets array and used the dev_cmd offsets
4. Changed some macro names to small letters for better code readability
5. Changed the compatible string to SoC specific
6. Did minor code changes for adding comment, error handling, structure names
7. Combined raw write (patch#18) and passing flag parameter (patch#22) patch
into one
8. Made separate patch for compatible string and NAND properties
9. Made separate patch for BAM command descriptor and data descriptors handling
10. Changed commit message for some of the patches
11. Addressed review comments given in v2
12. Added Reviewed-by of Archit for some of the patches from v2
13. All the MTD tests are working fine for IPQ8064 AP148, IPQ4019 DK04 and
IPQ8074 HK01 boards for v3 patches

[1] http://git.infradead.org/l2-mtd.git/shortlog/refs/heads/nand/next
[2] http://www.spinics.net/lists/dmaengine/msg13662.html

* v2:

1. Addressed the review comments given in v1
2. Removed the DMA coherent buffer for register read and used
streaming DMA API’s
3. Reorganized the NAND read and write functions
4. Separated patch for driver and documentation changes
5. Changed the compatible string for EBI2

* v1:

http://www.spinics.net/lists/devicetree/msg183706.html


Abhishek Sahu (16):
mtd: nand: qcom: DMA mapping support for register read buffer
mtd: nand: qcom: allocate BAM transaction
mtd: nand: qcom: add BAM DMA descriptor handling
mtd: nand: qcom: support for passing flags in DMA helper functions
mtd: nand: qcom: support for read location registers
mtd: nand: qcom: erased codeword detection configuration
mtd: nand: qcom: enable BAM or ADM mode
mtd: nand: qcom: QPIC data descriptors handling
mtd: nand: qcom: support for different DEV_CMD register offsets
mtd: nand: qcom: add command elements in BAM transaction
mtd: nand: qcom: support for command descriptor formation
dt-bindings: qcom_nandc: fix the ipq806x device tree example
dt-bindings: qcom_nandc: IPQ4019 QPIC NAND documentation
dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation
mtd: nand: qcom: support for IPQ4019 QPIC NAND controller
mtd: nand: qcom: Support for IPQ8074 QPIC NAND controller

.../devicetree/bindings/mtd/qcom_nandc.txt | 63 +-
drivers/mtd/nand/qcom_nandc.c | 746 ++++++++++++++++++---
2 files changed, 722 insertions(+), 87 deletions(-)

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2017-08-17 12:08:25

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 03/16] mtd: nand: qcom: add BAM DMA descriptor handling

1. prepare_bam_async_desc is the function which will call
all the DMA API’s. It will fetch the outstanding scatter gather
list for passed channel and will do the DMA descriptor formation.
The DMA flag is dependent upon the type of channel.

2. For ADM DMA, the descriptor is being formed for every DMA
request so its sgl count will be always 1 while in BAM DMA, the
clubbing of descriptor is being done to increase throughput.

3. ADM DMA uses only one channel while in BAM DMA, data descriptors
will be submitted to tx channel (for write) or rx channel
(for read) and all the registers read/write descriptors in
command channel.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 148 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 136 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 4f8306e..f52a692 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -203,11 +203,27 @@ struct bam_transaction {
u32 rx_sgl_start;
};

+/*
+ * This data type corresponds to the nand dma descriptor
+ * @list - list for desc_info
+ * @dir - DMA transfer direction
+ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
+ * ADM
+ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
+ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
+ * @dma_desc - low level DMA engine descriptor
+ */
struct desc_info {
struct list_head node;

enum dma_data_direction dir;
- struct scatterlist sgl;
+ union {
+ struct scatterlist adm_sgl;
+ struct {
+ struct scatterlist *bam_sgl;
+ int sgl_cnt;
+ };
+ };
struct dma_async_tx_descriptor *dma_desc;
};

@@ -568,9 +584,78 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
}

-static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read,
- int reg_off, const void *vaddr, int size,
- bool flow_control)
+/*
+ * Maps the scatter gather list for DMA transfer and forms the DMA descriptor
+ * for BAM. This descriptor will be added in the NAND DMA descriptor queue
+ * which will be submitted to DMA engine.
+ */
+static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
+ struct dma_chan *chan,
+ unsigned long flags)
+{
+ struct desc_info *desc;
+ struct scatterlist *sgl;
+ unsigned int sgl_cnt;
+ int ret;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+ enum dma_transfer_direction dir_eng;
+ struct dma_async_tx_descriptor *dma_desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ if (chan == nandc->cmd_chan) {
+ sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
+ sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
+ bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ } else if (chan == nandc->tx_chan) {
+ sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
+ sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
+ bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
+ dir_eng = DMA_MEM_TO_DEV;
+ desc->dir = DMA_TO_DEVICE;
+ } else {
+ sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
+ sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
+ bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
+ dir_eng = DMA_DEV_TO_MEM;
+ desc->dir = DMA_FROM_DEVICE;
+ }
+
+ sg_mark_end(sgl + sgl_cnt - 1);
+ ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+ if (ret == 0) {
+ dev_err(nandc->dev, "failure in mapping desc\n");
+ kfree(desc);
+ return -ENOMEM;
+ }
+
+ desc->sgl_cnt = sgl_cnt;
+ desc->bam_sgl = sgl;
+
+ dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
+ flags);
+
+ if (!dma_desc) {
+ dev_err(nandc->dev, "failure in prep desc\n");
+ dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+ kfree(desc);
+ return -EINVAL;
+ }
+
+ desc->dma_desc = dma_desc;
+
+ list_add_tail(&desc->node, &nandc->desc_list);
+
+ return 0;
+}
+
+static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr, int size,
+ bool flow_control)
{
struct desc_info *desc;
struct dma_async_tx_descriptor *dma_desc;
@@ -583,7 +668,7 @@ static int prep_dma_desc(struct qcom_nand_controller *nandc, bool read,
if (!desc)
return -ENOMEM;

- sgl = &desc->sgl;
+ sgl = &desc->adm_sgl;

sg_init_one(sgl, vaddr, size);

@@ -659,7 +744,7 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
nandc->reg_read_pos += num_regs;

- return prep_dma_desc(nandc, true, first, vaddr, size, flow_control);
+ return prep_adm_dma_desc(nandc, true, first, vaddr, size, flow_control);
}

/*
@@ -690,7 +775,8 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,

size = num_regs * sizeof(u32);

- return prep_dma_desc(nandc, false, first, vaddr, size, flow_control);
+ return prep_adm_dma_desc(nandc, false, first, vaddr, size,
+ flow_control);
}

/*
@@ -704,7 +790,7 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
const u8 *vaddr, int size)
{
- return prep_dma_desc(nandc, true, reg_off, vaddr, size, false);
+ return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
}

/*
@@ -718,7 +804,7 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
const u8 *vaddr, int size)
{
- return prep_dma_desc(nandc, false, reg_off, vaddr, size, false);
+ return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
}

/*
@@ -917,12 +1003,43 @@ static int submit_descs(struct qcom_nand_controller *nandc)
{
struct desc_info *desc;
dma_cookie_t cookie = 0;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+ int r;
+
+ if (nandc->props->is_bam) {
+ if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+ if (r)
+ return r;
+ }
+
+ if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
+ if (r)
+ return r;
+ }
+
+ if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
+ r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0);
+ if (r)
+ return r;
+ }
+ }

list_for_each_entry(desc, &nandc->desc_list, node)
cookie = dmaengine_submit(desc->dma_desc);

- if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
- return -ETIMEDOUT;
+ if (nandc->props->is_bam) {
+ dma_async_issue_pending(nandc->tx_chan);
+ dma_async_issue_pending(nandc->rx_chan);
+
+ if (dma_sync_wait(nandc->cmd_chan, cookie) != DMA_COMPLETE)
+ return -ETIMEDOUT;
+ } else {
+ if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
+ return -ETIMEDOUT;
+ }

return 0;
}
@@ -933,7 +1050,14 @@ static void free_descs(struct qcom_nand_controller *nandc)

list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
list_del(&desc->node);
- dma_unmap_sg(nandc->dev, &desc->sgl, 1, desc->dir);
+
+ if (nandc->props->is_bam)
+ dma_unmap_sg(nandc->dev, desc->bam_sgl,
+ desc->sgl_cnt, desc->dir);
+ else
+ dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
+ desc->dir);
+
kfree(desc);
}
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:17

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 01/16] mtd: nand: qcom: DMA mapping support for register read buffer

The EBI2 NAND controller directly remaps register read buffer with
dma_map_sg and DMA address of this buffer will be passed to DMA
API’s. While, on QPIC NAND controller, which uses BAM DMA, we read
the controller registers by preparing a BAM command descriptor. This
command descriptor requires the

- controller register address
- the DMA address in which we want to store the value read
back from the controller register.

This command descriptor will be remapped with dma_map_sg
and its DMA address will be passed to DMA API’s. Therefore,
it's required that we also map our register read buffer for
DMA (using dma_map_single). We use the returned DMA address
for preparing entries in our command descriptor.

This patch adds the DMA mapping support for register read
buffer. This buffer will be DMA mapped during allocation
time. Before starting of any operation, this buffer will
be synced for device operation and after operation
completion, it will be synced again for CPU.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 59b764a..590fc1d 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -234,6 +234,7 @@ struct nandc_regs {
* by upper layers directly
* @buf_size/count/start: markers for chip->read_buf/write_buf functions
* @reg_read_buf: local buffer for reading back registers via DMA
+ * @reg_read_dma: contains dma address for register read buffer
* @reg_read_pos: marker for data read in reg_read_buf
*
* @regs: a contiguous chunk of memory for DMA register
@@ -279,6 +280,7 @@ struct qcom_nand_controller {
int buf_start;

__le32 *reg_read_buf;
+ dma_addr_t reg_read_dma;
int reg_read_pos;

struct nandc_regs *regs;
@@ -371,6 +373,24 @@ static inline void nandc_write(struct qcom_nand_controller *nandc, int offset,
iowrite32(val, nandc->base + offset);
}

+static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
+ bool is_cpu)
+{
+ if (!nandc->props->is_bam)
+ return;
+
+ if (is_cpu)
+ dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+ else
+ dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+}
+
static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
{
switch (offset) {
@@ -854,6 +874,7 @@ static void free_descs(struct qcom_nand_controller *nandc)
static void clear_read_regs(struct qcom_nand_controller *nandc)
{
nandc->reg_read_pos = 0;
+ nandc_read_buffer_sync(nandc, false);
}

static void pre_command(struct qcom_nand_host *host, int command)
@@ -883,6 +904,7 @@ static void parse_erase_write_errors(struct qcom_nand_host *host, int command)
int i;

num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
+ nandc_read_buffer_sync(nandc, true);

for (i = 0; i < num_cw; i++) {
u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
@@ -904,6 +926,7 @@ static void post_command(struct qcom_nand_host *host, int command)

switch (command) {
case NAND_CMD_READID:
+ nandc_read_buffer_sync(nandc, true);
memcpy(nandc->data_buffer, nandc->reg_read_buf,
nandc->buf_count);
break;
@@ -1067,6 +1090,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
int i;

buf = (struct read_stats *)nandc->reg_read_buf;
+ nandc_read_buffer_sync(nandc, true);

for (i = 0; i < ecc->steps; i++, buf++) {
u32 flash, buffer, erased_cw;
@@ -2003,6 +2027,16 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
return -ENOMEM;

if (nandc->props->is_bam) {
+ nandc->reg_read_dma =
+ dma_map_single(nandc->dev, nandc->reg_read_buf,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
+ dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
+ return -EIO;
+ }
+
nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx");
if (!nandc->tx_chan) {
dev_err(nandc->dev, "failed to request tx channel\n");
@@ -2040,6 +2074,12 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
{
if (nandc->props->is_bam) {
+ if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
+ dma_unmap_single(nandc->dev, nandc->reg_read_dma,
+ MAX_REG_RD *
+ sizeof(*nandc->reg_read_buf),
+ DMA_FROM_DEVICE);
+
if (nandc->tx_chan)
dma_release_channel(nandc->tx_chan);

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:35

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 05/16] mtd: nand: qcom: support for read location registers

In EBI2, all codeword data will be read in FLASH_BUF_ACC buffer
and ADM will copy the data from source (FLASH_BUF_ACC) to
destination (memory for data read).

In QPIC, there is no FLASH_BUF_ACC and all the codeword data will
held in QPIC BAM FIFO buffers. It provides multiple READ_LOCATION
registers which will be used for copying the data from FIFO to
memory. The READ_LOCATION register will be used to read a
specific amount of data from a specific offset within the flash
buffer. It supports sequential offset requests. Each request is
composed of the following fields:

a. Offset within the flash buffer from which data should be
read
b. Amount of data to be read
c. Flag bit specifying the last read request from the flash
buffer. Following the last read request the NANDc refers to the
buffer as empty.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4:

1. Changed macro name from nanc_set_readl to nandc_set_read_loc
2. Removed redundant nanc_set_readl in copy_lasy_cw

drivers/mtd/nand/qcom_nandc.c | 63 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index c922617..9d55e8e 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -53,6 +53,8 @@
#define NAND_VERSION 0xf08
#define NAND_READ_LOCATION_0 0xf20
#define NAND_READ_LOCATION_1 0xf24
+#define NAND_READ_LOCATION_2 0xf28
+#define NAND_READ_LOCATION_3 0xf2c

/* dummy register offsets, used by write_reg_dma */
#define NAND_DEV_CMD1_RESTORE 0xdead
@@ -135,6 +137,11 @@
#define ERASED_PAGE (PAGE_ALL_ERASED | PAGE_ERASED)
#define ERASED_CW (CODEWORD_ALL_ERASED | CODEWORD_ERASED)

+/* NAND_READ_LOCATION_n bits */
+#define READ_LOCATION_OFFSET 0
+#define READ_LOCATION_SIZE 16
+#define READ_LOCATION_LAST 31
+
/* Version Mask */
#define NAND_VERSION_MAJOR_MASK 0xf0000000
#define NAND_VERSION_MAJOR_SHIFT 28
@@ -177,6 +184,12 @@
#define ECC_BCH_4BIT BIT(2)
#define ECC_BCH_8BIT BIT(3)

+#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \
+nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
+ ((offset) << READ_LOCATION_OFFSET) | \
+ ((size) << READ_LOCATION_SIZE) | \
+ ((is_last) << READ_LOCATION_LAST))
+
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8

@@ -263,6 +276,11 @@ struct nandc_regs {
__le32 orig_vld;

__le32 ecc_buf_cfg;
+ __le32 read_location0;
+ __le32 read_location1;
+ __le32 read_location2;
+ __le32 read_location3;
+
};

/*
@@ -519,6 +537,14 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
return &regs->orig_vld;
case NAND_EBI2_ECC_BUF_CFG:
return &regs->ecc_buf_cfg;
+ case NAND_READ_LOCATION_0:
+ return &regs->read_location0;
+ case NAND_READ_LOCATION_1:
+ return &regs->read_location1;
+ case NAND_READ_LOCATION_2:
+ return &regs->read_location2;
+ case NAND_READ_LOCATION_3:
+ return &regs->read_location3;
default:
return NULL;
}
@@ -593,6 +619,10 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+
+ if (read)
+ nandc_set_read_loc(nandc, 0, 0, host->use_ecc ?
+ host->cw_data : host->cw_size, 1);
}

/*
@@ -842,6 +872,10 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc)
*/
static void config_nand_cw_read(struct qcom_nand_controller *nandc)
{
+ if (nandc->props->is_bam)
+ write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
+ NAND_BAM_NEXT_SGL);
+
write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);

@@ -930,6 +964,7 @@ static int nandc_param(struct qcom_nand_host *host)

nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
+ nandc_set_read_loc(nandc, 0, 0, 512, 1);

write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
@@ -1405,6 +1440,19 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
oob_size = host->ecc_bytes_hw + host->spare_bytes;
}

+ if (nandc->props->is_bam) {
+ if (data_buf && oob_buf) {
+ nandc_set_read_loc(nandc, 0, 0, data_size, 0);
+ nandc_set_read_loc(nandc, 1, data_size,
+ oob_size, 1);
+ } else if (data_buf) {
+ nandc_set_read_loc(nandc, 0, 0, data_size, 1);
+ } else {
+ nandc_set_read_loc(nandc, 0, data_size,
+ oob_size, 1);
+ }
+ }
+
config_nand_cw_read(nandc);

if (data_buf)
@@ -1509,6 +1557,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
u8 *data_buf, *oob_buf;
struct nand_ecc_ctrl *ecc = &chip->ecc;
int i, ret;
+ int read_loc;

data_buf = buf;
oob_buf = chip->oob_poi;
@@ -1534,6 +1583,20 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
}

+ if (nandc->props->is_bam) {
+ read_loc = 0;
+ nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
+ read_loc += data_size1;
+
+ nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
+ read_loc += oob_size1;
+
+ nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
+ read_loc += data_size2;
+
+ nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
+ }
+
config_nand_cw_read(nandc);

read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:39

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 06/16] mtd: nand: qcom: erased codeword detection configuration

The NAND controller returns ECC failure during read of completely
erased codeword. The NAND controller has hardware functionality
to detect erased codeword in case of BCH ECC algorithm. The
NAND_ERASED_CW_DETECT_CFG register controls the erased
codeword/page detection controller. This register should be reset
before every page read by setting and clearing bit 0 of
NAND_ERASED_CW_DETECT_CFG.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 9d55e8e..81cfce7 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -203,6 +203,11 @@
#define NAND_BAM_NWD BIT(1)
/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
#define NAND_BAM_NEXT_SGL BIT(2)
+/*
+ * Erased codeword status is being used two times in single transfer so this
+ * flag will determine the current value of erased codeword status register
+ */
+#define NAND_ERASED_CW_SET BIT(4)

/*
* This data type corresponds to the BAM transaction which will be used for all
@@ -281,6 +286,8 @@ struct nandc_regs {
__le32 read_location2;
__le32 read_location3;

+ __le32 erased_cw_detect_cfg_clr;
+ __le32 erased_cw_detect_cfg_set;
};

/*
@@ -810,6 +817,13 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_FLASH_CMD)
flow_control = true;

+ if (first == NAND_ERASED_CW_DETECT_CFG) {
+ if (flags & NAND_ERASED_CW_SET)
+ vaddr = &regs->erased_cw_detect_cfg_set;
+ else
+ vaddr = &regs->erased_cw_detect_cfg_clr;
+ }
+
if (first == NAND_EXEC_CMD)
flags |= NAND_BAM_NWD;

@@ -864,6 +878,9 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc)
write_reg_dma(nandc, NAND_ADDR0, 2, 0);
write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
+ write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
+ NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
}

/*
@@ -2264,6 +2281,10 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)

host->clrflashstatus = FS_READY_BSY_N;
host->clrreadstatus = 0xc0;
+ nandc->regs->erased_cw_detect_cfg_clr =
+ cpu_to_le32(CLR_ERASED_PAGE_DET);
+ nandc->regs->erased_cw_detect_cfg_set =
+ cpu_to_le32(SET_ERASED_PAGE_DET);

dev_dbg(nandc->dev,
"cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:49

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 08/16] mtd: nand: qcom: QPIC data descriptors handling

1. Add the data descriptor preparation function which will be used
only by BAM DMA for forming the data SGL’s
2. Add clear BAM transaction and call it before every new request
3. Check DMA mode for ADM or BAM and call the appropriate
descriptor formation function.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 76 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index fd77d59..1ff5daf 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -473,6 +473,27 @@ static void free_bam_transaction(struct qcom_nand_controller *nandc)
return bam_txn;
}

+/* Clears the BAM transaction indexes */
+static void clear_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ if (!nandc->props->is_bam)
+ return;
+
+ bam_txn->cmd_sgl_pos = 0;
+ bam_txn->cmd_sgl_start = 0;
+ bam_txn->tx_sgl_pos = 0;
+ bam_txn->tx_sgl_start = 0;
+ bam_txn->rx_sgl_pos = 0;
+ bam_txn->rx_sgl_start = 0;
+
+ sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
+ QPIC_PER_CW_CMD_SGL);
+ sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
+ QPIC_PER_CW_DATA_SGL);
+}
+
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
{
return container_of(chip, struct qcom_nand_host, chip);
@@ -704,6 +725,41 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
return 0;
}

+/*
+ * Prepares the data descriptor for BAM DMA which will be used for NAND
+ * data reads and writes.
+ */
+static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
+ const void *vaddr,
+ int size, unsigned int flags)
+{
+ int ret;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ if (read) {
+ sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
+ vaddr, size);
+ bam_txn->rx_sgl_pos++;
+ } else {
+ sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
+ vaddr, size);
+ bam_txn->tx_sgl_pos++;
+
+ /*
+ * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
+ * is not set, form the DMA descriptor
+ */
+ if (!(flags & NAND_BAM_NO_EOT)) {
+ ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
int reg_off, const void *vaddr, int size,
bool flow_control)
@@ -854,6 +910,9 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
const u8 *vaddr, int size, unsigned int flags)
{
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+
return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
}

@@ -869,6 +928,9 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
const u8 *vaddr, int size, unsigned int flags)
{
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+
return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
}

@@ -1156,6 +1218,10 @@ static void pre_command(struct qcom_nand_host *host, int command)
host->last_command = command;

clear_read_regs(nandc);
+
+ if (command == NAND_CMD_RESET || command == NAND_CMD_READID ||
+ command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1)
+ clear_bam_transaction(nandc);
}

/*
@@ -1559,6 +1625,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
data_buf = buf;
oob_buf = oob_required ? chip->oob_poi : NULL;

+ clear_bam_transaction(nandc);
ret = read_page_ecc(host, data_buf, oob_buf);
if (ret) {
dev_err(nandc->dev, "failure to read page\n");
@@ -1584,6 +1651,8 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,
oob_buf = chip->oob_poi;

host->use_ecc = false;
+
+ clear_bam_transaction(nandc);
update_rw_regs(host, ecc->steps, true);
config_nand_page_read(nandc);

@@ -1655,6 +1724,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int ret;

clear_read_regs(nandc);
+ clear_bam_transaction(nandc);

host->use_ecc = true;
set_address(host, 0, page);
@@ -1678,6 +1748,7 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
int i, ret;

clear_read_regs(nandc);
+ clear_bam_transaction(nandc);

data_buf = (u8 *)buf;
oob_buf = chip->oob_poi;
@@ -1743,6 +1814,7 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
int i, ret;

clear_read_regs(nandc);
+ clear_bam_transaction(nandc);

data_buf = (u8 *)buf;
oob_buf = chip->oob_poi;
@@ -1819,11 +1891,13 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,

host->use_ecc = true;

+ clear_bam_transaction(nandc);
ret = copy_last_cw(host, page);
if (ret)
return ret;

clear_read_regs(nandc);
+ clear_bam_transaction(nandc);

/* calculate the data and oob size for the last codeword/step */
data_size = ecc->size - ((ecc->steps - 1) << 2);
@@ -1876,6 +1950,7 @@ static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs)
*/
host->use_ecc = false;

+ clear_bam_transaction(nandc);
ret = copy_last_cw(host, page);
if (ret)
goto err;
@@ -1906,6 +1981,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)
int page, ret, status = 0;

clear_read_regs(nandc);
+ clear_bam_transaction(nandc);

/*
* to mark the BBM as bad, we flash the entire last codeword with 0s.
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:51

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 07/16] mtd: nand: qcom: enable BAM or ADM mode

1. DM_EN is only required for EBI2 NAND controller which uses ADM
2. BAM mode will be disabled after power on reset which needs to
be enabled before starting any BAM transfers.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 81cfce7..fd77d59 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -163,6 +163,9 @@
#define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \
ERASE_START_VLD | SEQ_READ_START_VLD)

+/* NAND_CTRL bits */
+#define BAM_MODE_EN BIT(0)
+
/*
* the NAND controller performs reads/writes with ECC in 516 byte chunks.
* the driver calls the chunks 'step' or 'codeword' interchangeably
@@ -1042,7 +1045,8 @@ static int read_id(struct qcom_nand_host *host, int column)
nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID);
nandc_set_reg(nandc, NAND_ADDR0, column);
nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
+ nandc->props->is_bam ? 0 : DM_EN);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);

write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
@@ -2414,12 +2418,19 @@ static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
/* one time setup of a few nand controller registers */
static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
{
+ u32 nand_ctrl;
+
/* kill onenand */
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
nandc_write(nandc, NAND_DEV_CMD_VLD, NAND_DEV_CMD_VLD_VAL);

- /* enable ADM DMA */
- nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ /* enable ADM or BAM DMA */
+ if (nandc->props->is_bam) {
+ nand_ctrl = nandc_read(nandc, NAND_CTRL);
+ nandc_write(nandc, NAND_CTRL, nand_ctrl | BAM_MODE_EN);
+ } else {
+ nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
+ }

/* save the original values of these registers */
nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:59

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 10/16] mtd: nand: qcom: add command elements in BAM transaction

All the QPIC register read/write through BAM DMA requires
command descriptor which contains the array of command elements.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

* BUILD DEPENDENCY:

This PATCH has build dependency over following BAM command descriptor
patch posted in DMA engine mailing list

http://www.spinics.net/lists/dmaengine/msg13665.html

drivers/mtd/nand/qcom_nandc.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 7977a70..b0a4734 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -22,6 +22,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/delay.h>
+#include <linux/dma/qcom_bam_dma.h>

/* NANDc reg offsets */
#define NAND_FLASH_CMD 0x00
@@ -199,6 +200,7 @@
*/
#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))

+#define QPIC_PER_CW_CMD_ELEMENTS 32
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8

@@ -221,8 +223,13 @@
/*
* This data type corresponds to the BAM transaction which will be used for all
* NAND transfers.
+ * @bam_ce - the array of BAM command elements
* @cmd_sgl - sgl for NAND BAM command pipe
* @data_sgl - sgl for NAND BAM consumer/producer pipe
+ * @bam_ce_pos - the index in bam_ce which is available for next sgl
+ * @bam_ce_start - the index in bam_ce which marks the start position ce
+ * for current sgl. It will be used for size calculation
+ * for current sgl
* @cmd_sgl_pos - current index in command sgl.
* @cmd_sgl_start - start index in command sgl.
* @tx_sgl_pos - current index in data sgl for tx.
@@ -231,8 +238,11 @@
* @rx_sgl_start - start index in data sgl for rx.
*/
struct bam_transaction {
+ struct bam_cmd_element *bam_ce;
struct scatterlist *cmd_sgl;
struct scatterlist *data_sgl;
+ u32 bam_ce_pos;
+ u32 bam_ce_start;
u32 cmd_sgl_pos;
u32 cmd_sgl_start;
u32 tx_sgl_pos;
@@ -462,7 +472,8 @@ static void free_bam_transaction(struct qcom_nand_controller *nandc)

bam_txn_size =
sizeof(*bam_txn) + num_cw *
- ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
+ ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
+ (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
(sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));

bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
@@ -472,6 +483,10 @@ static void free_bam_transaction(struct qcom_nand_controller *nandc)
bam_txn = bam_txn_buf;
bam_txn_buf += sizeof(*bam_txn);

+ bam_txn->bam_ce = bam_txn_buf;
+ bam_txn_buf +=
+ sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
+
bam_txn->cmd_sgl = bam_txn_buf;
bam_txn_buf +=
sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
@@ -489,6 +504,8 @@ static void clear_bam_transaction(struct qcom_nand_controller *nandc)
if (!nandc->props->is_bam)
return;

+ bam_txn->bam_ce_pos = 0;
+ bam_txn->bam_ce_start = 0;
bam_txn->cmd_sgl_pos = 0;
bam_txn->cmd_sgl_start = 0;
bam_txn->tx_sgl_pos = 0;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:09:03

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 11/16] mtd: nand: qcom: support for command descriptor formation

1. Add the function for command descriptor preparation which will
be used only by BAM DMA and it will form the DMA descriptors
containing command elements
2. DMA_PREP_CMD flag should be used for forming command DMA
descriptors

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

* BUILD DEPENDENCY:

This PATCH has build dependency over following BAM command descriptor
patch posted in DMA engine mailing list

http://www.spinics.net/lists/dmaengine/msg13665.html

drivers/mtd/nand/qcom_nandc.c | 108 +++++++++++++++++++++++++++++++++++-------
1 file changed, 92 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index b0a4734..52d9fae 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -200,6 +200,14 @@
*/
#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))

+/* Returns the NAND register physical address */
+#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
+
+/* Returns the dma address for reg read buffer */
+#define reg_buf_dma_addr(chip, vaddr) \
+ ((chip)->reg_read_dma + \
+ ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf))
+
#define QPIC_PER_CW_CMD_ELEMENTS 32
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8
@@ -317,7 +325,8 @@ struct nandc_regs {
* controller
* @dev: parent device
* @base: MMIO base
- * @base_dma: physical base address of controller registers
+ * @base_phys: physical base address of controller registers
+ * @base_dma: dma base address of controller registers
* @core_clk: controller clock
* @aon_clk: another controller clock
*
@@ -350,6 +359,7 @@ struct qcom_nand_controller {
struct device *dev;

void __iomem *base;
+ phys_addr_t base_phys;
dma_addr_t base_dma;

struct clk *core_clk;
@@ -751,6 +761,66 @@ static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
}

/*
+ * Prepares the command descriptor for BAM DMA which will be used for NAND
+ * register reads and writes. The command descriptor requires the command
+ * to be formed in command element type so this function uses the command
+ * element from bam transaction ce array and fills the same with required
+ * data. A single SGL can contain multiple command elements so
+ * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
+ * after the current command element.
+ */
+static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr,
+ int size, unsigned int flags)
+{
+ int bam_ce_size;
+ int i, ret;
+ struct bam_cmd_element *bam_ce_buffer;
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
+
+ /* fill the command desc */
+ for (i = 0; i < size; i++) {
+ if (read)
+ bam_prep_ce(&bam_ce_buffer[i],
+ nandc_reg_phys(nandc, reg_off + 4 * i),
+ BAM_READ_COMMAND,
+ reg_buf_dma_addr(nandc,
+ (__le32 *)vaddr + i));
+ else
+ bam_prep_ce_le32(&bam_ce_buffer[i],
+ nandc_reg_phys(nandc, reg_off + 4 * i),
+ BAM_WRITE_COMMAND,
+ *((__le32 *)vaddr + i));
+ }
+
+ bam_txn->bam_ce_pos += size;
+
+ /* use the separate sgl after this command */
+ if (flags & NAND_BAM_NEXT_SGL) {
+ bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
+ bam_ce_size = (bam_txn->bam_ce_pos -
+ bam_txn->bam_ce_start) *
+ sizeof(struct bam_cmd_element);
+ sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
+ bam_ce_buffer, bam_ce_size);
+ bam_txn->cmd_sgl_pos++;
+ bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
+
+ if (flags & NAND_BAM_NWD) {
+ ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
+ DMA_PREP_FENCE |
+ DMA_PREP_CMD);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
* Prepares the data descriptor for BAM DMA which will be used for NAND
* data reads and writes.
*/
@@ -868,19 +938,22 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
{
bool flow_control = false;
void *vaddr;
- int size;

- if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
- flow_control = true;
+ vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
+ nandc->reg_read_pos += num_regs;

if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
first = dev_cmd_reg_addr(nandc, first);

- size = num_regs * sizeof(u32);
- vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
- nandc->reg_read_pos += num_regs;
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
+ num_regs, flags);

- return prep_adm_dma_desc(nandc, true, first, vaddr, size, flow_control);
+ if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
+ flow_control = true;
+
+ return prep_adm_dma_desc(nandc, true, first, vaddr,
+ num_regs * sizeof(u32), flow_control);
}

/*
@@ -897,13 +970,9 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
bool flow_control = false;
struct nandc_regs *regs = nandc->regs;
void *vaddr;
- int size;

vaddr = offset_to_nandc_reg(regs, first);

- if (first == NAND_FLASH_CMD)
- flow_control = true;
-
if (first == NAND_ERASED_CW_DETECT_CFG) {
if (flags & NAND_ERASED_CW_SET)
vaddr = &regs->erased_cw_detect_cfg_set;
@@ -920,10 +989,15 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);

- size = num_regs * sizeof(u32);
+ if (nandc->props->is_bam)
+ return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
+ num_regs, flags);
+
+ if (first == NAND_FLASH_CMD)
+ flow_control = true;

- return prep_adm_dma_desc(nandc, false, first, vaddr, size,
- flow_control);
+ return prep_adm_dma_desc(nandc, false, first, vaddr,
+ num_regs * sizeof(u32), flow_control);
}

/*
@@ -1187,7 +1261,8 @@ static int submit_descs(struct qcom_nand_controller *nandc)
}

if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
- r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0);
+ r = prepare_bam_async_desc(nandc, nandc->cmd_chan,
+ DMA_PREP_CMD);
if (r)
return r;
}
@@ -2722,6 +2797,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
if (IS_ERR(nandc->base))
return PTR_ERR(nandc->base);

+ nandc->base_phys = res->start;
nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start);

nandc->core_clk = devm_clk_get(dev, "core");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:09:11

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 12/16] dt-bindings: qcom_nandc: fix the ipq806x device tree example

1. Correct the compatible string for IPQ806x
2. Change the NAND controller and NAND chip nodes name
for more clarity.

Acked-by: Rob Herring <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
index 26360fe..f475b65 100644
--- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
@@ -42,8 +42,8 @@ partition.txt for more detail.

Example:

-nand@1ac00000 {
- compatible = "qcom,ebi2-nandc";
+nand-controller@1ac00000 {
+ compatible = "qcom,ipq806x-nand";
reg = <0x1ac00000 0x800>;

clocks = <&gcc EBI2_CLK>,
@@ -58,7 +58,7 @@ nand@1ac00000 {
#address-cells = <1>;
#size-cells = <0>;

- nandcs@0 {
+ nand@0 {
reg = <0>;

nand-ecc-strength = <4>;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:09:14

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 13/16] dt-bindings: qcom_nandc: IPQ4019 QPIC NAND documentation

1. Qualcom IPQ4019 SoC uses QPIC NAND controller version 1.4.0
which uses BAM DMA Engine while IPQ806x uses EBI2 NAND
which uses ADM DMA Engine.
2. QPIC NAND will 3 BAM channels: command, data tx and data rx
while EBI2 NAND uses only single ADM channel.
3. CRCI is only required for ADM DMA and its not required for
BAM DMA.

Acked-by: Rob Herring <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

.../devicetree/bindings/mtd/qcom_nandc.txt | 55 +++++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
index f475b65..d93b952 100644
--- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
@@ -1,11 +1,18 @@
* Qualcomm NAND controller

Required properties:
-- compatible: should be "qcom,ipq806x-nand"
+- compatible: must be one of the following:
+ * "qcom,ipq806x-nand" - for EBI2 NAND controller being used in IPQ806x
+ SoC and it uses ADM DMA
+ * "qcom,ipq4019-nand" - for QPIC NAND controller v1.4.0 being used in
+ IPQ4019 SoC and it uses BAM DMA
+
- reg: MMIO address range
- clocks: must contain core clock and always on clock
- clock-names: must contain "core" for the core clock and "aon" for the
always on clock
+
+EBI2 specific properties:
- dmas: DMA specifier, consisting of a phandle to the ADM DMA
controller node and the channel number to be used for
NAND. Refer to dma.txt and qcom_adm.txt for more details
@@ -16,6 +23,12 @@ Required properties:
- qcom,data-crci: must contain the ADM data type CRCI block instance
number specified for the NAND controller on the given
platform
+
+QPIC specific properties:
+- dmas: DMA specifier, consisting of a phandle to the BAM DMA
+ and the channel number to be used for NAND. Refer to
+ dma.txt, qcom_bam_dma.txt for more details
+- dma-names: must contain all 3 channel names : "tx", "rx", "cmd"
- #address-cells: <1> - subnodes give the chip-select number
- #size-cells: <0>

@@ -82,3 +95,43 @@ nand-controller@1ac00000 {
};
};
};
+
+nand-controller@79b0000 {
+ compatible = "qcom,ipq4019-nand";
+ reg = <0x79b0000 0x1000>;
+
+ clocks = <&gcc GCC_QPIC_CLK>,
+ <&gcc GCC_QPIC_AHB_CLK>;
+ clock-names = "core", "aon";
+
+ dmas = <&qpicbam 0>,
+ <&qpicbam 1>,
+ <&qpicbam 2>;
+ dma-names = "tx", "rx", "cmd";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ nand@0 {
+ reg = <0>;
+ nand-ecc-strength = <4>;
+ nand-ecc-step-size = <512>;
+ nand-bus-width = <8>;
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "boot-nand";
+ reg = <0 0x58a0000>;
+ };
+
+ partition@58a0000 {
+ label = "fs-nand";
+ reg = <0x58a0000 0x4000000>;
+ };
+ };
+ };
+};
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:09:20

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 14/16] dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation

Qualcom IPQ8074 SoC uses QPIC NAND controller version 1.5.0
which uses BAM DMA Engine.

Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 2 ++
1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
index d93b952..73d336be 100644
--- a/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
@@ -6,6 +6,8 @@ Required properties:
SoC and it uses ADM DMA
* "qcom,ipq4019-nand" - for QPIC NAND controller v1.4.0 being used in
IPQ4019 SoC and it uses BAM DMA
+ * "qcom,ipq8074-nand" - for QPIC NAND controller v1.5.0 being used in
+ IPQ8074 SoC and it uses BAM DMA

- reg: MMIO address range
- clocks: must contain core clock and always on clock
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:09:29

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 16/16] mtd: nand: qcom: Support for IPQ8074 QPIC NAND controller

Add the compatible string for IPQ8074 QPIC NAND controller
version 1.5.0 which uses BAM DMA and its FLASH_DEV_CMD registers
starting offset is 0x7000.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 84fcdb3..97ce16a 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -2872,6 +2872,12 @@ static int qcom_nandc_remove(struct platform_device *pdev)
.dev_cmd_reg_start = 0x0,
};

+static const struct qcom_nandc_props ipq8074_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .dev_cmd_reg_start = 0x7000,
+};
+
/*
* data will hold a struct pointer containing more differences once we support
* more controller variants
@@ -2885,6 +2891,10 @@ static int qcom_nandc_remove(struct platform_device *pdev)
.compatible = "qcom,ipq4019-nand",
.data = &ipq4019_nandc_props,
},
+ {
+ .compatible = "qcom,ipq8074-nand",
+ .data = &ipq8074_nandc_props,
+ },
{}
};
MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:10:08

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 15/16] mtd: nand: qcom: support for IPQ4019 QPIC NAND controller

Add the compatible string for IPQ4019 QPIC NAND controller
version 1.4.0 which uses BAM DMA.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 52d9fae..84fcdb3 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -2866,6 +2866,12 @@ static int qcom_nandc_remove(struct platform_device *pdev)
.dev_cmd_reg_start = 0x0,
};

+static const struct qcom_nandc_props ipq4019_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .is_bam = true,
+ .dev_cmd_reg_start = 0x0,
+};
+
/*
* data will hold a struct pointer containing more differences once we support
* more controller variants
@@ -2875,6 +2881,10 @@ static int qcom_nandc_remove(struct platform_device *pdev)
.compatible = "qcom,ipq806x-nand",
.data = &ipq806x_nandc_props,
},
+ {
+ .compatible = "qcom,ipq4019-nand",
+ .data = &ipq4019_nandc_props,
+ },
{}
};
MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:11:18

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 09/16] mtd: nand: qcom: support for different DEV_CMD register offsets

The FLASH_DEV_CMD registers starting offset is not same in
different QPIC NAND controller versions. This patch adds
the starting offset in NAND controller properties and uses
the same for calculating the actual offset of these registers.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4:

1. Changed macro name from nandc_dev_addr to dev_cmd_reg_addr
2. Modified comment for nandc_dev_addr
3. Changed new property variable name from flash_dev_offset to
dev_cmd_reg_start

drivers/mtd/nand/qcom_nandc.c | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 1ff5daf..7977a70 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -193,6 +193,12 @@
((size) << READ_LOCATION_SIZE) | \
((is_last) << READ_LOCATION_LAST))

+/*
+ * Returns the actual register address for all NAND_DEV_ registers
+ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
+ */
+#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
+
#define QPIC_PER_CW_CMD_SGL 32
#define QPIC_PER_CW_DATA_SGL 8

@@ -429,10 +435,12 @@ struct qcom_nand_host {
* among different NAND controllers.
* @ecc_modes - ecc mode for NAND
* @is_bam - whether NAND controller is using BAM
+ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
*/
struct qcom_nandc_props {
u32 ecc_modes;
bool is_bam;
+ u32 dev_cmd_reg_start;
};

/* Frees the BAM transaction memory */
@@ -848,6 +856,9 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
flow_control = true;

+ if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
+ first = dev_cmd_reg_addr(nandc, first);
+
size = num_regs * sizeof(u32);
vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
nandc->reg_read_pos += num_regs;
@@ -886,11 +897,11 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_EXEC_CMD)
flags |= NAND_BAM_NWD;

- if (first == NAND_DEV_CMD1_RESTORE)
- first = NAND_DEV_CMD1;
+ if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
+ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);

- if (first == NAND_DEV_CMD_VLD_RESTORE)
- first = NAND_DEV_CMD_VLD;
+ if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
+ first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);

size = num_regs * sizeof(u32);

@@ -2498,7 +2509,8 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)

/* kill onenand */
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
- nandc_write(nandc, NAND_DEV_CMD_VLD, NAND_DEV_CMD_VLD_VAL);
+ nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
+ NAND_DEV_CMD_VLD_VAL);

/* enable ADM or BAM DMA */
if (nandc->props->is_bam) {
@@ -2509,7 +2521,7 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
}

/* save the original values of these registers */
- nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1);
+ nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
nandc->vld = NAND_DEV_CMD_VLD_VAL;

return 0;
@@ -2758,6 +2770,7 @@ static int qcom_nandc_remove(struct platform_device *pdev)
static const struct qcom_nandc_props ipq806x_nandc_props = {
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
.is_bam = false,
+ .dev_cmd_reg_start = 0x0,
};

/*
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:37

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 04/16] mtd: nand: qcom: support for passing flags in DMA helper functions

The QPIC NAND BAM has multiple flags to control the transfer. This
patch adds flags parameter in register and data transfer DMA helper
functions and modifies all these functions call with appropriate
flags using following rule

1. Read and write can’t go in single command descriptor so
separate SGL should be used.
2. For some of the requests, NWD flag should be set in BAM
DMA descriptor.
3. For Data write, the BAM has internal buffer for each codeword.
All write request will modify the data in internal buffer and
this buffer will be flushed to NAND device once EOT flag is set.
So for all the write requests in single codeword, the EOT should
be cleared for all tx data descriptors except the last one.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4:

1. Added flag argument description in function comment
2. Minor modification in the comment given before
#define NAND_BAM_NO_EOT
3. Minor modification in commit message

drivers/mtd/nand/qcom_nandc.c | 129 +++++++++++++++++++++++++-----------------
1 file changed, 77 insertions(+), 52 deletions(-)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index f52a692..c922617 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -181,6 +181,17 @@
#define QPIC_PER_CW_DATA_SGL 8

/*
+ * Flags used in DMA descriptor preparation helper functions
+ * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
+ */
+/* Don't set the EOT in current tx BAM sgl */
+#define NAND_BAM_NO_EOT BIT(0)
+/* Set the NWD flag in current BAM sgl */
+#define NAND_BAM_NWD BIT(1)
+/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
+#define NAND_BAM_NEXT_SGL BIT(2)
+
+/*
* This data type corresponds to the BAM transaction which will be used for all
* NAND transfers.
* @cmd_sgl - sgl for NAND BAM command pipe
@@ -729,9 +740,10 @@ static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
*
* @first: offset of the first register in the contiguous block
* @num_regs: number of registers to read
+ * @flags: flags to control DMA descriptor preparation
*/
static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs)
+ int num_regs, unsigned int flags)
{
bool flow_control = false;
void *vaddr;
@@ -753,9 +765,10 @@ static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
*
* @first: offset of the first register in the contiguous block
* @num_regs: number of registers to write
+ * @flags: flags to control DMA descriptor preparation
*/
static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs)
+ int num_regs, unsigned int flags)
{
bool flow_control = false;
struct nandc_regs *regs = nandc->regs;
@@ -767,6 +780,9 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
if (first == NAND_FLASH_CMD)
flow_control = true;

+ if (first == NAND_EXEC_CMD)
+ flags |= NAND_BAM_NWD;
+
if (first == NAND_DEV_CMD1_RESTORE)
first = NAND_DEV_CMD1;

@@ -786,9 +802,10 @@ static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
* @reg_off: offset within the controller's data buffer
* @vaddr: virtual address of the buffer we want to write to
* @size: DMA transaction size in bytes
+ * @flags: flags to control DMA descriptor preparation
*/
static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size)
+ const u8 *vaddr, int size, unsigned int flags)
{
return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
}
@@ -800,9 +817,10 @@ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
* @reg_off: offset within the controller's data buffer
* @vaddr: virtual address of the buffer we want to read from
* @size: DMA transaction size in bytes
+ * @flags: flags to control DMA descriptor preparation
*/
static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size)
+ const u8 *vaddr, int size, unsigned int flags)
{
return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
}
@@ -813,9 +831,9 @@ static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
*/
static void config_nand_page_read(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_ADDR0, 2);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3);
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1);
+ write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
}

/*
@@ -824,11 +842,12 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc)
*/
static void config_nand_cw_read(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_FLASH_CMD, 1);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);

- read_reg_dma(nandc, NAND_FLASH_STATUS, 2);
- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
+ read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
}

/*
@@ -847,9 +866,10 @@ static void config_nand_single_cw_page_read(struct qcom_nand_controller *nandc)
*/
static void config_nand_page_write(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_ADDR0, 2);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3);
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1);
+ write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+ write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
+ NAND_BAM_NEXT_SGL);
}

/*
@@ -858,13 +878,13 @@ static void config_nand_page_write(struct qcom_nand_controller *nandc)
*/
static void config_nand_cw_write(struct qcom_nand_controller *nandc)
{
- write_reg_dma(nandc, NAND_FLASH_CMD, 1);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);

- read_reg_dma(nandc, NAND_FLASH_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);

- write_reg_dma(nandc, NAND_FLASH_STATUS, 1);
- write_reg_dma(nandc, NAND_READ_STATUS, 1);
+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
}

/*
@@ -911,8 +931,8 @@ static int nandc_param(struct qcom_nand_host *host)
nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);

- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1);
- write_reg_dma(nandc, NAND_DEV_CMD1, 1);
+ write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
+ write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);

nandc->buf_count = 512;
memset(nandc->data_buffer, 0xff, nandc->buf_count);
@@ -920,11 +940,11 @@ static int nandc_param(struct qcom_nand_host *host)
config_nand_single_cw_page_read(nandc);

read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
- nandc->buf_count);
+ nandc->buf_count, 0);

/* restore CMD1 and VLD regs */
- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1);
- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1);
+ write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
+ write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);

return 0;
}
@@ -946,14 +966,14 @@ static int erase_block(struct qcom_nand_host *host, int page_addr)
nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);

- write_reg_dma(nandc, NAND_FLASH_CMD, 3);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 2);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);

- read_reg_dma(nandc, NAND_FLASH_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);

- write_reg_dma(nandc, NAND_FLASH_STATUS, 1);
- write_reg_dma(nandc, NAND_READ_STATUS, 1);
+ write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);

return 0;
}
@@ -973,10 +993,10 @@ static int read_id(struct qcom_nand_host *host, int column)
nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);

- write_reg_dma(nandc, NAND_FLASH_CMD, 4);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);

- read_reg_dma(nandc, NAND_READ_ID, 1);
+ read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);

return 0;
}
@@ -990,10 +1010,10 @@ static int reset(struct qcom_nand_host *host)
nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE);
nandc_set_reg(nandc, NAND_EXEC_CMD, 1);

- write_reg_dma(nandc, NAND_FLASH_CMD, 1);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1);
+ write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);

- read_reg_dma(nandc, NAND_FLASH_STATUS, 1);
+ read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);

return 0;
}
@@ -1389,7 +1409,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,

if (data_buf)
read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
- data_size);
+ data_size, 0);

/*
* when ecc is enabled, the controller doesn't read the real
@@ -1405,7 +1425,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
*oob_buf++ = 0xff;

read_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size);
+ oob_buf, oob_size, 0);
}

if (data_buf)
@@ -1447,7 +1467,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)

config_nand_single_cw_page_read(nandc);

- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size);
+ read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);

ret = submit_descs(nandc);
if (ret)
@@ -1516,19 +1536,19 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd,

config_nand_cw_read(nandc);

- read_data_dma(nandc, reg_off, data_buf, data_size1);
+ read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
reg_off += data_size1;
data_buf += data_size1;

- read_data_dma(nandc, reg_off, oob_buf, oob_size1);
+ read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
reg_off += oob_size1;
oob_buf += oob_size1;

- read_data_dma(nandc, reg_off, data_buf, data_size2);
+ read_data_dma(nandc, reg_off, data_buf, data_size2, 0);
reg_off += data_size2;
data_buf += data_size2;

- read_data_dma(nandc, reg_off, oob_buf, oob_size2);
+ read_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;
}

@@ -1595,7 +1615,8 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
}


- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size);
+ write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
+ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);

/*
* when ECC is enabled, we don't really need to write anything
@@ -1608,7 +1629,7 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
oob_buf += host->bbm_size;

write_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size);
+ oob_buf, oob_size, 0);
}

config_nand_cw_write(nandc);
@@ -1663,19 +1684,22 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd,
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
}

- write_data_dma(nandc, reg_off, data_buf, data_size1);
+ write_data_dma(nandc, reg_off, data_buf, data_size1,
+ NAND_BAM_NO_EOT);
reg_off += data_size1;
data_buf += data_size1;

- write_data_dma(nandc, reg_off, oob_buf, oob_size1);
+ write_data_dma(nandc, reg_off, oob_buf, oob_size1,
+ NAND_BAM_NO_EOT);
reg_off += oob_size1;
oob_buf += oob_size1;

- write_data_dma(nandc, reg_off, data_buf, data_size2);
+ write_data_dma(nandc, reg_off, data_buf, data_size2,
+ NAND_BAM_NO_EOT);
reg_off += data_size2;
data_buf += data_size2;

- write_data_dma(nandc, reg_off, oob_buf, oob_size2);
+ write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;

config_nand_cw_write(nandc);
@@ -1729,8 +1753,8 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
update_rw_regs(host, 1, false);

config_nand_page_write(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
- data_size + oob_size);
+ write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, data_size + oob_size, 0);
config_nand_cw_write(nandc);

ret = submit_descs(nandc);
@@ -1814,7 +1838,8 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)
update_rw_regs(host, 1, false);

config_nand_page_write(nandc);
- write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size);
+ write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, host->cw_size, 0);
config_nand_cw_write(nandc);

ret = submit_descs(nandc);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-17 12:08:21

by Abhishek Sahu

[permalink] [raw]
Subject: [PATCH v5 02/16] mtd: nand: qcom: allocate BAM transaction

- The BAM transaction is the core data structure which will be used
for all the data transfers in QPIC NAND. Since the core framework
in nand_base.c is serializing all the NAND requests so allocating
BAM transaction before every transfer will be overhead. The memory
for it be allocated during probe time and before every transfer,
it will be cleared.

- The BAM transaction contains the array of
command and data scatter gather list and indexes. For
every transfer, all the resource will be taken from BAM
transaction.

- The size of the buffer used for BAM transactions
is calculated based on the NAND device with the maximum page size,
among all the devices connected to the
controller.

Reviewed-by: Archit Taneja <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---

* Changes from v4: None

drivers/mtd/nand/qcom_nandc.c | 94 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)

diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 590fc1d..4f8306e 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -177,6 +177,32 @@
#define ECC_BCH_4BIT BIT(2)
#define ECC_BCH_8BIT BIT(3)

+#define QPIC_PER_CW_CMD_SGL 32
+#define QPIC_PER_CW_DATA_SGL 8
+
+/*
+ * This data type corresponds to the BAM transaction which will be used for all
+ * NAND transfers.
+ * @cmd_sgl - sgl for NAND BAM command pipe
+ * @data_sgl - sgl for NAND BAM consumer/producer pipe
+ * @cmd_sgl_pos - current index in command sgl.
+ * @cmd_sgl_start - start index in command sgl.
+ * @tx_sgl_pos - current index in data sgl for tx.
+ * @tx_sgl_start - start index in data sgl for tx.
+ * @rx_sgl_pos - current index in data sgl for rx.
+ * @rx_sgl_start - start index in data sgl for rx.
+ */
+struct bam_transaction {
+ struct scatterlist *cmd_sgl;
+ struct scatterlist *data_sgl;
+ u32 cmd_sgl_pos;
+ u32 cmd_sgl_start;
+ u32 tx_sgl_pos;
+ u32 tx_sgl_start;
+ u32 rx_sgl_pos;
+ u32 rx_sgl_start;
+};
+
struct desc_info {
struct list_head node;

@@ -243,6 +269,8 @@ struct nandc_regs {
* @cmd1/vld: some fixed controller register values
* @props: properties of current NAND controller,
* initialized via DT match data
+ * @max_cwperpage: maximum QPIC codewords required. calculated
+ * from all connected NAND devices pagesize
*/
struct qcom_nand_controller {
struct nand_hw_control controller;
@@ -273,11 +301,13 @@ struct qcom_nand_controller {
};

struct list_head desc_list;
+ struct bam_transaction *bam_txn;

u8 *data_buffer;
int buf_size;
int buf_count;
int buf_start;
+ unsigned int max_cwperpage;

__le32 *reg_read_buf;
dma_addr_t reg_read_dma;
@@ -350,6 +380,44 @@ struct qcom_nandc_props {
bool is_bam;
};

+/* Frees the BAM transaction memory */
+static void free_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn = nandc->bam_txn;
+
+ devm_kfree(nandc->dev, bam_txn);
+}
+
+/* Allocates and Initializes the BAM transaction */
+static struct bam_transaction *
+alloc_bam_transaction(struct qcom_nand_controller *nandc)
+{
+ struct bam_transaction *bam_txn;
+ size_t bam_txn_size;
+ unsigned int num_cw = nandc->max_cwperpage;
+ void *bam_txn_buf;
+
+ bam_txn_size =
+ sizeof(*bam_txn) + num_cw *
+ ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
+ (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
+
+ bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
+ if (!bam_txn_buf)
+ return NULL;
+
+ bam_txn = bam_txn_buf;
+ bam_txn_buf += sizeof(*bam_txn);
+
+ bam_txn->cmd_sgl = bam_txn_buf;
+ bam_txn_buf +=
+ sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
+
+ bam_txn->data_sgl = bam_txn_buf;
+
+ return bam_txn;
+}
+
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
{
return container_of(chip, struct qcom_nand_host, chip);
@@ -1920,6 +1988,8 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);

cwperpage = mtd->writesize / ecc->size;
+ nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
+ cwperpage);

/*
* DATA_UD_BYTES varies based on whether the read/write command protects
@@ -2054,6 +2124,20 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
dev_err(nandc->dev, "failed to request cmd channel\n");
return -ENODEV;
}
+
+ /*
+ * Initially allocate BAM transaction to read ONFI param page.
+ * After detecting all the devices, this BAM transaction will
+ * be freed and the next BAM tranasction will be allocated with
+ * maximum codeword size
+ */
+ nandc->max_cwperpage = 1;
+ nandc->bam_txn = alloc_bam_transaction(nandc);
+ if (!nandc->bam_txn) {
+ dev_err(nandc->dev,
+ "failed to allocate bam transaction\n");
+ return -ENOMEM;
+ }
} else {
nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx");
if (!nandc->chan) {
@@ -2211,6 +2295,16 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
if (list_empty(&nandc->host_list))
return -ENODEV;

+ if (nandc->props->is_bam) {
+ free_bam_transaction(nandc);
+ nandc->bam_txn = alloc_bam_transaction(nandc);
+ if (!nandc->bam_txn) {
+ dev_err(nandc->dev,
+ "failed to allocate bam transaction\n");
+ return -ENOMEM;
+ }
+ }
+
list_for_each_entry_safe(host, tmp, &nandc->host_list, node) {
ret = qcom_nand_mtd_register(nandc, host, child);
if (ret) {
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-19 09:32:11

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH v5 10/16] mtd: nand: qcom: add command elements in BAM transaction

On 2017-08-17 17:37, Abhishek Sahu wrote:
> All the QPIC register read/write through BAM DMA requires
> command descriptor which contains the array of command elements.
>
> Reviewed-by: Archit Taneja <[email protected]>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
>
> * Changes from v4: None
>
> * BUILD DEPENDENCY:
>
> This PATCH has build dependency over following BAM command descriptor
> patch posted in DMA engine mailing list
>
> http://www.spinics.net/lists/dmaengine/msg13665.html

Hi Boris,

These patch has build dependency over DMA engine patch
http://www.spinics.net/lists/dmaengine/msg13665.html

but this patch has been applied in your github
https://github.com/bbrezillon/linux-0day/commits/nand/next

The DMA change has not merged yet so could we drop this and
next patch alone till the the DMA change is merged to prevent
build error.

Rest of the patch can go without any build or functionality
failure.

Thanks,
Abhishek

>
> drivers/mtd/nand/qcom_nandc.c | 19 ++++++++++++++++++-
> 1 file changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/nand/qcom_nandc.c
> b/drivers/mtd/nand/qcom_nandc.c
> index 7977a70..b0a4734 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -22,6 +22,7 @@
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/delay.h>
> +#include <linux/dma/qcom_bam_dma.h>
>
> /* NANDc reg offsets */
> #define NAND_FLASH_CMD 0x00
> @@ -199,6 +200,7 @@
> */
> #define dev_cmd_reg_addr(nandc, reg)
> ((nandc)->props->dev_cmd_reg_start + (reg))
>
> +#define QPIC_PER_CW_CMD_ELEMENTS 32
> #define QPIC_PER_CW_CMD_SGL 32
> #define QPIC_PER_CW_DATA_SGL 8
>
> @@ -221,8 +223,13 @@
> /*
> * This data type corresponds to the BAM transaction which will be
> used for all
> * NAND transfers.
> + * @bam_ce - the array of BAM command elements
> * @cmd_sgl - sgl for NAND BAM command pipe
> * @data_sgl - sgl for NAND BAM consumer/producer pipe
> + * @bam_ce_pos - the index in bam_ce which is available for next sgl
> + * @bam_ce_start - the index in bam_ce which marks the start position
> ce
> + * for current sgl. It will be used for size calculation
> + * for current sgl
> * @cmd_sgl_pos - current index in command sgl.
> * @cmd_sgl_start - start index in command sgl.
> * @tx_sgl_pos - current index in data sgl for tx.
> @@ -231,8 +238,11 @@
> * @rx_sgl_start - start index in data sgl for rx.
> */
> struct bam_transaction {
> + struct bam_cmd_element *bam_ce;
> struct scatterlist *cmd_sgl;
> struct scatterlist *data_sgl;
> + u32 bam_ce_pos;
> + u32 bam_ce_start;
> u32 cmd_sgl_pos;
> u32 cmd_sgl_start;
> u32 tx_sgl_pos;
> @@ -462,7 +472,8 @@ static void free_bam_transaction(struct
> qcom_nand_controller *nandc)
>
> bam_txn_size =
> sizeof(*bam_txn) + num_cw *
> - ((sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
> + ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
> + (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
> (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
>
> bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
> @@ -472,6 +483,10 @@ static void free_bam_transaction(struct
> qcom_nand_controller *nandc)
> bam_txn = bam_txn_buf;
> bam_txn_buf += sizeof(*bam_txn);
>
> + bam_txn->bam_ce = bam_txn_buf;
> + bam_txn_buf +=
> + sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
> +
> bam_txn->cmd_sgl = bam_txn_buf;
> bam_txn_buf +=
> sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
> @@ -489,6 +504,8 @@ static void clear_bam_transaction(struct
> qcom_nand_controller *nandc)
> if (!nandc->props->is_bam)
> return;
>
> + bam_txn->bam_ce_pos = 0;
> + bam_txn->bam_ce_start = 0;
> bam_txn->cmd_sgl_pos = 0;
> bam_txn->cmd_sgl_start = 0;
> bam_txn->tx_sgl_pos = 0;

2017-08-19 09:47:22

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH v5 11/16] mtd: nand: qcom: support for command descriptor formation

On 2017-08-17 17:37, Abhishek Sahu wrote:
> 1. Add the function for command descriptor preparation which will
> be used only by BAM DMA and it will form the DMA descriptors
> containing command elements
> 2. DMA_PREP_CMD flag should be used for forming command DMA
> descriptors
>
> Reviewed-by: Archit Taneja <[email protected]>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
>
> * Changes from v4: None
>
> * BUILD DEPENDENCY:
>
> This PATCH has build dependency over following BAM command descriptor
> patch posted in DMA engine mailing list
>
> http://www.spinics.net/lists/dmaengine/msg13665.html
>

Hi Boris,

These patch has build dependency over DMA engine patch
http://www.spinics.net/lists/dmaengine/msg13665.html

but this patch has been applied in your github
https://github.com/bbrezillon/linux-0day/commits/nand/next

The DMA change has not merged yet so could we drop this and
previous patch alone till the the DMA change is merged to prevent
build error.

Rest of the patch can go without any build or functionality
failure.

> drivers/mtd/nand/qcom_nandc.c | 108
> +++++++++++++++++++++++++++++++++++-------
> 1 file changed, 92 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/mtd/nand/qcom_nandc.c
> b/drivers/mtd/nand/qcom_nandc.c
> index b0a4734..52d9fae 100644
> --- a/drivers/mtd/nand/qcom_nandc.c
> +++ b/drivers/mtd/nand/qcom_nandc.c
> @@ -200,6 +200,14 @@
> */
> #define dev_cmd_reg_addr(nandc, reg)
> ((nandc)->props->dev_cmd_reg_start + (reg))
>
> +/* Returns the NAND register physical address */
> +#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
> +
> +/* Returns the dma address for reg read buffer */
> +#define reg_buf_dma_addr(chip, vaddr) \
> + ((chip)->reg_read_dma + \
> + ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf))
> +
> #define QPIC_PER_CW_CMD_ELEMENTS 32
> #define QPIC_PER_CW_CMD_SGL 32
> #define QPIC_PER_CW_DATA_SGL 8
> @@ -317,7 +325,8 @@ struct nandc_regs {
> * controller
> * @dev: parent device
> * @base: MMIO base
> - * @base_dma: physical base address of controller registers
> + * @base_phys: physical base address of controller registers
> + * @base_dma: dma base address of controller registers
> * @core_clk: controller clock
> * @aon_clk: another controller clock
> *
> @@ -350,6 +359,7 @@ struct qcom_nand_controller {
> struct device *dev;
>
> void __iomem *base;
> + phys_addr_t base_phys;
> dma_addr_t base_dma;
>
> struct clk *core_clk;
> @@ -751,6 +761,66 @@ static int prepare_bam_async_desc(struct
> qcom_nand_controller *nandc,
> }
>
> /*
> + * Prepares the command descriptor for BAM DMA which will be used for
> NAND
> + * register reads and writes. The command descriptor requires the
> command
> + * to be formed in command element type so this function uses the
> command
> + * element from bam transaction ce array and fills the same with
> required
> + * data. A single SGL can contain multiple command elements so
> + * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
> + * after the current command element.
> + */
> +static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc,
> bool read,
> + int reg_off, const void *vaddr,
> + int size, unsigned int flags)
> +{
> + int bam_ce_size;
> + int i, ret;
> + struct bam_cmd_element *bam_ce_buffer;
> + struct bam_transaction *bam_txn = nandc->bam_txn;
> +
> + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
> +
> + /* fill the command desc */
> + for (i = 0; i < size; i++) {
> + if (read)
> + bam_prep_ce(&bam_ce_buffer[i],
> + nandc_reg_phys(nandc, reg_off + 4 * i),
> + BAM_READ_COMMAND,
> + reg_buf_dma_addr(nandc,
> + (__le32 *)vaddr + i));
> + else
> + bam_prep_ce_le32(&bam_ce_buffer[i],
> + nandc_reg_phys(nandc, reg_off + 4 * i),
> + BAM_WRITE_COMMAND,
> + *((__le32 *)vaddr + i));
> + }
> +
> + bam_txn->bam_ce_pos += size;
> +
> + /* use the separate sgl after this command */
> + if (flags & NAND_BAM_NEXT_SGL) {
> + bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
> + bam_ce_size = (bam_txn->bam_ce_pos -
> + bam_txn->bam_ce_start) *
> + sizeof(struct bam_cmd_element);
> + sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
> + bam_ce_buffer, bam_ce_size);
> + bam_txn->cmd_sgl_pos++;
> + bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
> +
> + if (flags & NAND_BAM_NWD) {
> + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
> + DMA_PREP_FENCE |
> + DMA_PREP_CMD);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/*
> * Prepares the data descriptor for BAM DMA which will be used for
> NAND
> * data reads and writes.
> */
> @@ -868,19 +938,22 @@ static int read_reg_dma(struct
> qcom_nand_controller *nandc, int first,
> {
> bool flow_control = false;
> void *vaddr;
> - int size;
>
> - if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
> - flow_control = true;
> + vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
> + nandc->reg_read_pos += num_regs;
>
> if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
> first = dev_cmd_reg_addr(nandc, first);
>
> - size = num_regs * sizeof(u32);
> - vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
> - nandc->reg_read_pos += num_regs;
> + if (nandc->props->is_bam)
> + return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
> + num_regs, flags);
>
> - return prep_adm_dma_desc(nandc, true, first, vaddr, size,
> flow_control);
> + if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
> + flow_control = true;
> +
> + return prep_adm_dma_desc(nandc, true, first, vaddr,
> + num_regs * sizeof(u32), flow_control);
> }
>
> /*
> @@ -897,13 +970,9 @@ static int write_reg_dma(struct
> qcom_nand_controller *nandc, int first,
> bool flow_control = false;
> struct nandc_regs *regs = nandc->regs;
> void *vaddr;
> - int size;
>
> vaddr = offset_to_nandc_reg(regs, first);
>
> - if (first == NAND_FLASH_CMD)
> - flow_control = true;
> -
> if (first == NAND_ERASED_CW_DETECT_CFG) {
> if (flags & NAND_ERASED_CW_SET)
> vaddr = &regs->erased_cw_detect_cfg_set;
> @@ -920,10 +989,15 @@ static int write_reg_dma(struct
> qcom_nand_controller *nandc, int first,
> if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
> first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
>
> - size = num_regs * sizeof(u32);
> + if (nandc->props->is_bam)
> + return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
> + num_regs, flags);
> +
> + if (first == NAND_FLASH_CMD)
> + flow_control = true;
>
> - return prep_adm_dma_desc(nandc, false, first, vaddr, size,
> - flow_control);
> + return prep_adm_dma_desc(nandc, false, first, vaddr,
> + num_regs * sizeof(u32), flow_control);
> }
>
> /*
> @@ -1187,7 +1261,8 @@ static int submit_descs(struct
> qcom_nand_controller *nandc)
> }
>
> if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
> - r = prepare_bam_async_desc(nandc, nandc->cmd_chan, 0);
> + r = prepare_bam_async_desc(nandc, nandc->cmd_chan,
> + DMA_PREP_CMD);
> if (r)
> return r;
> }
> @@ -2722,6 +2797,7 @@ static int qcom_nandc_probe(struct
> platform_device *pdev)
> if (IS_ERR(nandc->base))
> return PTR_ERR(nandc->base);
>
> + nandc->base_phys = res->start;
> nandc->base_dma = phys_to_dma(dev, (phys_addr_t)res->start);
>
> nandc->core_clk = devm_clk_get(dev, "core");

2017-08-19 20:38:50

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v5 11/16] mtd: nand: qcom: support for command descriptor formation

Le Sat, 19 Aug 2017 15:17:13 +0530,
Abhishek Sahu <[email protected]> a écrit :

> On 2017-08-17 17:37, Abhishek Sahu wrote:
> > 1. Add the function for command descriptor preparation which will
> > be used only by BAM DMA and it will form the DMA descriptors
> > containing command elements
> > 2. DMA_PREP_CMD flag should be used for forming command DMA
> > descriptors
> >
> > Reviewed-by: Archit Taneja <[email protected]>
> > Signed-off-by: Abhishek Sahu <[email protected]>
> > ---
> >
> > * Changes from v4: None
> >
> > * BUILD DEPENDENCY:
> >
> > This PATCH has build dependency over following BAM command descriptor
> > patch posted in DMA engine mailing list
> >
> > http://www.spinics.net/lists/dmaengine/msg13665.html
> >
>
> Hi Boris,
>
> These patch has build dependency over DMA engine patch
> http://www.spinics.net/lists/dmaengine/msg13665.html
>
> but this patch has been applied in your github
> https://github.com/bbrezillon/linux-0day/commits/nand/next
>
> The DMA change has not merged yet so could we drop this and
> previous patch alone till the the DMA change is merged to prevent
> build error.

Oops. I saw this note when reviewing previous version of this and
completely forgot when applying v5. I dropped it from my nand/next
branch.
;
Note that the linux-0day repo is just a personal repo I use to let
Fenguang's robots test things before I push then to l2-mtd/nand/next.

Anyway, thanks for the heads up.

Boris

2017-08-21 20:15:18

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v5 00/16] Add QCOM QPIC NAND support

Le Thu, 17 Aug 2017 17:37:38 +0530,
Abhishek Sahu <[email protected]> a écrit :

> * v5:
>
> 1. Removed the patches already applied to linux-next and rebased the
> remaining patches on [3]
> 2. Addressed the review comments in v4 and Added Archit Reviewed
> by tag.
>
> [3] http://git.infradead.org/l2-mtd.git/shortlog/refs/heads/nand/next
>
> * v4:
>
> 1. Added Acked-by from Rob for DT documentation patches
> 2. Removed ipq8074 compatible string from ipq4019 DT example.
> 2. Used the BIT macro for NAND_CMD_VLD bits and consistent names
> as suggested by Boris in v3.
>
> * v3:
>
> 1. Removed the patches already applied to linux-next and
> rebased the remaining patches on [1]
> 2. Reordered the patches and put the BAM DMA changes [2]
> dependent patches and compatible string patches in last
> 3. Removed the register offsets array and used the dev_cmd offsets
> 4. Changed some macro names to small letters for better code readability
> 5. Changed the compatible string to SoC specific
> 6. Did minor code changes for adding comment, error handling, structure names
> 7. Combined raw write (patch#18) and passing flag parameter (patch#22) patch
> into one
> 8. Made separate patch for compatible string and NAND properties
> 9. Made separate patch for BAM command descriptor and data descriptors handling
> 10. Changed commit message for some of the patches
> 11. Addressed review comments given in v2
> 12. Added Reviewed-by of Archit for some of the patches from v2
> 13. All the MTD tests are working fine for IPQ8064 AP148, IPQ4019 DK04 and
> IPQ8074 HK01 boards for v3 patches
>
> [1] http://git.infradead.org/l2-mtd.git/shortlog/refs/heads/nand/next
> [2] http://www.spinics.net/lists/dmaengine/msg13662.html
>
> * v2:
>
> 1. Addressed the review comments given in v1
> 2. Removed the DMA coherent buffer for register read and used
> streaming DMA API’s
> 3. Reorganized the NAND read and write functions
> 4. Separated patch for driver and documentation changes
> 5. Changed the compatible string for EBI2
>
> * v1:
>
> http://www.spinics.net/lists/devicetree/msg183706.html
>
>
> Abhishek Sahu (16):
> mtd: nand: qcom: DMA mapping support for register read buffer
> mtd: nand: qcom: allocate BAM transaction
> mtd: nand: qcom: add BAM DMA descriptor handling
> mtd: nand: qcom: support for passing flags in DMA helper functions
> mtd: nand: qcom: support for read location registers
> mtd: nand: qcom: erased codeword detection configuration
> mtd: nand: qcom: enable BAM or ADM mode
> mtd: nand: qcom: QPIC data descriptors handling
> mtd: nand: qcom: support for different DEV_CMD register offsets
> mtd: nand: qcom: add command elements in BAM transaction
> mtd: nand: qcom: support for command descriptor formation
> dt-bindings: qcom_nandc: fix the ipq806x device tree example
> dt-bindings: qcom_nandc: IPQ4019 QPIC NAND documentation
> dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation
> mtd: nand: qcom: support for IPQ4019 QPIC NAND controller
> mtd: nand: qcom: Support for IPQ8074 QPIC NAND controller

Applied everything to nand/next except patch 10 and 11. Let me know
when the dmaengine dependency is merged and I'll take the remaining
patches.

Thanks,

Boris

>
> .../devicetree/bindings/mtd/qcom_nandc.txt | 63 +-
> drivers/mtd/nand/qcom_nandc.c | 746 ++++++++++++++++++---
> 2 files changed, 722 insertions(+), 87 deletions(-)
>

2017-08-22 02:21:51

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v5 14/16] dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation

On Thu, Aug 17, 2017 at 05:37:52PM +0530, Abhishek Sahu wrote:
> Qualcom IPQ8074 SoC uses QPIC NAND controller version 1.5.0
> which uses BAM DMA Engine.
>
> Signed-off-by: Abhishek Sahu <[email protected]>
> ---
>
> * Changes from v4: None
>
> Documentation/devicetree/bindings/mtd/qcom_nandc.txt | 2 ++
> 1 file changed, 2 insertions(+)

Acked-by: Rob Herring <[email protected]>

2017-08-22 06:32:38

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH v5 00/16] Add QCOM QPIC NAND support

On 2017-08-22 01:45, Boris Brezillon wrote:
> Le Thu, 17 Aug 2017 17:37:38 +0530,
> Abhishek Sahu <[email protected]> a écrit :
>
>> * v5:
>>
>> 1. Removed the patches already applied to linux-next and rebased the
>> remaining patches on [3]
>> 2. Addressed the review comments in v4 and Added Archit Reviewed
>> by tag.
>>
>> [3] http://git.infradead.org/l2-mtd.git/shortlog/refs/heads/nand/next
>>

<snip>

>>
>> Abhishek Sahu (16):
>> mtd: nand: qcom: DMA mapping support for register read buffer
>> mtd: nand: qcom: allocate BAM transaction
>> mtd: nand: qcom: add BAM DMA descriptor handling
>> mtd: nand: qcom: support for passing flags in DMA helper functions
>> mtd: nand: qcom: support for read location registers
>> mtd: nand: qcom: erased codeword detection configuration
>> mtd: nand: qcom: enable BAM or ADM mode
>> mtd: nand: qcom: QPIC data descriptors handling
>> mtd: nand: qcom: support for different DEV_CMD register offsets
>> mtd: nand: qcom: add command elements in BAM transaction
>> mtd: nand: qcom: support for command descriptor formation
>> dt-bindings: qcom_nandc: fix the ipq806x device tree example
>> dt-bindings: qcom_nandc: IPQ4019 QPIC NAND documentation
>> dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation
>> mtd: nand: qcom: support for IPQ4019 QPIC NAND controller
>> mtd: nand: qcom: Support for IPQ8074 QPIC NAND controller
>
> Applied everything to nand/next except patch 10 and 11. Let me know
> when the dmaengine dependency is merged and I'll take the remaining
> patches.
>

Thanks Boris for your great help and reviewing this long patch series
:-)

I will update you when DMA dependency is merged.

Thanks,
Abhishek

>
>>
>> .../devicetree/bindings/mtd/qcom_nandc.txt | 63 +-
>> drivers/mtd/nand/qcom_nandc.c | 746
>> ++++++++++++++++++---
>> 2 files changed, 722 insertions(+), 87 deletions(-)
>>

2017-09-25 08:09:42

by Abhishek Sahu

[permalink] [raw]
Subject: Re: [PATCH v5 00/16] Add QCOM QPIC NAND support

On 2017-08-22 12:02, Abhishek Sahu wrote:
> On 2017-08-22 01:45, Boris Brezillon wrote:
>> Le Thu, 17 Aug 2017 17:37:38 +0530,
>> Abhishek Sahu <[email protected]> a écrit :
>>
>>> * v5:
>>>
>>> 1. Removed the patches already applied to linux-next and rebased the
>>> remaining patches on [3]
>>> 2. Addressed the review comments in v4 and Added Archit Reviewed
>>> by tag.
>>>
>>> [3] http://git.infradead.org/l2-mtd.git/shortlog/refs/heads/nand/next
>>>
>
> <snip>
>
>>>
>>> Abhishek Sahu (16):
>>> mtd: nand: qcom: DMA mapping support for register read buffer
>>> mtd: nand: qcom: allocate BAM transaction
>>> mtd: nand: qcom: add BAM DMA descriptor handling
>>> mtd: nand: qcom: support for passing flags in DMA helper functions
>>> mtd: nand: qcom: support for read location registers
>>> mtd: nand: qcom: erased codeword detection configuration
>>> mtd: nand: qcom: enable BAM or ADM mode
>>> mtd: nand: qcom: QPIC data descriptors handling
>>> mtd: nand: qcom: support for different DEV_CMD register offsets
>>> mtd: nand: qcom: add command elements in BAM transaction
>>> mtd: nand: qcom: support for command descriptor formation
>>> dt-bindings: qcom_nandc: fix the ipq806x device tree example
>>> dt-bindings: qcom_nandc: IPQ4019 QPIC NAND documentation
>>> dt-bindings: qcom_nandc: IPQ8074 QPIC NAND documentation
>>> mtd: nand: qcom: support for IPQ4019 QPIC NAND controller
>>> mtd: nand: qcom: Support for IPQ8074 QPIC NAND controller
>>
>> Applied everything to nand/next except patch 10 and 11. Let me know
>> when the dmaengine dependency is merged and I'll take the remaining
>> patches.
>>

Hi Boris,

The dmaengine dependencies are merged in 4.14-rc1 so you can take
remaining 2
patches. I have resent those 2 patches again and it can be cleanly
applied over 4.14-rc1.

Thanks,
Abhishek

>
>>
>>>
>>> .../devicetree/bindings/mtd/qcom_nandc.txt | 63 +-
>>> drivers/mtd/nand/qcom_nandc.c | 746
>>> ++++++++++++++++++---
>>> 2 files changed, 722 insertions(+), 87 deletions(-)
>>>